/* * NAME: optimize->binop() * DESCRIPTION: optimize a binary operator expression */ static Uint opt_binop(node **m) { node *n, *t; Uint d1, d2, d; Float f1, f2; n = *m; if (n->type == N_ADD && n->r.right->type == N_ADD && n->l.left->mod == n->r.right->mod && (n->mod == T_STRING || (n->mod & T_REF) != 0)) { /* * a + (b + c) --> (a + b) + c * the order in which these are added won't affect the final result */ t = n->l.left; n->l.left = n->r.right; n->r.right = n->l.left->r.right; n->l.left->r.right = n->l.left->l.left; n->l.left->l.left = t; } d1 = opt_expr(&n->l.left, FALSE); d2 = opt_expr(&n->r.right, FALSE); if (n->type == N_SUM) { if (n->l.left->type == N_RANGE) { d1 = max2(d1, 3); } else if (n->l.left->type != N_SUM) { d1++; } if (n->r.right->type == N_RANGE) { d2 = max2(d2, 3); } else { d2++; } return d1 + d2; } if (n->type == N_ADD) { if (n->r.right->type == N_STR && (n->l.left->type == N_ADD || n->l.left->type == N_SUM) && n->l.left->r.right->type == N_STR) { /* (x + s1) + s2 */ node_tostr(n->r.right, str_add(n->l.left->r.right->l.string, n->r.right->l.string)); n->l.left = n->l.left->l.left; return d1; } if (n->l.left->mod == T_STRING || (n->l.left->mod & T_REF) != 0) { /* * see if the summand operator can be used */ switch (n->l.left->type) { case N_ADD: n->l.left->type = N_SUM; d1 += 2; /* SUM_SIMPLE on both sides */ if (n->l.left->l.left->type == N_RANGE) { d1++; } n->type = N_SUM; if (n->r.right->type == N_RANGE) { d2 = max2(d2, 3); /* at least 3 */ } else { d2++; /* add SUM_SIMPLE */ } return d1 + d2; case N_FUNC: if (n->l.left->r.number == kd_allocate || n->l.left->r.number == kd_allocate_int || n->l.left->r.number == kd_allocate_float) { t = n->l.left->l.left->r.right; if (t != (node *) NULL && t->type != N_PAIR && t->type != N_SPREAD && t->mod == T_INT) { d1++; /* add SUM_ALLOCATE */ n->type = N_SUM; if (n->r.right->type == N_RANGE) { d2 = max2(d2, 3); /* at least 3 */ } else { d2++; /* add SUM_SIMPLE */ } return d1 + d2; } } /* fall through */ default: if (n->r.right->type != N_RANGE && n->r.right->type != N_AGGR) { if (n->r.right->type != N_FUNC || (n->r.right->r.number != kd_allocate && n->r.right->r.number != kd_allocate_int && n->r.right->r.number != kd_allocate_float)) { break; } t = n->r.right->l.left->r.right; if (t == (node *) NULL || t->type == N_PAIR || t->type == N_SPREAD || t->mod != T_INT) { break; } } /* fall through */ case N_AGGR: d1++; /* add SUM_SIMPLE */ n->type = N_SUM; if (n->r.right->type == N_RANGE) { d2 = max2(d2, 3); /* at least 3 */ } else { d2++; /* add SUM_SIMPLE */ } return d1 + d2; case N_RANGE: d1 = max2(d1, 3); /* at least 3 */ /* fall through */ case N_SUM: n->type = N_SUM; if (n->r.right->type == N_RANGE) { d2 = max2(d2, 3); /* at least 3 */ } else { d2++; /* add SUM_SIMPLE */ } return d1 + d2; } } } if (n->l.left->flags & F_CONST) { if (n->r.right->flags & F_CONST) { /* c1 . c2 */ return opt_binconst(m); } switch (n->type) { case N_ADD: if (!T_ARITHMETIC(n->l.left->mod) || !T_ARITHMETIC(n->r.right->mod)) { break; } /* fall through */ case N_ADD_INT: case N_ADD_FLOAT: case N_AND: case N_AND_INT: case N_EQ: case N_EQ_INT: case N_EQ_FLOAT: case N_MULT: case N_MULT_INT: case N_MULT_FLOAT: case N_NE: case N_NE_INT: case N_NE_FLOAT: case N_OR: case N_OR_INT: case N_XOR: case N_XOR_INT: /* swap constant to the right */ t = n->l.left; n->l.left = n->r.right; n->r.right = t; d = d1; d1 = d2; d2 = d; break; } } d = max2(d1, d2 + 1); if ((n->r.right->type == N_INT && n->r.right->l.number == 0 && n->l.left->mod == T_INT) || (n->r.right->type == N_FLOAT && NFLT_ISZERO(n->r.right) && n->l.left->mod == T_FLOAT) || (n->r.right->type == nil_node && n->r.right->l.number == 0 && n->l.left->mod != T_MIXED && T_POINTER(n->l.left->mod))) { /* * int == 0, float == 0.0, ptr == nil */ switch (n->type) { case N_EQ: case N_EQ_INT: case N_EQ_FLOAT: *m = opt_not(n->l.left); return d1; case N_NE: case N_NE_INT: case N_NE_FLOAT: *m = opt_tst(n->l.left); return d1; } } if (T_ARITHMETIC(n->mod) && n->mod == n->l.left->mod && n->mod == n->r.right->mod) { if (n->r.right->flags & F_CONST) { /* x . c */ if ((n->type == n->l.left->type || (n->mod == T_INT && n->l.left->mod == T_INT && n->type == n->l.left->type + 1)) && (n->l.left->r.right->flags & F_CONST)) { /* (x . c1) . c2 */ switch (n->type) { case N_ADD_FLOAT: case N_SUB_FLOAT: NFLT_GET(n->l.left->r.right, f1); NFLT_GET(n->r.right, f2); f1.add(f2); NFLT_PUT(n->l.left->r.right, f1); *m = n->l.left; d = d1; break; case N_ADD_INT: case N_SUB_INT: case N_LSHIFT_INT: case N_RSHIFT_INT: n->l.left->r.right->l.number += n->r.right->l.number; *m = n->l.left; d = d1; break; case N_AND_INT: n->l.left->r.right->l.number &= n->r.right->l.number; *m = n->l.left; d = d1; break; case N_DIV_FLOAT: case N_MULT_FLOAT: NFLT_GET(n->l.left->r.right, f1); NFLT_GET(n->r.right, f2); f1.mult(f2); NFLT_PUT(n->l.left->r.right, f1); *m = n->l.left; d = d1; break; case N_DIV_INT: case N_MULT_INT: n->l.left->r.right->l.number *= n->r.right->l.number; *m = n->l.left; d = d1; break; case N_OR_INT: n->l.left->r.right->l.number |= n->r.right->l.number; *m = n->l.left; d = d1; break; case N_XOR_INT: n->l.left->r.right->l.number ^= n->r.right->l.number; *m = n->l.left; d = d1; break; } } else { switch (n->type) { case N_ADD_FLOAT: if (n->l.left->type == N_SUB_FLOAT) { if (n->l.left->l.left->type == N_FLOAT) { /* (c1 - x) + c2 */ NFLT_GET(n->l.left->l.left, f1); NFLT_GET(n->r.right, f2); f1.add(f2); NFLT_PUT(n->l.left->l.left, f1); *m = n->l.left; return d1; } if (n->l.left->r.right->type == N_FLOAT) { /* (x - c1) + c2 */ NFLT_GET(n->l.left->r.right, f1); NFLT_GET(n->r.right, f2); f1.sub(f2); NFLT_PUT(n->l.left->r.right, f1); *m = n->l.left; d = d1; } } break; case N_ADD_INT: if (n->l.left->type == N_SUB || n->l.left->type == N_SUB_INT) { if (n->l.left->l.left->type == N_INT) { /* (c1 - x) + c2 */ n->l.left->l.left->l.number += n->r.right->l.number; *m = n->l.left; return d1; } if (n->l.left->r.right->type == N_INT) { /* (x - c1) + c2 */ n->l.left->r.right->l.number -= n->r.right->l.number; *m = n->l.left; d = d1; } } break; case N_DIV_FLOAT: if (n->l.left->type == N_MULT_FLOAT && n->l.left->r.right->type == N_FLOAT && !NFLT_ISZERO(n->r.right)) { /* (x * c1) / c2 */ NFLT_GET(n->l.left->r.right, f1); NFLT_GET(n->r.right, f2); f1.div(f2); NFLT_PUT(n->l.left->r.right, f1); *m = n->l.left; d = d1; } break; case N_MULT_FLOAT: if (n->l.left->type == N_DIV_FLOAT) { if (n->l.left->l.left->type == N_FLOAT) { /* (c1 / x) * c2 */ NFLT_GET(n->l.left->l.left, f1); NFLT_GET(n->r.right, f2); f1.mult(f2); NFLT_PUT(n->l.left->l.left, f1); *m = n->l.left; return d1; } if (n->l.left->r.right->type == N_FLOAT && !NFLT_ISZERO(n->l.left->r.right)) { /* (x / c1) * c2 */ NFLT_GET(n->r.right, f1); NFLT_GET(n->l.left->r.right, f2); f1.div(f2); NFLT_PUT(n->r.right, f1); n->l.left = n->l.left->l.left; d = d1; } } break; case N_SUB_FLOAT: if (n->l.left->type == N_ADD_FLOAT && n->l.left->r.right->type == N_FLOAT) { /* (x + c1) - c2 */ NFLT_GET(n->l.left->r.right, f1); NFLT_GET(n->r.right, f2); f1.sub(f2); NFLT_PUT(n->l.left->r.right, f1); *m = n->l.left; d = d1; } break; case N_SUB_INT: if (n->l.left->type == N_ADD_INT && n->l.left->r.right->type == N_INT) { /* (x + c1) - c2 */ n->l.left->r.right->l.number -= n->r.right->l.number; *m = n->l.left; d = d1; } break; } } } else if (n->l.left->flags & F_CONST) { /* c . x */ switch (n->type) { case N_SUB_FLOAT: if (n->r.right->type == N_SUB_FLOAT) { if (n->r.right->l.left->type == N_FLOAT) { /* c1 - (c2 - x) */ NFLT_GET(n->l.left, f1); NFLT_GET(n->r.right->l.left, f2); f1.sub(f2); n->type = N_ADD; n->l.left = n->r.right->r.right; n->r.right = n->r.right->l.left; NFLT_PUT(n->r.right, f1); d = d2; } else if (n->r.right->r.right->type == N_FLOAT) { /* c1 - (x - c2) */ NFLT_GET(n->l.left, f1); NFLT_GET(n->r.right->r.right, f2); f1.add(f2); NFLT_PUT(n->l.left, f1); n->r.right = n->r.right->l.left; return d2 + 1; } } else if (n->r.right->type == N_ADD_FLOAT && n->r.right->r.right->type == N_FLOAT) { /* c1 - (x + c2) */ NFLT_GET(n->l.left, f1); NFLT_GET(n->r.right->r.right, f2); f1.sub(f2); NFLT_PUT(n->l.left, f1); n->r.right = n->r.right->l.left; return d2 + 1; } break; case N_SUB_INT: if ((n->r.right->type == N_SUB || n->r.right->type == N_SUB_INT)) { if (n->r.right->l.left->type == N_INT) { /* c1 - (c2 - x) */ n->r.right->l.left->l.number -= n->l.left->l.number; n->type = n->r.right->type; n->l.left = n->r.right->r.right; n->r.right = n->r.right->l.left; d = d2; } else if (n->r.right->r.right->type == N_INT) { /* c1 - (x - c2) */ n->l.left->l.number += n->r.right->r.right->l.number; n->r.right->r.right = n->r.right->l.left; n->r.right->l.left = n->l.left; *m = n->r.right; return d2 + 1; } } else if (n->r.right->type == N_ADD_INT && n->r.right->r.right->type == N_INT) { /* c1 - (x + c2) */ n->l.left->l.number -= n->r.right->r.right->l.number; n->r.right = n->r.right->l.left; return d2 + 1; } break; case N_DIV_FLOAT: if (n->r.right->type == N_DIV_FLOAT) { if (n->r.right->l.left->type == N_FLOAT && !NFLT_ISZERO(n->r.right->l.left)) { /* c1 / (c2 / x) */ NFLT_GET(n->l.left, f1); NFLT_GET(n->r.right->l.left, f2); f1.div(f2); n->type = N_MULT; n->l.left = n->r.right->r.right; n->r.right = n->r.right->l.left; NFLT_PUT(n->r.right, f1); d = d2; } else if (n->r.right->r.right->type == N_FLOAT) { /* c1 / (x / c2) */ NFLT_GET(n->l.left, f1); NFLT_GET(n->r.right->r.right, f2); f1.mult(f2); NFLT_PUT(n->l.left, f1); n->r.right = n->r.right->l.left; return d2 + 1; } } else if (n->r.right->type == N_MULT_FLOAT && n->r.right->r.right->type == N_FLOAT && !NFLT_ISZERO(n->r.right->r.right)) { /* c1 / (x * c2) */ NFLT_GET(n->l.left, f1); NFLT_GET(n->r.right->r.right, f2); f1.div(f2); NFLT_PUT(n->l.left, f1); n->r.right = n->r.right->l.left; return d2 + 1; } break; } } n = *m; if (T_ARITHMETIC(n->l.left->mod) && (n->r.right->flags & F_CONST)) { switch (n->type) { case N_ADD: case N_ADD_FLOAT: case N_SUB: case N_SUB_FLOAT: if (NFLT_ISZERO(n->r.right)) { *m = n->l.left; d = d1; } break; case N_ADD_INT: case N_SUB_INT: case N_LSHIFT_INT: case N_RSHIFT_INT: case N_XOR_INT: if (n->r.right->l.number == 0) { *m = n->l.left; d = d1; } break; case N_AND_INT: if (n->r.right->l.number == 0) { n->type = N_COMMA; return opt_expr(m, FALSE); } if (n->r.right->l.number == -1) { *m = n->l.left; d = d1; } break; case N_MULT: case N_MULT_FLOAT: if (NFLT_ISZERO(n->r.right)) { n->type = N_COMMA; return opt_expr(m, FALSE); } /* fall through */ case N_DIV: case N_DIV_FLOAT: if (NFLT_ISONE(n->r.right)) { *m = n->l.left; d = d1; } break; case N_MULT_INT: if (n->r.right->l.number == 0) { n->type = N_COMMA; return opt_expr(m, FALSE); } /* fall through */ case N_DIV_INT: if (n->r.right->l.number == 1) { *m = n->l.left; d = d1; } break; case N_MOD_INT: if (n->r.right->l.number == 1) { n->r.right->l.number = 0; n->type = N_COMMA; return opt_expr(m, FALSE); } break; case N_OR_INT: if (n->r.right->l.number == -1) { n->type = N_COMMA; return opt_expr(m, FALSE); } if (n->r.right->l.number == 0) { *m = n->l.left; d = d1; } break; } } } return d; }
/* * NAME: optimize->stmt() * DESCRIPTION: optimize a statement */ node *opt_stmt(node *first, Uint *depth) { node *n, **m, **prev; Uint d; Uint d1, d2; int i; node *side; if (first == (node *) NULL) { *depth = 0; return (node *) NULL; } d = 0; prev = m = &first; for (;;) { n = ((*m)->type == N_PAIR) ? (*m)->l.left : *m; switch (n->type) { case N_BLOCK: n->l.left = opt_stmt(n->l.left, &d1); if (n->l.left == (node *) NULL) { n = (node *) NULL; } d = max2(d, d1); break; case N_CASE: n->l.left = opt_stmt(n->l.left, &d1); d = max2(d, d1); break; case N_COMPOUND: n->l.left = opt_stmt(n->l.left, &d1); if (n->l.left == (node *) NULL) { n = (node *) NULL; } else if (n->r.right != (node *) NULL) { n->r.right = opt_stmt(n->r.right, &d2); d1 = max2(d1, d2); } d = max2(d, d1); break; case N_DO: n->r.right = opt_stmt(n->r.right, &d1); side_start(&side, depth); d2 = opt_cond(&n->l.left, FALSE); d2 = max2(d2, side_end(&n->l.left, side, (node **) NULL, 0)); d = max3(d, d1, d2); break; case N_FOR: side_start(&side, depth); d1 = opt_cond(&n->l.left, FALSE); d1 = max2(d1, side_end(&n->l.left, side, (node **) NULL, 0)); i = opt_const(n->l.left); if (i == 0) { /* never */ n->r.right = opt_skip(n->r.right); if (n->r.right == (node *) NULL) { n->type = N_POP; n = opt_stmt(n, &d1); d = max2(d, d1); break; } } else if (i > 0) { /* always */ n->type = N_FOREVER; n = opt_stmt(n, &d1); d = max2(d, d1); break; } n->r.right = opt_stmt(n->r.right, &d2); d = max3(d, d1, d2); break; case N_FOREVER: if (n->l.left != (node *) NULL) { side_start(&side, depth); d1 = opt_expr(&n->l.left, TRUE); if (d1 == 0) { n->l.left = (node *) NULL; } d1 = max2(d1, side_end(&n->l.left, side, (node **) NULL, 0)); if (d1 == 0) { n->l.left = (node *) NULL; } } else { d1 = 0; } n->r.right = opt_stmt(n->r.right, &d2); d = max3(d, d1, d2); break; case N_RLIMITS: side_start(&side, depth); d1 = opt_expr(&n->l.left->l.left, FALSE); d1 = max2(d1, side_end(&n->l.left->l.left, side, (node **) NULL, 0)); side_start(&side, depth); d2 = opt_expr(&n->l.left->r.right, FALSE); d2 = max2(d2, side_end(&n->l.left->r.right, side, (node **) NULL, 0)); d1 = max2(d1, d2 + 1); n->r.right = opt_stmt(n->r.right, &d2); d = max3(d, d1, d2); break; case N_CATCH: n->l.left = opt_stmt(n->l.left, &d1); if (n->l.left == (node *) NULL) { n = opt_stmt(opt_skip(n->r.right), &d1); d = max2(d, d1); } else { n->r.right = opt_stmt(n->r.right, &d2); d = max3(d, d1, d2); } break; case N_IF: side_start(&side, depth); d1 = opt_cond(&n->l.left, FALSE); d1 = max2(d1, side_end(&n->l.left, side, (node **) NULL, 0)); i = opt_const(n->l.left); if (i == 0) { n->r.right->l.left = opt_skip(n->r.right->l.left); } else if (i > 0) { n->r.right->r.right = opt_skip(n->r.right->r.right); } n->r.right->l.left = opt_stmt(n->r.right->l.left, &d2); d1 = max2(d1, d2); n->r.right->r.right = opt_stmt(n->r.right->r.right, &d2); if (n->r.right->l.left == (node *) NULL) { if (n->r.right->r.right == (node *) NULL) { n->type = N_POP; n = opt_stmt(n, &d1); d = max2(d, d1); break; } n->l.left = opt_not(n->l.left); n->r.right->l.left = n->r.right->r.right; n->r.right->r.right = (node *) NULL; } d = max3(d, d1, d2); break; case N_PAIR: n = opt_stmt(n, &d1); d = max2(d, d1); break; case N_POP: side_start(&side, depth); d1 = opt_expr(&n->l.left, TRUE); if (d1 == 0) { n->l.left = (node *) NULL; } d = max3(d, d1, side_end(&n->l.left, side, (node **) NULL, 0)); if (n->l.left == (node *) NULL) { n = (node *) NULL; } break; case N_RETURN: side_start(&side, depth); d1 = opt_expr(&n->l.left, FALSE); d = max3(d, d1, side_end(&n->l.left, side, (node **) NULL, 0)); break; case N_SWITCH_INT: case N_SWITCH_RANGE: case N_SWITCH_STR: n->r.right->r.right = opt_stmt(n->r.right->r.right, &d1); if (n->r.right->r.right == (node *) NULL) { n = n->r.right; n->type = N_POP; n = opt_stmt(n, &d1); d = max2(d, d1); } else { side_start(&side, depth); d2 = opt_expr(&n->r.right->l.left, FALSE); d2 = max2(d2, side_end(&n->r.right->l.left, side, (node **) NULL, 0)); d = max3(d, d1, d2); } break; } if ((*m)->type == N_PAIR) { if (n == (node *) NULL) { *m = (*m)->r.right; } else { (*m)->l.left = n; if (n->flags & F_END) { n = opt_skip((*m)->r.right); if (n == (node *) NULL) { *m = (*m)->l.left; break; } (*m)->r.right = n; } prev = m; m = &(*m)->r.right; } } else { *m = n; if (n == (node *) NULL && prev != m) { *prev = (*prev)->l.left; } break; } } *depth = d; return first; }
/* * NAME: optimize->not() * DESCRIPTION: optimize a not operation */ static node *opt_not(node *n) { node *m; switch (n->type) { case N_INT: node_toint(n, (Int) (n->l.number == 0)); return n; case N_FLOAT: node_toint(n, (Int) NFLT_ISZERO(n)); return n; case N_STR: node_toint(n, (Int) FALSE); return n; case N_NIL: node_toint(n, (Int) TRUE); return n; case N_LAND: n->type = N_LOR; n->l.left = opt_not(n->l.left); n->r.right = opt_not(n->r.right); return n; case N_LOR: n->type = N_LAND; n->l.left = opt_not(n->l.left); n->r.right = opt_not(n->r.right); return n; case N_TST: n->type = N_NOT; return n; case N_NOT: n->type = N_TST; return n; case N_EQ: n->type = N_NE; return n; case N_EQ_INT: n->type = N_NE_INT; return n; case N_EQ_FLOAT: n->type = N_NE_FLOAT; return n; case N_NE: n->type = N_EQ; return n; case N_NE_INT: n->type = N_EQ_INT; return n; case N_NE_FLOAT: n->type = N_EQ_FLOAT; return n; case N_GT: n->type = N_LE; return n; case N_GT_INT: n->type = N_LE_INT; return n; case N_GT_FLOAT: n->type = N_LE_FLOAT; return n; case N_GE: n->type = N_LT; return n; case N_GE_INT: n->type = N_LT_INT; return n; case N_GE_FLOAT: n->type = N_LT_FLOAT; return n; case N_LT: n->type = N_GE; return n; case N_LT_INT: n->type = N_GE_INT; return n; case N_LT_FLOAT: n->type = N_GE_FLOAT; return n; case N_LE: n->type = N_GT; return n; case N_LE_INT: n->type = N_GT_INT; return n; case N_LE_FLOAT: n->type = N_GT_FLOAT; return n; case N_COMMA: n->mod = T_INT; n->r.right = opt_not(n->r.right); return n; default: m = node_new(n->line); m->type = N_NOT; m->mod = T_INT; m->l.left = n; return m; } }
static void opt_peep(struct block *b) { struct slist *s; struct slist *next, *last; int val; s = b->stmts; if (s == 0) return; last = s; for (/*empty*/; /*empty*/; s = next) { /* * Skip over nops. */ s = this_op(s); if (s == 0) break; /* nothing left in the block */ /* * Find the next real instruction after that one * (skipping nops). */ next = this_op(s->next); if (next == 0) break; /* no next instruction */ last = next; /* * st M[k] --> st M[k] * ldx M[k] tax */ if (s->s.code == BPF_ST && next->s.code == (BPF_LDX|BPF_MEM) && s->s.k == next->s.k) { done = 0; next->s.code = BPF_MISC|BPF_TAX; } /* * ld #k --> ldx #k * tax txa */ if (s->s.code == (BPF_LD|BPF_IMM) && next->s.code == (BPF_MISC|BPF_TAX)) { s->s.code = BPF_LDX|BPF_IMM; next->s.code = BPF_MISC|BPF_TXA; done = 0; } /* * This is an ugly special case, but it happens * when you say tcp[k] or udp[k] where k is a constant. */ if (s->s.code == (BPF_LD|BPF_IMM)) { struct slist *add, *tax, *ild; /* * Check that X isn't used on exit from this * block (which the optimizer might cause). * We know the code generator won't generate * any local dependencies. */ if (ATOMELEM(b->out_use, X_ATOM)) continue; /* * Check that the instruction following the ldi * is an addx, or it's an ldxms with an addx * following it (with 0 or more nops between the * ldxms and addx). */ if (next->s.code != (BPF_LDX|BPF_MSH|BPF_B)) add = next; else add = this_op(next->next); if (add == 0 || add->s.code != (BPF_ALU|BPF_ADD|BPF_X)) continue; /* * Check that a tax follows that (with 0 or more * nops between them). */ tax = this_op(add->next); if (tax == 0 || tax->s.code != (BPF_MISC|BPF_TAX)) continue; /* * Check that an ild follows that (with 0 or more * nops between them). */ ild = this_op(tax->next); if (ild == 0 || BPF_CLASS(ild->s.code) != BPF_LD || BPF_MODE(ild->s.code) != BPF_IND) continue; /* * We want to turn this sequence: * * (004) ldi #0x2 {s} * (005) ldxms [14] {next} -- optional * (006) addx {add} * (007) tax {tax} * (008) ild [x+0] {ild} * * into this sequence: * * (004) nop * (005) ldxms [14] * (006) nop * (007) nop * (008) ild [x+2] * * XXX We need to check that X is not * subsequently used, because we want to change * what'll be in it after this sequence. * * We know we can eliminate the accumulator * modifications earlier in the sequence since * it is defined by the last stmt of this sequence * (i.e., the last statement of the sequence loads * a value into the accumulator, so we can eliminate * earlier operations on the accumulator). */ ild->s.k += s->s.k; s->s.code = NOP; add->s.code = NOP; tax->s.code = NOP; done = 0; } } /* * If the comparison at the end of a block is an equality * comparison against a constant, and nobody uses the value * we leave in the A register at the end of a block, and * the operation preceding the comparison is an arithmetic * operation, we can sometime optimize it away. */ if (b->s.code == (BPF_JMP|BPF_JEQ|BPF_K) && !ATOMELEM(b->out_use, A_ATOM)) { /* * We can optimize away certain subtractions of the * X register. */ if (last->s.code == (BPF_ALU|BPF_SUB|BPF_X)) { val = b->val[X_ATOM]; if (vmap[val].is_const) { /* * If we have a subtract to do a comparison, * and the X register is a known constant, * we can merge this value into the * comparison: * * sub x -> nop * jeq #y jeq #(x+y) */ b->s.k += vmap[val].const_val; last->s.code = NOP; done = 0; } else if (b->s.k == 0) { /* * If the X register isn't a constant, * and the comparison in the test is * against 0, we can compare with the * X register, instead: * * sub x -> nop * jeq #0 jeq x */ last->s.code = NOP; b->s.code = BPF_JMP|BPF_JEQ|BPF_X; done = 0; } } /* * Likewise, a constant subtract can be simplified: * * sub #x -> nop * jeq #y -> jeq #(x+y) */ else if (last->s.code == (BPF_ALU|BPF_SUB|BPF_K)) { last->s.code = NOP; b->s.k += last->s.k; done = 0; } /* * And, similarly, a constant AND can be simplified * if we're testing against 0, i.e.: * * and #k nop * jeq #0 -> jset #k */ else if (last->s.code == (BPF_ALU|BPF_AND|BPF_K) && b->s.k == 0) { b->s.k = last->s.k; b->s.code = BPF_JMP|BPF_K|BPF_JSET; last->s.code = NOP; done = 0; opt_not(b); } } /* * jset #0 -> never * jset #ffffffff -> always */ if (b->s.code == (BPF_JMP|BPF_K|BPF_JSET)) { if (b->s.k == 0) JT(b) = JF(b); if (b->s.k == (int)0xffffffff) JF(b) = JT(b); } /* * If we're comparing against the index register, and the index * register is a known constant, we can just compare against that * constant. */ val = b->val[X_ATOM]; if (vmap[val].is_const && BPF_SRC(b->s.code) == BPF_X) { bpf_int32 v = vmap[val].const_val; b->s.code &= ~BPF_X; b->s.k = v; } /* * If the accumulator is a known constant, we can compute the * comparison result. */ val = b->val[A_ATOM]; if (vmap[val].is_const && BPF_SRC(b->s.code) == BPF_K) { bpf_int32 v = vmap[val].const_val; switch (BPF_OP(b->s.code)) { case BPF_JEQ: v = v == b->s.k; break; case BPF_JGT: v = (unsigned)v > (unsigned)b->s.k; break; case BPF_JGE: v = (unsigned)v >= (unsigned)b->s.k; break; case BPF_JSET: v &= b->s.k; break; default: abort(); } if (JF(b) != JT(b)) done = 0; if (v) JF(b) = JT(b); else JT(b) = JF(b); } }