/* 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)); }
static int value_finalize_scan(yasm_value *value, yasm_expr *e, /*@null@*/ yasm_bytecode *expr_precbc, int ssym_not_ok) { int i; /*@dependent@*/ yasm_section *sect; /*@dependent@*/ /*@null@*/ yasm_bytecode *precbc; unsigned long shamt; /* for SHR */ /* Yes, this has a maximum upper bound on 32 terms, based on an * "insane number of terms" (and ease of implementation) WAG. * The right way to do this would be a stack-based alloca, but that's * not ISO C. We really don't want to malloc here as this function is * hit a lot! * * This is a bitmask to keep things small, as this is a recursive * routine and we don't want to eat up stack space. */ unsigned long used; /* for ADD */ /* Thanks to this running after a simplify, we don't need to iterate * down through IDENTs or handle SUB. * * We scan for a single symrec, gathering info along the way. After * we've found the symrec, we keep scanning but error if we find * another one. We pull out the single symrec and any legal operations * performed on it. * * Also, if we find a float anywhere, we don't allow mixing of a single * symrec with it. */ switch (e->op) { case YASM_EXPR_ADD: /* Okay for single symrec anywhere in expr. * Check for single symrec anywhere. * Handle symrec-symrec by checking for (-1*symrec) * and symrec term pairs (where both symrecs are in the same * segment). */ if (e->numterms > 32) yasm__fatal(N_("expression on line %d has too many add terms;" " internal limit of 32"), e->line); used = 0; for (i=0; i<e->numterms; i++) { int j; yasm_expr *sube; yasm_intnum *intn; yasm_symrec *sym; /*@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) { /* recurse instead */ if (value_finalize_scan(value, sube, expr_precbc, ssym_not_ok)) return 1; continue; } if (sube->terms[0].type == YASM_EXPR_INT && sube->terms[1].type == YASM_EXPR_SYM) { intn = sube->terms[0].data.intn; sym = sube->terms[1].data.sym; } else if (sube->terms[0].type == YASM_EXPR_SYM && sube->terms[1].type == YASM_EXPR_INT) { sym = sube->terms[0].data.sym; intn = sube->terms[1].data.intn; } else { if (value_finalize_scan(value, sube, expr_precbc, ssym_not_ok)) return 1; continue; } if (!yasm_intnum_is_neg1(intn)) { if (value_finalize_scan(value, sube, expr_precbc, ssym_not_ok)) return 1; continue; } /* Look for the same symrec term; even if both are external, * they should cancel out. */ for (j=0; j<e->numterms; j++) { if (e->terms[j].type == YASM_EXPR_SYM && e->terms[j].data.sym == sym && (used & (1<<j)) == 0) { /* Mark as used */ used |= 1<<j; /* Replace both symrec portions with 0 */ yasm_expr_destroy(sube); e->terms[i].type = YASM_EXPR_INT; e->terms[i].data.intn = yasm_intnum_create_uint(0); e->terms[j].type = YASM_EXPR_INT; e->terms[j].data.intn = yasm_intnum_create_uint(0); break; /* stop looking */ } } if (j != e->numterms) continue; if (!yasm_symrec_get_label(sym, &precbc)) { if (value_finalize_scan(value, sube, expr_precbc, ssym_not_ok)) return 1; continue; } sect2 = yasm_bc_get_section(precbc); /* Now look for a unused symrec term in the same segment */ for (j=0; j<e->numterms; j++) { if (e->terms[j].type == YASM_EXPR_SYM && yasm_symrec_get_label(e->terms[j].data.sym, &precbc2) && (sect = yasm_bc_get_section(precbc2)) && sect == sect2 && (used & (1<<j)) == 0) { /* Mark as used */ used |= 1<<j; break; /* stop looking */ } } /* We didn't match in the same segment. If the * -1*symrec is actually -1*curpos, we can match * unused symrec terms in other segments and generate * a curpos-relative reloc. * * Similarly, handle -1*symrec in other segment via the * following transformation: * other-this = (other-.)+(.-this) * We can only do this transformation if "this" is in * this expr's segment. * * Don't do this if we've already become curpos-relative. * The unmatched symrec will be caught below. */ if (j == e->numterms && !value->curpos_rel && (yasm_symrec_is_curpos(sym) || (expr_precbc && sect2 == yasm_bc_get_section(expr_precbc)))) { for (j=0; j<e->numterms; j++) { if (e->terms[j].type == YASM_EXPR_SYM && !yasm_symrec_get_equ(e->terms[j].data.sym) && !yasm_symrec_is_special(e->terms[j].data.sym) && (used & (1<<j)) == 0) { /* Mark as used */ used |= 1<<j; /* Mark value as curpos-relative */ if (value->rel || ssym_not_ok) return 1; value->rel = e->terms[j].data.sym; value->curpos_rel = 1; if (yasm_symrec_is_curpos(sym)) { /* Replace both symrec portions with 0 */ yasm_expr_destroy(sube); e->terms[i].type = YASM_EXPR_INT; e->terms[i].data.intn = yasm_intnum_create_uint(0); e->terms[j].type = YASM_EXPR_INT; e->terms[j].data.intn = yasm_intnum_create_uint(0); } else { /* Replace positive portion with curpos */ yasm_object *object = yasm_section_get_object(sect2); yasm_symtab *symtab = object->symtab; e->terms[j].data.sym = yasm_symtab_define_curpos (symtab, ".", expr_precbc, e->line); } break; /* stop looking */ } } } if (j == e->numterms) return 1; /* We didn't find a match! */ } /* Look for unmatched symrecs. If we've already found one or * we don't WANT to find one, error out. */ for (i=0; i<e->numterms; i++) { if (e->terms[i].type == YASM_EXPR_SYM && (used & (1<<i)) == 0) { if (value->rel || ssym_not_ok) return 1; value->rel = e->terms[i].data.sym; /* and replace with 0 */ e->terms[i].type = YASM_EXPR_INT; e->terms[i].data.intn = yasm_intnum_create_uint(0); } } break; case YASM_EXPR_SHR: /* Okay for single symrec in LHS and constant on RHS. * Single symrecs are not okay on RHS. * If RHS is non-constant, don't allow single symrec on LHS. * XXX: should rshift be an expr instead?? */ /* Check for single sym on LHS */ if (e->terms[0].type != YASM_EXPR_SYM) break; /* If we already have a sym, we can't take another one */ if (value->rel || ssym_not_ok) return 1; /* RHS must be a positive integer */ if (e->terms[1].type != YASM_EXPR_INT) return 1; /* can't shift sym by non-constant integer */ shamt = yasm_intnum_get_uint(e->terms[1].data.intn); if ((shamt + value->rshift) > YASM_VALUE_RSHIFT_MAX) return 1; /* total shift would be too large */ /* Update value */ value->rshift += shamt; value->rel = e->terms[0].data.sym; /* Replace symbol with 0 */ e->terms[0].type = YASM_EXPR_INT; e->terms[0].data.intn = yasm_intnum_create_uint(0); /* Just leave SHR in place */ break; case YASM_EXPR_SEG: /* Okay for single symrec (can only be done once). * Not okay for anything BUT a single symrec as an immediate * child. */ if (e->terms[0].type != YASM_EXPR_SYM) return 1; if (value->seg_of) return 1; /* multiple SEG not legal */ value->seg_of = 1; if (value->rel || ssym_not_ok) return 1; /* got a relative portion somewhere else? */ value->rel = e->terms[0].data.sym; /* replace with ident'ed 0 */ e->op = YASM_EXPR_IDENT; e->terms[0].type = YASM_EXPR_INT; e->terms[0].data.intn = yasm_intnum_create_uint(0); break; case YASM_EXPR_WRT: /* Okay for single symrec in LHS and either a register or single * symrec (as an immediate child) on RHS. * If a single symrec on RHS, can only be done once. * WRT reg is left in expr for arch to look at. */ /* Handle RHS */ switch (e->terms[1].type) { case YASM_EXPR_SYM: if (value->wrt) return 1; value->wrt = e->terms[1].data.sym; /* and drop the WRT portion */ e->op = YASM_EXPR_IDENT; e->numterms = 1; break; case YASM_EXPR_REG: break; /* ignore */ default: return 1; } /* Handle LHS */ switch (e->terms[0].type) { case YASM_EXPR_SYM: if (value->rel || ssym_not_ok) return 1; value->rel = e->terms[0].data.sym; /* and replace with 0 */ e->terms[0].type = YASM_EXPR_INT; e->terms[0].data.intn = yasm_intnum_create_uint(0); break; case YASM_EXPR_EXPR: /* recurse */ return value_finalize_scan(value, e->terms[0].data.expn, expr_precbc, ssym_not_ok); default: break; /* ignore */ } break; default: /* Single symrec not allowed anywhere */ for (i=0; i<e->numterms; i++) { switch (e->terms[i].type) { case YASM_EXPR_SYM: return 1; case YASM_EXPR_EXPR: /* recurse */ return value_finalize_scan(value, e->terms[i].data.expn, expr_precbc, 1); default: break; } } break; } return 0; }
/* 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; i<e->numterms; 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; j<e->numterms; 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; i<e->numterms; 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; }