*
* Optimization module for tcpdump intermediate representation.
*/
-#ifndef lint
-static const char rcsid[] _U_ =
- "@(#) $Header: /tcpdump/master/libpcap/optimize.c,v 1.91 2008-01-02 04:16:46 guy Exp $ (LBL)";
-#endif
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
-#ifdef WIN32
+#ifdef _WIN32
#include <pcap-stdinc.h>
-#else /* WIN32 */
+#else /* _WIN32 */
#if HAVE_INTTYPES_H
#include <inttypes.h>
#elif HAVE_STDINT_H
#include <sys/bitypes.h>
#endif
#include <sys/types.h>
-#endif /* WIN32 */
+#endif /* _WIN32 */
#include <stdio.h>
#include <stdlib.h>
#endif
#ifdef BDEBUG
-extern int dflag;
+int pcap_optimizer_debug;
#endif
#if defined(MSDOS) && !defined(__DJGPP__)
#define ffs _w32_ffs
#endif
-#if defined(WIN32) && defined (_MSC_VER)
-int ffs(int mask);
+/*
+ * So is the check for _MSC_VER done because MinGW has this?
+ */
+#if defined(_WIN32) && defined (_MSC_VER)
+/*
+ * ffs -- vax ffs instruction
+ *
+ * XXX - with versions of VS that have it, use _BitScanForward()?
+ */
+static int
+ffs(int mask)
+{
+ int bit;
+
+ if (mask == 0)
+ return(0);
+ for (bit = 1; !(mask & 1); bit++)
+ mask >>= 1;
+ return(bit);
+}
#endif
/*
static void opt_init(struct block *);
static void opt_cleanup(void);
-static void make_marks(struct block *);
-static void mark_code(struct block *);
-
static void intern_blocks(struct block *);
-static int eq_slist(struct slist *, struct slist *);
-
-static void find_levels_r(struct block *);
-
-static void find_levels(struct block *);
-static void find_dom(struct block *);
-static void propedom(struct edge *);
-static void find_edom(struct block *);
-static void find_closure(struct block *);
-static int atomuse(struct stmt *);
-static int atomdef(struct stmt *);
-static void compute_local_ud(struct block *);
-static void find_ud(struct block *);
-static void init_val(void);
-static int F(int, int, int);
-static inline void vstore(struct stmt *, int *, int, int);
-static void opt_blk(struct block *, int);
-static int use_conflict(struct block *, struct block *);
-static void opt_j(struct edge *);
-static void or_pullup(struct block *);
-static void and_pullup(struct block *);
-static void opt_blks(struct block *, int);
-static inline void link_inedge(struct edge *, struct block *);
static void find_inedges(struct block *);
-static void opt_root(struct block **);
-static void opt_loop(struct block *, int);
-static void fold_op(struct stmt *, int, int);
-static inline struct slist *this_op(struct slist *);
-static void opt_not(struct block *);
-static void opt_peep(struct block *);
-static void opt_stmt(struct stmt *, int[], int);
-static void deadstmt(struct stmt *, struct stmt *[]);
-static void opt_deadstores(struct block *);
-static struct block *fold_edge(struct block *, struct edge *);
-static inline int eq_blk(struct block *, struct block *);
-static int slength(struct slist *);
-static int count_blocks(struct block *);
-static void number_blks_r(struct block *);
-static int count_stmts(struct block *);
-static int convert_code_r(struct block *);
#ifdef BDEBUG
static void opt_dump(struct block *);
#endif
#endif
static void
-find_levels_r(b)
- struct block *b;
+find_levels_r(struct block *b)
{
int level;
* with the 'link' field of the struct block.
*/
static void
-find_levels(root)
- struct block *root;
+find_levels(struct block *root)
{
memset((char *)levels, 0, n_blocks * sizeof(*levels));
unMarkAll();
* Assumes graph has been leveled.
*/
static void
-find_dom(root)
- struct block *root;
+find_dom(struct block *root)
{
int i;
struct block *b;
}
static void
-propedom(ep)
- struct edge *ep;
+propedom(struct edge *ep)
{
SET_INSERT(ep->edom, ep->id);
if (ep->succ) {
* Assumes graph has been leveled and predecessors established.
*/
static void
-find_edom(root)
- struct block *root;
+find_edom(struct block *root)
{
int i;
uset x;
* Assumes graph has been leveled.
*/
static void
-find_closure(root)
- struct block *root;
+find_closure(struct block *root)
{
int i;
struct block *b;
* The implementation should probably change to an array access.
*/
static int
-atomuse(s)
- struct stmt *s;
+atomuse(struct stmt *s)
{
register int c = s->code;
* The implementation should probably change to an array access.
*/
static int
-atomdef(s)
- struct stmt *s;
+atomdef(struct stmt *s)
{
if (s->code == NOP)
return -1;
* register by a predecessor block of this block.
*/
static void
-compute_local_ud(b)
- struct block *b;
+compute_local_ud(struct block *b)
{
struct slist *s;
atomset def = 0, use = 0, kill = 0;
* Assume graph is already leveled.
*/
static void
-find_ud(root)
- struct block *root;
+find_ud(struct block *root)
{
int i, maxlevel;
struct block *p;
struct valnode *next_vnode;
static void
-init_val()
+init_val(void)
{
curval = 0;
next_vnode = vnode_base;
/* Because we really don't have an IR, this stuff is a little messy. */
static int
-F(code, v0, v1)
- int code;
- int v0, v1;
+F(int code, int v0, int v1)
{
u_int hash;
int val;
}
static inline void
-vstore(s, valp, newval, alter)
- struct stmt *s;
- int *valp;
- int newval;
- int alter;
+vstore(struct stmt *s, int *valp, int newval, int alter)
{
if (alter && *valp == newval)
s->code = NOP;
*valp = newval;
}
+/*
+ * Do constant-folding on binary operators.
+ * (Unary operators are handled elsewhere.)
+ */
static void
-fold_op(s, v0, v1)
- struct stmt *s;
- int v0, v1;
+fold_op(struct stmt *s, int v0, int v1)
{
bpf_u_int32 a, b;
a /= b;
break;
+ case BPF_MOD:
+ if (b == 0)
+ bpf_error("modulus by zero");
+ a %= b;
+ break;
+
case BPF_AND:
a &= b;
break;
a |= b;
break;
+ case BPF_XOR:
+ a ^= b;
+ break;
+
case BPF_LSH:
a <<= b;
break;
a >>= b;
break;
- case BPF_NEG:
- a = -a;
- break;
-
default:
abort();
}
}
static inline struct slist *
-this_op(s)
- struct slist *s;
+this_op(struct slist *s)
{
while (s != 0 && s->s.code == NOP)
s = s->next;
}
static void
-opt_not(b)
- struct block *b;
+opt_not(struct block *b)
{
struct block *tmp = JT(b);
}
static void
-opt_peep(b)
- struct block *b;
+opt_peep(struct block *b)
{
struct slist *s;
struct slist *next, *last;
* evaluation and code transformations weren't folded together.
*/
static void
-opt_stmt(s, val, alter)
- struct stmt *s;
- int val[];
- int alter;
+opt_stmt(struct stmt *s, int val[], int alter)
{
int op;
int v;
case BPF_ALU|BPF_SUB|BPF_K:
case BPF_ALU|BPF_MUL|BPF_K:
case BPF_ALU|BPF_DIV|BPF_K:
+ case BPF_ALU|BPF_MOD|BPF_K:
case BPF_ALU|BPF_AND|BPF_K:
case BPF_ALU|BPF_OR|BPF_K:
+ case BPF_ALU|BPF_XOR|BPF_K:
case BPF_ALU|BPF_LSH|BPF_K:
case BPF_ALU|BPF_RSH|BPF_K:
op = BPF_OP(s->code);
* fixup the generated math code */
if (op == BPF_ADD ||
op == BPF_LSH || op == BPF_RSH ||
- op == BPF_OR) {
+ op == BPF_OR || op == BPF_XOR) {
s->code = NOP;
break;
}
case BPF_ALU|BPF_SUB|BPF_X:
case BPF_ALU|BPF_MUL|BPF_X:
case BPF_ALU|BPF_DIV|BPF_X:
+ case BPF_ALU|BPF_MOD|BPF_X:
case BPF_ALU|BPF_AND|BPF_X:
case BPF_ALU|BPF_OR|BPF_X:
+ case BPF_ALU|BPF_XOR|BPF_X:
case BPF_ALU|BPF_LSH|BPF_X:
case BPF_ALU|BPF_RSH|BPF_X:
op = BPF_OP(s->code);
*/
if (alter && vmap[val[A_ATOM]].is_const
&& vmap[val[A_ATOM]].const_val == 0) {
- if (op == BPF_ADD || op == BPF_OR) {
+ if (op == BPF_ADD || op == BPF_OR || op == BPF_XOR) {
s->code = BPF_MISC|BPF_TXA;
vstore(s, &val[A_ATOM], val[X_ATOM], alter);
break;
}
- else if (op == BPF_MUL || op == BPF_DIV ||
+ else if (op == BPF_MUL || op == BPF_DIV || op == BPF_MOD ||
op == BPF_AND || op == BPF_LSH || op == BPF_RSH) {
s->code = BPF_LD|BPF_IMM;
s->k = 0;
}
static void
-deadstmt(s, last)
- register struct stmt *s;
- register struct stmt *last[];
+deadstmt(register struct stmt *s, register struct stmt *last[])
{
register int atom;
}
static void
-opt_deadstores(b)
- register struct block *b;
+opt_deadstores(register struct block *b)
{
register struct slist *s;
register int atom;
}
static void
-opt_blk(b, do_stmts)
- struct block *b;
- int do_stmts;
+opt_blk(struct block *b, int do_stmts)
{
struct slist *s;
struct edge *p;
* from 'b'.
*/
static int
-use_conflict(b, succ)
- struct block *b, *succ;
+use_conflict(struct block *b, struct block *succ)
{
int atom;
atomset use = succ->out_use;
}
static struct block *
-fold_edge(child, ep)
- struct block *child;
- struct edge *ep;
+fold_edge(struct block *child, struct edge *ep)
{
int sense;
int aval0, aval1, oval0, oval1;
}
static void
-opt_j(ep)
- struct edge *ep;
+opt_j(struct edge *ep)
{
register int i, k;
register struct block *target;
static void
-or_pullup(b)
- struct block *b;
+or_pullup(struct block *b)
{
int val, at_top;
struct block *pull;
}
static void
-and_pullup(b)
- struct block *b;
+and_pullup(struct block *b)
{
int val, at_top;
struct block *pull;
}
static void
-opt_blks(root, do_stmts)
- struct block *root;
- int do_stmts;
+opt_blks(struct block *root, int do_stmts)
{
int i, maxlevel;
struct block *p;
}
static inline void
-link_inedge(parent, child)
- struct edge *parent;
- struct block *child;
+link_inedge(struct edge *parent, struct block *child)
{
parent->next = child->in_edges;
child->in_edges = parent;
}
static void
-find_inedges(root)
- struct block *root;
+find_inedges(struct block *root)
{
int i;
struct block *b;
}
static void
-opt_root(b)
- struct block **b;
+opt_root(struct block **b)
{
struct slist *tmp, *s;
}
static void
-opt_loop(root, do_stmts)
- struct block *root;
- int do_stmts;
+opt_loop(struct block *root, int do_stmts)
{
#ifdef BDEBUG
- if (dflag > 1) {
+ if (pcap_optimizer_debug > 1) {
printf("opt_loop(root, %d) begin\n", do_stmts);
opt_dump(root);
}
find_edom(root);
opt_blks(root, do_stmts);
#ifdef BDEBUG
- if (dflag > 1) {
+ if (pcap_optimizer_debug > 1) {
printf("opt_loop(root, %d) bottom, done=%d\n", do_stmts, done);
opt_dump(root);
}
* Optimize the filter code in its dag representation.
*/
void
-bpf_optimize(rootp)
- struct block **rootp;
+bpf_optimize(struct block **rootp)
{
struct block *root;
opt_loop(root, 1);
intern_blocks(root);
#ifdef BDEBUG
- if (dflag > 1) {
+ if (pcap_optimizer_debug > 1) {
printf("after intern_blocks()\n");
opt_dump(root);
}
#endif
opt_root(rootp);
#ifdef BDEBUG
- if (dflag > 1) {
+ if (pcap_optimizer_debug > 1) {
printf("after opt_root()\n");
opt_dump(root);
}
}
static void
-make_marks(p)
- struct block *p;
+make_marks(struct block *p)
{
if (!isMarked(p)) {
Mark(p);
* only for nodes that are alive.
*/
static void
-mark_code(p)
- struct block *p;
+mark_code(struct block *p)
{
cur_mark += 1;
make_marks(p);
* the accumulator.
*/
static int
-eq_slist(x, y)
- struct slist *x, *y;
+eq_slist(struct slist *x, struct slist *y)
{
while (1) {
while (x && x->s.code == NOP)
}
static inline int
-eq_blk(b0, b1)
- struct block *b0, *b1;
+eq_blk(struct block *b0, struct block *b1)
{
if (b0->s.code == b1->s.code &&
b0->s.k == b1->s.k &&
}
static void
-intern_blocks(root)
- struct block *root;
+intern_blocks(struct block *root)
{
struct block *p;
int i, j;
}
static void
-opt_cleanup()
+opt_cleanup(void)
{
free((void *)vnode_base);
free((void *)vmap);
/*
* Return the number of stmts in 's'.
*/
-static int
-slength(s)
- struct slist *s;
+static u_int
+slength(struct slist *s)
{
- int n = 0;
+ u_int n = 0;
for (; s; s = s->next)
if (s->s.code != NOP)
* All nodes should be initially unmarked.
*/
static int
-count_blocks(p)
- struct block *p;
+count_blocks(struct block *p)
{
if (p == 0 || isMarked(p))
return 0;
* the basic blocks, and entering them into the 'blocks' array.`
*/
static void
-number_blks_r(p)
- struct block *p;
+number_blks_r(struct block *p)
{
int n;
*
* an extra long jump if the false branch requires it (p->longjf).
*/
-static int
-count_stmts(p)
- struct block *p;
+static u_int
+count_stmts(struct block *p)
{
- int n;
+ u_int n;
if (p == 0 || isMarked(p))
return 0;
* from the total number of blocks and/or statements.
*/
static void
-opt_init(root)
- struct block *root;
+opt_init(struct block *root)
{
bpf_u_int32 *p;
int i, n, max_stmts;
* properly.
*/
static int
-convert_code_r(p)
- struct block *p;
+convert_code_r(struct block *p)
{
struct bpf_insn *dst;
struct slist *src;
* done with the filter program. See the pcap man page.
*/
struct bpf_insn *
-icode_to_fcode(root, lenp)
- struct block *root;
- int *lenp;
+icode_to_fcode(struct block *root, u_int *lenp)
{
- int n;
+ u_int n;
struct bpf_insn *fp;
/*
* Validate the program.
*/
if (!bpf_validate(fp->bf_insns, fp->bf_len)) {
- snprintf(p->errbuf, sizeof(p->errbuf),
+ pcap_snprintf(p->errbuf, sizeof(p->errbuf),
"BPF program is not valid");
return (-1);
}
p->fcode.bf_len = fp->bf_len;
p->fcode.bf_insns = (struct bpf_insn *)malloc(prog_size);
if (p->fcode.bf_insns == NULL) {
- snprintf(p->errbuf, sizeof(p->errbuf),
+ pcap_snprintf(p->errbuf, sizeof(p->errbuf),
"malloc: %s", pcap_strerror(errno));
return (-1);
}
#ifdef BDEBUG
static void
-opt_dump(root)
- struct block *root;
+dot_dump_node(struct block *block, struct bpf_program *prog, FILE *out)
+{
+ int icount, noffset;
+ int i;
+
+ if (block == NULL || isMarked(block))
+ return;
+ Mark(block);
+
+ icount = slength(block->stmts) + 1 + block->longjt + block->longjf;
+ noffset = min(block->offset + icount, (int)prog->bf_len);
+
+ fprintf(out, "\tblock%d [shape=ellipse, id=\"block-%d\" label=\"BLOCK%d\\n", block->id, block->id, block->id);
+ for (i = block->offset; i < noffset; i++) {
+ fprintf(out, "\\n%s", bpf_image(prog->bf_insns + i, i));
+ }
+ fprintf(out, "\" tooltip=\"");
+ for (i = 0; i < BPF_MEMWORDS; i++)
+ if (block->val[i] != 0)
+ fprintf(out, "val[%d]=%d ", i, block->val[i]);
+ fprintf(out, "val[A]=%d ", block->val[A_ATOM]);
+ fprintf(out, "val[X]=%d", block->val[X_ATOM]);
+ fprintf(out, "\"");
+ if (JT(block) == NULL)
+ fprintf(out, ", peripheries=2");
+ fprintf(out, "];\n");
+
+ dot_dump_node(JT(block), prog, out);
+ dot_dump_node(JF(block), prog, out);
+}
+
+static void
+dot_dump_edge(struct block *block, FILE *out)
+{
+ if (block == NULL || isMarked(block))
+ return;
+ Mark(block);
+
+ if (JT(block)) {
+ fprintf(out, "\t\"block%d\":se -> \"block%d\":n [label=\"T\"]; \n",
+ block->id, JT(block)->id);
+ fprintf(out, "\t\"block%d\":sw -> \"block%d\":n [label=\"F\"]; \n",
+ block->id, JF(block)->id);
+ }
+ dot_dump_edge(JT(block), out);
+ dot_dump_edge(JF(block), out);
+}
+
+/* Output the block CFG using graphviz/DOT language
+ * In the CFG, block's code, value index for each registers at EXIT,
+ * and the jump relationship is show.
+ *
+ * example DOT for BPF `ip src host 1.1.1.1' is:
+ digraph BPF {
+ block0 [shape=ellipse, id="block-0" label="BLOCK0\n\n(000) ldh [12]\n(001) jeq #0x800 jt 2 jf 5" tooltip="val[A]=0 val[X]=0"];
+ block1 [shape=ellipse, id="block-1" label="BLOCK1\n\n(002) ld [26]\n(003) jeq #0x1010101 jt 4 jf 5" tooltip="val[A]=0 val[X]=0"];
+ block2 [shape=ellipse, id="block-2" label="BLOCK2\n\n(004) ret #68" tooltip="val[A]=0 val[X]=0", peripheries=2];
+ block3 [shape=ellipse, id="block-3" label="BLOCK3\n\n(005) ret #0" tooltip="val[A]=0 val[X]=0", peripheries=2];
+ "block0":se -> "block1":n [label="T"];
+ "block0":sw -> "block3":n [label="F"];
+ "block1":se -> "block2":n [label="T"];
+ "block1":sw -> "block3":n [label="F"];
+ }
+ *
+ * After install graphviz on http://www.graphviz.org/, save it as bpf.dot
+ * and run `dot -Tpng -O bpf.dot' to draw the graph.
+ */
+static void
+dot_dump(struct block *root)
+{
+ struct bpf_program f;
+ FILE *out = stdout;
+
+ memset(bids, 0, sizeof bids);
+ f.bf_insns = icode_to_fcode(root, &f.bf_len);
+
+ fprintf(out, "digraph BPF {\n");
+ unMarkAll();
+ dot_dump_node(root, &f, out);
+ unMarkAll();
+ dot_dump_edge(root, out);
+ fprintf(out, "}\n");
+
+ free((char *)f.bf_insns);
+}
+
+static void
+plain_dump(struct block *root)
{
struct bpf_program f;
putchar('\n');
free((char *)f.bf_insns);
}
+
+static void
+opt_dump(struct block *root)
+{
+ /* if optimizer debugging is enabled, output DOT graph
+ * `pcap_optimizer_debug=4' is equivalent to -dddd to follow -d/-dd/-ddd
+ * convention in tcpdump command line
+ */
+ if (pcap_optimizer_debug > 3)
+ dot_dump(root);
+ else
+ plain_dump(root);
+}
#endif