/* * Expression handling * * Copyright (C) 2001-2007 Michael Urman, Peter Johnson * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND OTHER CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR OTHER CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include "util.h" #include "libyasm-stdint.h" #include "coretype.h" #include "bitvect.h" #include "errwarn.h" #include "intnum.h" #include "floatnum.h" #include "expr.h" #include "symrec.h" #include "bytecode.h" #include "section.h" #include "arch.h" static /*@only@*/ yasm_expr *expr_level_op (/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const, int simplify_ident, int simplify_reg_mul); static int expr_traverse_nodes_post(/*@null@*/ yasm_expr *e, /*@null@*/ void *d, int (*func) (/*@null@*/ yasm_expr *e, /*@null@*/ void *d)); static void expr_delete_term(yasm_expr__item *term, int recurse); /* Bitmap of used items. We should really never need more than 2 at a time, * so 31 is pretty much overkill. */ static unsigned long itempool_used = 0; static yasm_expr__item itempool[31]; /* allocate a new expression node, with children as defined. * If it's a unary operator, put the element in left and set right=NULL. */ /*@-compmempass@*/ yasm_expr * yasm_expr_create(yasm_expr_op op, yasm_expr__item *left, yasm_expr__item *right, unsigned long line) { yasm_expr *ptr, *sube; unsigned long z; ptr = yasm_xmalloc(sizeof(yasm_expr)); ptr->op = op; ptr->numterms = 0; ptr->terms[0].type = YASM_EXPR_NONE; ptr->terms[1].type = YASM_EXPR_NONE; if (left) { ptr->terms[0] = *left; /* structure copy */ z = (unsigned long)(left-itempool); if (z>=31) yasm_internal_error(N_("could not find expritem in pool")); itempool_used &= ~(1<numterms++; /* Search downward until we find something *other* than an * IDENT, then bring it up to the current level. */ while (ptr->terms[0].type == YASM_EXPR_EXPR && ptr->terms[0].data.expn->op == YASM_EXPR_IDENT) { sube = ptr->terms[0].data.expn; ptr->terms[0] = sube->terms[0]; /* structure copy */ /*@-usereleased@*/ yasm_xfree(sube); /*@=usereleased@*/ } } else { yasm_internal_error(N_("Right side of expression must exist")); } if (right) { ptr->terms[1] = *right; /* structure copy */ z = (unsigned long)(right-itempool); if (z>=31) yasm_internal_error(N_("could not find expritem in pool")); itempool_used &= ~(1<numterms++; /* Search downward until we find something *other* than an * IDENT, then bring it up to the current level. */ while (ptr->terms[1].type == YASM_EXPR_EXPR && ptr->terms[1].data.expn->op == YASM_EXPR_IDENT) { sube = ptr->terms[1].data.expn; ptr->terms[1] = sube->terms[0]; /* structure copy */ /*@-usereleased@*/ yasm_xfree(sube); /*@=usereleased@*/ } } ptr->line = line; return expr_level_op(ptr, 1, 1, 0); } /*@=compmempass@*/ /* helpers */ static yasm_expr__item * expr_get_item(void) { int z = 0; unsigned long v = itempool_used & 0x7fffffff; while (v & 1) { v >>= 1; z++; } if (z>=31) yasm_internal_error(N_("too many expritems")); itempool_used |= 1<type = YASM_EXPR_PRECBC; e->data.precbc = precbc; return e; } yasm_expr__item * yasm_expr_sym(yasm_symrec *s) { yasm_expr__item *e = expr_get_item(); e->type = YASM_EXPR_SYM; e->data.sym = s; return e; } yasm_expr__item * yasm_expr_expr(yasm_expr *x) { yasm_expr__item *e = expr_get_item(); e->type = YASM_EXPR_EXPR; e->data.expn = x; return e; } yasm_expr__item * yasm_expr_int(yasm_intnum *i) { yasm_expr__item *e = expr_get_item(); e->type = YASM_EXPR_INT; e->data.intn = i; return e; } yasm_expr__item * yasm_expr_float(yasm_floatnum *f) { yasm_expr__item *e = expr_get_item(); e->type = YASM_EXPR_FLOAT; e->data.flt = f; return e; } yasm_expr__item * yasm_expr_reg(uintptr_t reg) { yasm_expr__item *e = expr_get_item(); e->type = YASM_EXPR_REG; e->data.reg = reg; return e; } /* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into single * expritems if possible. Uses a simple n^2 algorithm because n is usually * quite small. Also works for precbc-precbc (or symrec-precbc, * precbc-symrec). */ static /*@only@*/ yasm_expr * expr_xform_bc_dist_base(/*@returned@*/ /*@only@*/ yasm_expr *e, /*@null@*/ void *cbd, int (*callback) (yasm_expr__item *ei, yasm_bytecode *precbc, yasm_bytecode *precbc2, void *cbd)) { int i; /*@dependent@*/ yasm_section *sect; /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; int numterms; /* Handle symrec-symrec in ADD exprs by looking for (-1*symrec) and * symrec term pairs (where both symrecs are in the same segment). */ if (e->op != YASM_EXPR_ADD) return e; for (i=0; inumterms; i++) { int j; yasm_expr *sube; yasm_intnum *intn; yasm_symrec *sym = NULL; /*@dependent@*/ yasm_section *sect2; /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc2; /* First look for an (-1*symrec) term */ if (e->terms[i].type != YASM_EXPR_EXPR) continue; sube = e->terms[i].data.expn; if (sube->op != YASM_EXPR_MUL || sube->numterms != 2) continue; if (sube->terms[0].type == YASM_EXPR_INT && (sube->terms[1].type == YASM_EXPR_SYM || sube->terms[1].type == YASM_EXPR_PRECBC)) { intn = sube->terms[0].data.intn; if (sube->terms[1].type == YASM_EXPR_PRECBC) precbc = sube->terms[1].data.precbc; else sym = sube->terms[1].data.sym; } else if ((sube->terms[0].type == YASM_EXPR_SYM || sube->terms[0].type == YASM_EXPR_PRECBC) && sube->terms[1].type == YASM_EXPR_INT) { if (sube->terms[0].type == YASM_EXPR_PRECBC) precbc = sube->terms[0].data.precbc; else sym = sube->terms[0].data.sym; intn = sube->terms[1].data.intn; } else continue; if (!yasm_intnum_is_neg1(intn)) continue; if (sym && !yasm_symrec_get_label(sym, &precbc)) continue; sect2 = yasm_bc_get_section(precbc); /* Now look for a symrec term in the same segment */ for (j=0; jnumterms; j++) { if (((e->terms[j].type == YASM_EXPR_SYM && yasm_symrec_get_label(e->terms[j].data.sym, &precbc2)) || (e->terms[j].type == YASM_EXPR_PRECBC && (precbc2 = e->terms[j].data.precbc))) && (sect = yasm_bc_get_section(precbc2)) && sect == sect2 && callback(&e->terms[j], precbc, precbc2, cbd)) { /* Delete the matching (-1*symrec) term */ yasm_expr_destroy(sube); e->terms[i].type = YASM_EXPR_NONE; break; /* stop looking for matching symrec term */ } } } /* Clean up any deleted (EXPR_NONE) terms */ numterms = 0; for (i=0; inumterms; i++) { if (e->terms[i].type != YASM_EXPR_NONE) e->terms[numterms++] = e->terms[i]; /* structure copy */ } if (e->numterms != numterms) { e->numterms = numterms; e = yasm_xrealloc(e, sizeof(yasm_expr)+((numterms<2) ? 0 : sizeof(yasm_expr__item)*(numterms-2))); if (numterms == 1) e->op = YASM_EXPR_IDENT; } return e; } static int expr_xform_bc_dist_cb(yasm_expr__item *ei, yasm_bytecode *precbc, yasm_bytecode *precbc2, /*@null@*/ void *d) { yasm_intnum *dist = yasm_calc_bc_dist(precbc, precbc2); if (!dist) return 0; /* Change the term to an integer */ ei->type = YASM_EXPR_INT; ei->data.intn = dist; return 1; } /* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into integers if * possible. */ static /*@only@*/ yasm_expr * expr_xform_bc_dist(/*@returned@*/ /*@only@*/ yasm_expr *e) { return expr_xform_bc_dist_base(e, NULL, expr_xform_bc_dist_cb); } typedef struct bc_dist_subst_cbd { void (*callback) (unsigned int subst, yasm_bytecode *precbc, yasm_bytecode *precbc2, void *cbd); void *cbd; unsigned int subst; } bc_dist_subst_cbd; static int expr_bc_dist_subst_cb(yasm_expr__item *ei, yasm_bytecode *precbc, yasm_bytecode *precbc2, /*@null@*/ void *d) { bc_dist_subst_cbd *my_cbd = d; assert(my_cbd != NULL); /* Call higher-level callback */ my_cbd->callback(my_cbd->subst, precbc, precbc2, my_cbd->cbd); /* Change the term to an subst */ ei->type = YASM_EXPR_SUBST; ei->data.subst = my_cbd->subst; my_cbd->subst++; return 1; } static yasm_expr * expr_xform_bc_dist_subst(yasm_expr *e, void *d) { return expr_xform_bc_dist_base(e, d, expr_bc_dist_subst_cb); } int yasm_expr__bc_dist_subst(yasm_expr **ep, void *cbd, void (*callback) (unsigned int subst, yasm_bytecode *precbc, yasm_bytecode *precbc2, void *cbd)) { bc_dist_subst_cbd my_cbd; /* callback info for low-level callback */ my_cbd.callback = callback; my_cbd.cbd = cbd; my_cbd.subst = 0; *ep = yasm_expr__level_tree(*ep, 1, 1, 1, 0, &expr_xform_bc_dist_subst, &my_cbd); return my_cbd.subst; } /* Negate just a single ExprItem by building a -1*ei subexpression */ static void expr_xform_neg_item(yasm_expr *e, yasm_expr__item *ei) { yasm_expr *sube = yasm_xmalloc(sizeof(yasm_expr)); /* Build -1*ei subexpression */ sube->op = YASM_EXPR_MUL; sube->line = e->line; sube->numterms = 2; sube->terms[0].type = YASM_EXPR_INT; sube->terms[0].data.intn = yasm_intnum_create_int(-1); sube->terms[1] = *ei; /* structure copy */ /* Replace original ExprItem with subexp */ ei->type = YASM_EXPR_EXPR; ei->data.expn = sube; } /* Negates e by multiplying by -1, with distribution over lower-precedence * operators (eg ADD) and special handling to simplify result w/ADD, NEG, and * others. * * Returns a possibly reallocated e. */ static /*@only@*/ yasm_expr * expr_xform_neg_helper(/*@returned@*/ /*@only@*/ yasm_expr *e) { yasm_expr *ne; int i; switch (e->op) { case YASM_EXPR_ADD: /* distribute (recursively if expr) over terms */ for (i=0; inumterms; i++) { if (e->terms[i].type == YASM_EXPR_EXPR) e->terms[i].data.expn = expr_xform_neg_helper(e->terms[i].data.expn); else expr_xform_neg_item(e, &e->terms[i]); } break; case YASM_EXPR_SUB: /* change op to ADD, and recursively negate left side (if expr) */ e->op = YASM_EXPR_ADD; if (e->terms[0].type == YASM_EXPR_EXPR) e->terms[0].data.expn = expr_xform_neg_helper(e->terms[0].data.expn); else expr_xform_neg_item(e, &e->terms[0]); break; case YASM_EXPR_NEG: /* Negating a negated value? Make it an IDENT. */ e->op = YASM_EXPR_IDENT; break; case YASM_EXPR_IDENT: /* Negating an ident? Change it into a MUL w/ -1 if there's no * floatnums present below; if there ARE floatnums, recurse. */ if (e->terms[0].type == YASM_EXPR_FLOAT) yasm_floatnum_calc(e->terms[0].data.flt, YASM_EXPR_NEG, NULL); else if (e->terms[0].type == YASM_EXPR_INT) yasm_intnum_calc(e->terms[0].data.intn, YASM_EXPR_NEG, NULL); else if (e->terms[0].type == YASM_EXPR_EXPR && yasm_expr__contains(e->terms[0].data.expn, YASM_EXPR_FLOAT)) expr_xform_neg_helper(e->terms[0].data.expn); else { e->op = YASM_EXPR_MUL; e->numterms = 2; e->terms[1].type = YASM_EXPR_INT; e->terms[1].data.intn = yasm_intnum_create_int(-1); } break; default: /* Everything else. MUL will be combined when it's leveled. * Make a new expr (to replace e) with -1*e. */ ne = yasm_xmalloc(sizeof(yasm_expr)); ne->op = YASM_EXPR_MUL; ne->line = e->line; ne->numterms = 2; ne->terms[0].type = YASM_EXPR_INT; ne->terms[0].data.intn = yasm_intnum_create_int(-1); ne->terms[1].type = YASM_EXPR_EXPR; ne->terms[1].data.expn = e; return ne; } return e; } /* Transforms negatives into expressions that are easier to combine: * -x -> -1*x * a-b -> a+(-1*b) * * Call post-order on an expression tree to transform the entire tree. * * Returns a possibly reallocated e. */ static /*@only@*/ yasm_expr * expr_xform_neg(/*@returned@*/ /*@only@*/ yasm_expr *e) { switch (e->op) { case YASM_EXPR_NEG: /* Turn -x into -1*x */ e->op = YASM_EXPR_IDENT; return expr_xform_neg_helper(e); case YASM_EXPR_SUB: /* Turn a-b into a+(-1*b) */ /* change op to ADD, and recursively negate right side (if expr) */ e->op = YASM_EXPR_ADD; if (e->terms[1].type == YASM_EXPR_EXPR) e->terms[1].data.expn = expr_xform_neg_helper(e->terms[1].data.expn); else expr_xform_neg_item(e, &e->terms[1]); break; default: break; } return e; } /* Look for simple identities that make the entire result constant: * 0*&x, -1|x, etc. */ static int expr_is_constant(yasm_expr_op op, yasm_intnum *intn) { int iszero = yasm_intnum_is_zero(intn); return ((iszero && op == YASM_EXPR_MUL) || (iszero && op == YASM_EXPR_AND) || (iszero && op == YASM_EXPR_LAND) || (yasm_intnum_is_neg1(intn) && op == YASM_EXPR_OR)); } /* Look for simple "left" identities like 0+x, 1*x, etc. */ static int expr_can_destroy_int_left(yasm_expr_op op, yasm_intnum *intn) { int iszero = yasm_intnum_is_zero(intn); return ((yasm_intnum_is_pos1(intn) && op == YASM_EXPR_MUL) || (iszero && op == YASM_EXPR_ADD) || (yasm_intnum_is_neg1(intn) && op == YASM_EXPR_AND) || (!iszero && op == YASM_EXPR_LAND) || (iszero && op == YASM_EXPR_OR) || (iszero && op == YASM_EXPR_LOR)); } /* Look for simple "right" identities like x+|-0, x*&/1 */ static int expr_can_destroy_int_right(yasm_expr_op op, yasm_intnum *intn) { int iszero = yasm_intnum_is_zero(intn); int ispos1 = yasm_intnum_is_pos1(intn); return ((ispos1 && op == YASM_EXPR_MUL) || (ispos1 && op == YASM_EXPR_DIV) || (iszero && op == YASM_EXPR_ADD) || (iszero && op == YASM_EXPR_SUB) || (yasm_intnum_is_neg1(intn) && op == YASM_EXPR_AND) || (!iszero && op == YASM_EXPR_LAND) || (iszero && op == YASM_EXPR_OR) || (iszero && op == YASM_EXPR_LOR) || (iszero && op == YASM_EXPR_SHL) || (iszero && op == YASM_EXPR_SHR)); } /* Check for and simplify identities. Returns new number of expr terms. * Sets e->op = EXPR_IDENT if numterms ends up being 1. * Uses numterms parameter instead of e->numterms for basis of "new" number * of terms. * Assumes int_term is *only* integer term in e. * NOTE: Really designed to only be used by expr_level_op(). */ static int expr_simplify_identity(yasm_expr *e, int numterms, int *int_term, int simplify_reg_mul) { int i; int save_numterms; /* Don't do this step if it's 1*REG. Save and restore numterms so * yasm_expr__contains() works correctly. */ save_numterms = e->numterms; e->numterms = numterms; if (simplify_reg_mul || e->op != YASM_EXPR_MUL || !yasm_intnum_is_pos1(e->terms[*int_term].data.intn) || !yasm_expr__contains(e, YASM_EXPR_REG)) { /* Check for simple identities that delete the intnum. * Don't delete if the intnum is the only thing in the expn. */ if ((*int_term == 0 && numterms > 1 && expr_can_destroy_int_left(e->op, e->terms[0].data.intn)) || (*int_term > 0 && expr_can_destroy_int_right(e->op, e->terms[*int_term].data.intn))) { /* Delete the intnum */ yasm_intnum_destroy(e->terms[*int_term].data.intn); /* Slide everything to its right over by 1 */ if (*int_term != numterms-1) /* if it wasn't last.. */ memmove(&e->terms[*int_term], &e->terms[*int_term+1], (numterms-1-*int_term)*sizeof(yasm_expr__item)); /* Update numterms */ numterms--; *int_term = -1; /* no longer an int term */ } } e->numterms = save_numterms; /* Check for simple identites that delete everything BUT the intnum. * Don't bother if the intnum is the only thing in the expn. */ if (numterms > 1 && *int_term != -1 && expr_is_constant(e->op, e->terms[*int_term].data.intn)) { /* Loop through, deleting everything but the integer term */ for (i=0; inumterms; i++) if (i != *int_term) expr_delete_term(&e->terms[i], 1); /* Move integer term to the first term (if not already there) */ if (*int_term != 0) e->terms[0] = e->terms[*int_term]; /* structure copy */ /* Set numterms to 1 */ numterms = 1; } /* Compute NOT, NEG, and LNOT on single intnum. */ if (numterms == 1 && *int_term == 0 && (e->op == YASM_EXPR_NOT || e->op == YASM_EXPR_NEG || e->op == YASM_EXPR_LNOT)) yasm_intnum_calc(e->terms[0].data.intn, e->op, NULL); /* Change expression to IDENT if possible. */ if (numterms == 1) e->op = YASM_EXPR_IDENT; /* Return the updated numterms */ return numterms; } /* Levels the expression tree starting at e. Eg: * a+(b+c) -> a+b+c * (a+b)+(c+d) -> a+b+c+d * Naturally, only levels operators that allow more than two operand terms. * NOTE: only does *one* level of leveling (no recursion). Should be called * post-order on a tree to combine deeper levels. * Also brings up any IDENT values into the current level (for ALL operators). * Folds (combines by evaluation) *integer* constant values if fold_const != 0. * * Returns a possibly reallocated e. */ /*@-mustfree@*/ static /*@only@*/ yasm_expr * expr_level_op(/*@returned@*/ /*@only@*/ yasm_expr *e, int fold_const, int simplify_ident, int simplify_reg_mul) { int i, j, o, fold_numterms, level_numterms, level_fold_numterms; int first_int_term = -1; /* Determine how many operands will need to be brought up (for leveling). * Go ahead and bring up any IDENT'ed values. */ while (e->op == YASM_EXPR_IDENT && e->terms[0].type == YASM_EXPR_EXPR) { yasm_expr *sube = e->terms[0].data.expn; yasm_xfree(e); e = sube; } /* If non-numeric expression, don't fold constants. */ if (e->op > YASM_EXPR_NONNUM) fold_const = 0; level_numterms = e->numterms; level_fold_numterms = 0; for (i=0; inumterms; i++) { /* Search downward until we find something *other* than an * IDENT, then bring it up to the current level. */ while (e->terms[i].type == YASM_EXPR_EXPR && e->terms[i].data.expn->op == YASM_EXPR_IDENT) { yasm_expr *sube = e->terms[i].data.expn; e->terms[i] = sube->terms[0]; yasm_xfree(sube); } if (e->terms[i].type == YASM_EXPR_EXPR && e->terms[i].data.expn->op == e->op) { /* It's an expression w/the same operator, add in its numterms. * But don't forget to subtract one for the expr itself! */ level_numterms += e->terms[i].data.expn->numterms - 1; /* If we're folding constants, count up the number of constants * that will be merged in. */ if (fold_const) for (j=0; jterms[i].data.expn->numterms; j++) if (e->terms[i].data.expn->terms[j].type == YASM_EXPR_INT) level_fold_numterms++; } /* Find the first integer term (if one is present) if we're folding * constants. */ if (fold_const && first_int_term == -1 && e->terms[i].type == YASM_EXPR_INT) first_int_term = i; } /* Look for other integer terms if there's one and combine. * Also eliminate empty spaces when combining and adjust numterms * variables. */ fold_numterms = e->numterms; if (first_int_term != -1) { for (i=first_int_term+1, o=first_int_term+1; inumterms; i++) { if (e->terms[i].type == YASM_EXPR_INT) { yasm_intnum_calc(e->terms[first_int_term].data.intn, e->op, e->terms[i].data.intn); fold_numterms--; level_numterms--; /* make sure to delete folded intnum */ yasm_intnum_destroy(e->terms[i].data.intn); } else if (o != i) { /* copy term if it changed places */ e->terms[o++] = e->terms[i]; } else o++; } if (simplify_ident) { int new_fold_numterms; /* Simplify identities and make IDENT if possible. */ new_fold_numterms = expr_simplify_identity(e, fold_numterms, &first_int_term, simplify_reg_mul); level_numterms -= fold_numterms-new_fold_numterms; fold_numterms = new_fold_numterms; } if (fold_numterms == 1) e->op = YASM_EXPR_IDENT; } /* Only level operators that allow more than two operand terms. * Also don't bother leveling if it's not necessary to bring up any terms. */ if ((e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_MUL && e->op != YASM_EXPR_OR && e->op != YASM_EXPR_AND && e->op != YASM_EXPR_LOR && e->op != YASM_EXPR_LAND && e->op != YASM_EXPR_LXOR && e->op != YASM_EXPR_XOR) || level_numterms <= fold_numterms) { /* Downsize e if necessary */ if (fold_numterms < e->numterms && e->numterms > 2) e = yasm_xrealloc(e, sizeof(yasm_expr)+((fold_numterms<2) ? 0 : sizeof(yasm_expr__item)*(fold_numterms-2))); /* Update numterms */ e->numterms = fold_numterms; return e; } /* Adjust numterms for constant folding from terms being "pulled up". * Careful: if there's no integer term in e, then save space for it. */ if (fold_const) { level_numterms -= level_fold_numterms; if (first_int_term == -1 && level_fold_numterms != 0) level_numterms++; } /* Alloc more (or conceivably less, but not usually) space for e */ e = yasm_xrealloc(e, sizeof(yasm_expr)+((level_numterms<2) ? 0 : sizeof(yasm_expr__item)*(level_numterms-2))); /* Copy up ExprItem's. Iterate from right to left to keep the same * ordering as was present originally. * Combine integer terms as necessary. */ for (i=fold_numterms-1, o=level_numterms-1; i>=0; i--) { if (e->terms[i].type == YASM_EXPR_EXPR && e->terms[i].data.expn->op == e->op) { /* bring up subexpression */ yasm_expr *sube = e->terms[i].data.expn; /* copy terms right to left */ for (j=sube->numterms-1; j>=0; j--) { if (fold_const && sube->terms[j].type == YASM_EXPR_INT) { /* Need to fold it in.. but if there's no int term already, * just copy into a new one. */ if (first_int_term == -1) { first_int_term = o--; e->terms[first_int_term] = sube->terms[j]; /* struc */ } else { yasm_intnum_calc(e->terms[first_int_term].data.intn, e->op, sube->terms[j].data.intn); /* make sure to delete folded intnum */ yasm_intnum_destroy(sube->terms[j].data.intn); } } else { if (o == first_int_term) o--; e->terms[o--] = sube->terms[j]; /* structure copy */ } } /* delete subexpression, but *don't delete nodes* (as we've just * copied them!) */ yasm_xfree(sube); } else if (o != i) { /* copy operand if it changed places */ if (o == first_int_term) o--; e->terms[o] = e->terms[i]; /* If we moved the first_int_term, change first_int_num too */ if (i == first_int_term) first_int_term = o; o--; } else o--; } /* Simplify identities, make IDENT if possible, and save to e->numterms. */ if (simplify_ident && first_int_term != -1) { e->numterms = expr_simplify_identity(e, level_numterms, &first_int_term, simplify_reg_mul); } else { e->numterms = level_numterms; if (level_numterms == 1) e->op = YASM_EXPR_IDENT; } return e; } /*@=mustfree@*/ typedef SLIST_HEAD(yasm__exprhead, yasm__exprentry) yasm__exprhead; typedef struct yasm__exprentry { /*@reldef@*/ SLIST_ENTRY(yasm__exprentry) next; /*@null@*/ const yasm_expr *e; } yasm__exprentry; static yasm_expr * expr_expand_equ(yasm_expr *e, yasm__exprhead *eh) { int i; yasm__exprentry ee; /* traverse terms */ for (i=0; inumterms; i++) { const yasm_expr *equ_expr; /* Expand equ's. */ if (e->terms[i].type == YASM_EXPR_SYM && (equ_expr = yasm_symrec_get_equ(e->terms[i].data.sym))) { yasm__exprentry *np; /* Check for circular reference */ SLIST_FOREACH(np, eh, next) { if (np->e == equ_expr) { yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("circular reference detected")); return e; } } e->terms[i].type = YASM_EXPR_EXPR; e->terms[i].data.expn = yasm_expr_copy(equ_expr); /* Remember we saw this equ and recurse */ ee.e = equ_expr; SLIST_INSERT_HEAD(eh, &ee, next); e->terms[i].data.expn = expr_expand_equ(e->terms[i].data.expn, eh); SLIST_REMOVE_HEAD(eh, next); } else if (e->terms[i].type == YASM_EXPR_EXPR) /* Recurse */ e->terms[i].data.expn = expr_expand_equ(e->terms[i].data.expn, eh); } return e; } static yasm_expr * expr_level_tree(yasm_expr *e, int fold_const, int simplify_ident, int simplify_reg_mul, int calc_bc_dist, yasm_expr_xform_func expr_xform_extra, void *expr_xform_extra_data) { int i; e = expr_xform_neg(e); /* traverse terms */ for (i=0; inumterms; i++) { /* Recurse */ if (e->terms[i].type == YASM_EXPR_EXPR) e->terms[i].data.expn = expr_level_tree(e->terms[i].data.expn, fold_const, simplify_ident, simplify_reg_mul, calc_bc_dist, expr_xform_extra, expr_xform_extra_data); } /* Check for SEG of SEG:OFF, if we match, simplify to just the segment */ if (e->op == YASM_EXPR_SEG && e->terms[0].type == YASM_EXPR_EXPR && e->terms[0].data.expn->op == YASM_EXPR_SEGOFF) { e->op = YASM_EXPR_IDENT; e->terms[0].data.expn->op = YASM_EXPR_IDENT; /* Destroy the second (offset) term */ e->terms[0].data.expn->numterms = 1; expr_delete_term(&e->terms[0].data.expn->terms[1], 1); } /* do callback */ e = expr_level_op(e, fold_const, simplify_ident, simplify_reg_mul); if (calc_bc_dist || expr_xform_extra) { if (calc_bc_dist) e = expr_xform_bc_dist(e); if (expr_xform_extra) e = expr_xform_extra(e, expr_xform_extra_data); e = expr_level_tree(e, fold_const, simplify_ident, simplify_reg_mul, 0, NULL, NULL); } return e; } /* Level an entire expn tree, expanding equ's as we go */ yasm_expr * yasm_expr__level_tree(yasm_expr *e, int fold_const, int simplify_ident, int simplify_reg_mul, int calc_bc_dist, yasm_expr_xform_func expr_xform_extra, void *expr_xform_extra_data) { yasm__exprhead eh; SLIST_INIT(&eh); if (!e) return 0; e = expr_expand_equ(e, &eh); e = expr_level_tree(e, fold_const, simplify_ident, simplify_reg_mul, calc_bc_dist, expr_xform_extra, expr_xform_extra_data); return e; } /* Comparison function for expr_order_terms(). * Assumes ExprType enum is in canonical order. */ static int expr_order_terms_compare(const void *va, const void *vb) { const yasm_expr__item *a = va, *b = vb; return (a->type - b->type); } /* Reorder terms of e into canonical order. Only reorders if reordering * doesn't change meaning of expression. (eg, doesn't reorder SUB). * Canonical order: REG, INT, FLOAT, SYM, EXPR. * Multiple terms of a single type are kept in the same order as in * the original expression. * NOTE: Only performs reordering on *one* level (no recursion). */ void yasm_expr__order_terms(yasm_expr *e) { /* don't bother reordering if only one element */ if (e->numterms == 1) return; /* only reorder some types of operations */ switch (e->op) { case YASM_EXPR_ADD: case YASM_EXPR_MUL: case YASM_EXPR_OR: case YASM_EXPR_AND: case YASM_EXPR_XOR: case YASM_EXPR_LOR: case YASM_EXPR_LAND: case YASM_EXPR_LXOR: /* Use mergesort to sort. It's fast on already sorted values and a * stable sort (multiple terms of same type are kept in the same * order). */ yasm__mergesort(e->terms, (size_t)e->numterms, sizeof(yasm_expr__item), expr_order_terms_compare); break; default: break; } } static void expr_item_copy(yasm_expr__item *dest, const yasm_expr__item *src) { dest->type = src->type; switch (src->type) { case YASM_EXPR_SYM: /* Symbols don't need to be copied */ dest->data.sym = src->data.sym; break; case YASM_EXPR_PRECBC: /* Nor do direct bytecode references */ dest->data.precbc = src->data.precbc; break; case YASM_EXPR_EXPR: dest->data.expn = yasm_expr__copy_except(src->data.expn, -1); break; case YASM_EXPR_INT: dest->data.intn = yasm_intnum_copy(src->data.intn); break; case YASM_EXPR_FLOAT: dest->data.flt = yasm_floatnum_copy(src->data.flt); break; case YASM_EXPR_REG: dest->data.reg = src->data.reg; break; case YASM_EXPR_SUBST: dest->data.subst = src->data.subst; break; default: break; } } /* Copy entire expression EXCEPT for index "except" at *top level only*. */ yasm_expr * yasm_expr__copy_except(const yasm_expr *e, int except) { yasm_expr *n; int i; n = yasm_xmalloc(sizeof(yasm_expr) + sizeof(yasm_expr__item)*(e->numterms<2?0:e->numterms-2)); n->op = e->op; n->line = e->line; n->numterms = e->numterms; for (i=0; inumterms; i++) { if (i != except) expr_item_copy(&n->terms[i], &e->terms[i]); } return n; } static void expr_delete_term(yasm_expr__item *term, int recurse) { switch (term->type) { case YASM_EXPR_INT: yasm_intnum_destroy(term->data.intn); break; case YASM_EXPR_FLOAT: yasm_floatnum_destroy(term->data.flt); break; case YASM_EXPR_EXPR: if (recurse) yasm_expr_destroy(term->data.expn); break; default: break; } } static int expr_destroy_each(/*@only@*/ yasm_expr *e, /*@unused@*/ void *d) { int i; for (i=0; inumterms; i++) expr_delete_term(&e->terms[i], 0); yasm_xfree(e); /* free ourselves */ return 0; /* don't stop recursion */ } /*@-mustfree@*/ void yasm_expr_destroy(yasm_expr *e) { expr_traverse_nodes_post(e, NULL, expr_destroy_each); } /*@=mustfree@*/ int yasm_expr_is_op(const yasm_expr *e, yasm_expr_op op) { return (e->op == op); } static int expr_contains_callback(const yasm_expr__item *ei, void *d) { yasm_expr__type *t = d; return (ei->type & *t); } int yasm_expr__contains(const yasm_expr *e, yasm_expr__type t) { return yasm_expr__traverse_leaves_in_const(e, &t, expr_contains_callback); } typedef struct subst_cbd { unsigned int num_items; const yasm_expr__item *items; } subst_cbd; static int expr_subst_callback(yasm_expr__item *ei, void *d) { subst_cbd *cbd = d; if (ei->type != YASM_EXPR_SUBST) return 0; if (ei->data.subst >= cbd->num_items) return 1; /* error */ expr_item_copy(ei, &cbd->items[ei->data.subst]); return 0; } int yasm_expr__subst(yasm_expr *e, unsigned int num_items, const yasm_expr__item *items) { subst_cbd cbd; cbd.num_items = num_items; cbd.items = items; return yasm_expr__traverse_leaves_in(e, &cbd, expr_subst_callback); } /* Traverse over expression tree, calling func for each operation AFTER the * branches (if expressions) have been traversed (eg, postorder * traversal). The data pointer d is passed to each func call. * * Stops early (and returns 1) if func returns 1. Otherwise returns 0. */ static int expr_traverse_nodes_post(yasm_expr *e, void *d, int (*func) (/*@null@*/ yasm_expr *e, /*@null@*/ void *d)) { int i; if (!e) return 0; /* traverse terms */ for (i=0; inumterms; i++) { if (e->terms[i].type == YASM_EXPR_EXPR && expr_traverse_nodes_post(e->terms[i].data.expn, d, func)) return 1; } /* do callback */ return func(e, d); } /* Traverse over expression tree in order, calling func for each leaf * (non-operation). The data pointer d is passed to each func call. * * Stops early (and returns 1) if func returns 1. Otherwise returns 0. */ int yasm_expr__traverse_leaves_in_const(const yasm_expr *e, void *d, int (*func) (/*@null@*/ const yasm_expr__item *ei, /*@null@*/ void *d)) { int i; if (!e) return 0; for (i=0; inumterms; i++) { if (e->terms[i].type == YASM_EXPR_EXPR) { if (yasm_expr__traverse_leaves_in_const(e->terms[i].data.expn, d, func)) return 1; } else { if (func(&e->terms[i], d)) return 1; } } return 0; } /* Traverse over expression tree in order, calling func for each leaf * (non-operation). The data pointer d is passed to each func call. * * Stops early (and returns 1) if func returns 1. Otherwise returns 0. */ int yasm_expr__traverse_leaves_in(yasm_expr *e, void *d, int (*func) (/*@null@*/ yasm_expr__item *ei, /*@null@*/ void *d)) { int i; if (!e) return 0; for (i=0; inumterms; i++) { if (e->terms[i].type == YASM_EXPR_EXPR) { if (yasm_expr__traverse_leaves_in(e->terms[i].data.expn, d, func)) return 1; } else { if (func(&e->terms[i], d)) return 1; } } return 0; } yasm_expr * yasm_expr_extract_deep_segoff(yasm_expr **ep) { yasm_expr *retval; yasm_expr *e = *ep; int i; /* Try to extract at this level */ retval = yasm_expr_extract_segoff(ep); if (retval) return retval; /* Not at this level? Search any expr children. */ for (i=0; inumterms; i++) { if (e->terms[i].type == YASM_EXPR_EXPR) { retval = yasm_expr_extract_deep_segoff(&e->terms[i].data.expn); if (retval) return retval; } } /* Didn't find one */ return NULL; } yasm_expr * yasm_expr_extract_segoff(yasm_expr **ep) { yasm_expr *retval; yasm_expr *e = *ep; /* If not SEG:OFF, we can't do this transformation */ if (e->op != YASM_EXPR_SEGOFF) return NULL; /* Extract the SEG portion out to its own expression */ if (e->terms[0].type == YASM_EXPR_EXPR) retval = e->terms[0].data.expn; else { /* Need to build IDENT expression to hold non-expression contents */ retval = yasm_xmalloc(sizeof(yasm_expr)); retval->op = YASM_EXPR_IDENT; retval->numterms = 1; retval->terms[0] = e->terms[0]; /* structure copy */ } /* Delete the SEG: portion by changing the expression into an IDENT */ e->op = YASM_EXPR_IDENT; e->numterms = 1; e->terms[0] = e->terms[1]; /* structure copy */ return retval; } yasm_expr * yasm_expr_extract_wrt(yasm_expr **ep) { yasm_expr *retval; yasm_expr *e = *ep; /* If not WRT, we can't do this transformation */ if (e->op != YASM_EXPR_WRT) return NULL; /* Extract the right side portion out to its own expression */ if (e->terms[1].type == YASM_EXPR_EXPR) retval = e->terms[1].data.expn; else { /* Need to build IDENT expression to hold non-expression contents */ retval = yasm_xmalloc(sizeof(yasm_expr)); retval->op = YASM_EXPR_IDENT; retval->numterms = 1; retval->terms[0] = e->terms[1]; /* structure copy */ } /* Delete the right side portion by changing the expr into an IDENT */ e->op = YASM_EXPR_IDENT; e->numterms = 1; return retval; } /*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/ yasm_intnum * yasm_expr_get_intnum(yasm_expr **ep, int calc_bc_dist) { *ep = yasm_expr_simplify(*ep, calc_bc_dist); if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_INT) return (*ep)->terms[0].data.intn; else return (yasm_intnum *)NULL; } /*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/ /*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/ const yasm_symrec * yasm_expr_get_symrec(yasm_expr **ep, int simplify) { if (simplify) *ep = yasm_expr_simplify(*ep, 0); if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_SYM) return (*ep)->terms[0].data.sym; else return (yasm_symrec *)NULL; } /*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/ /*@-unqualifiedtrans -nullderef -nullstate -onlytrans@*/ const uintptr_t * yasm_expr_get_reg(yasm_expr **ep, int simplify) { if (simplify) *ep = yasm_expr_simplify(*ep, 0); if ((*ep)->op == YASM_EXPR_IDENT && (*ep)->terms[0].type == YASM_EXPR_REG) return &((*ep)->terms[0].data.reg); else return NULL; } /*@=unqualifiedtrans =nullderef -nullstate -onlytrans@*/ void yasm_expr_print(const yasm_expr *e, FILE *f) { char opstr[8]; int i; if (!e) { fprintf(f, "(nil)"); return; } switch (e->op) { case YASM_EXPR_ADD: strcpy(opstr, "+"); break; case YASM_EXPR_SUB: strcpy(opstr, "-"); break; case YASM_EXPR_MUL: strcpy(opstr, "*"); break; case YASM_EXPR_DIV: strcpy(opstr, "/"); break; case YASM_EXPR_SIGNDIV: strcpy(opstr, "//"); break; case YASM_EXPR_MOD: strcpy(opstr, "%"); break; case YASM_EXPR_SIGNMOD: strcpy(opstr, "%%"); break; case YASM_EXPR_NEG: fprintf(f, "-"); opstr[0] = 0; break; case YASM_EXPR_NOT: fprintf(f, "~"); opstr[0] = 0; break; case YASM_EXPR_OR: strcpy(opstr, "|"); break; case YASM_EXPR_AND: strcpy(opstr, "&"); break; case YASM_EXPR_XOR: strcpy(opstr, "^"); break; case YASM_EXPR_XNOR: strcpy(opstr, "XNOR"); break; case YASM_EXPR_NOR: strcpy(opstr, "NOR"); break; case YASM_EXPR_SHL: strcpy(opstr, "<<"); break; case YASM_EXPR_SHR: strcpy(opstr, ">>"); break; case YASM_EXPR_LOR: strcpy(opstr, "||"); break; case YASM_EXPR_LAND: strcpy(opstr, "&&"); break; case YASM_EXPR_LNOT: strcpy(opstr, "!"); break; case YASM_EXPR_LXOR: strcpy(opstr, "^^"); break; case YASM_EXPR_LXNOR: strcpy(opstr, "LXNOR"); break; case YASM_EXPR_LNOR: strcpy(opstr, "LNOR"); break; case YASM_EXPR_LT: strcpy(opstr, "<"); break; case YASM_EXPR_GT: strcpy(opstr, ">"); break; case YASM_EXPR_LE: strcpy(opstr, "<="); break; case YASM_EXPR_GE: strcpy(opstr, ">="); break; case YASM_EXPR_NE: strcpy(opstr, "!="); break; case YASM_EXPR_EQ: strcpy(opstr, "=="); break; case YASM_EXPR_SEG: fprintf(f, "SEG "); opstr[0] = 0; break; case YASM_EXPR_WRT: strcpy(opstr, " WRT "); break; case YASM_EXPR_SEGOFF: strcpy(opstr, ":"); break; case YASM_EXPR_IDENT: opstr[0] = 0; break; default: strcpy(opstr, " !UNK! "); break; } for (i=0; inumterms; i++) { switch (e->terms[i].type) { case YASM_EXPR_PRECBC: fprintf(f, "{%lx}", yasm_bc_next_offset(e->terms[i].data.precbc)); break; case YASM_EXPR_SYM: fprintf(f, "%s", yasm_symrec_get_name(e->terms[i].data.sym)); break; case YASM_EXPR_EXPR: fprintf(f, "("); yasm_expr_print(e->terms[i].data.expn, f); fprintf(f, ")"); break; case YASM_EXPR_INT: yasm_intnum_print(e->terms[i].data.intn, f); break; case YASM_EXPR_FLOAT: yasm_floatnum_print(e->terms[i].data.flt, f); break; case YASM_EXPR_REG: /* FIXME */ /*yasm_arch_reg_print(arch, e->terms[i].data.reg, f);*/ break; case YASM_EXPR_SUBST: fprintf(f, "[%u]", e->terms[i].data.subst); break; case YASM_EXPR_NONE: break; } if (i < e->numterms-1) fprintf(f, "%s", opstr); } } unsigned int yasm_expr_size(const yasm_expr *e) { int i; int seen = 0; unsigned int size = 0, newsize; if (e->op == YASM_EXPR_IDENT) { if (e->terms[0].type == YASM_EXPR_SYM) return yasm_symrec_get_size(e->terms[0].data.sym); return 0; } if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_SUB) return 0; for (i=0; inumterms; i++) { newsize = 0; switch (e->terms[i].type) { case YASM_EXPR_EXPR: newsize = yasm_expr_size(e->terms[i].data.expn); break; case YASM_EXPR_SYM: newsize = yasm_symrec_get_size(e->terms[i].data.sym); break; default: break; } if (newsize) { size = newsize; if (seen) /* either sum of idents (?!) or substract of idents */ return 0; seen = 1; } } /* exactly one offset */ return size; } const char * yasm_expr_segment(const yasm_expr *e) { int i; int seen = 0; const char *segment = NULL; if (e->op == YASM_EXPR_IDENT) { if (e->terms[0].type == YASM_EXPR_SYM) return yasm_symrec_get_segment(e->terms[0].data.sym); return NULL; } if (e->op != YASM_EXPR_ADD && e->op != YASM_EXPR_SUB) return NULL; for (i=0; inumterms; i++) { if ((e->op == YASM_EXPR_ADD || !i) && e->terms[i].type == YASM_EXPR_EXPR) { if ((segment = yasm_expr_segment(e->terms[i].data.expn))) { if (seen) { /* either sum of idents (?!) or substract of idents */ return NULL; } seen = 1; } } } /* exactly one offset */ return segment; }