static void elf_x86_amd64_write_secthead_rel(unsigned char *bufp, elf_secthead *shead, elf_section_index symtab_idx, elf_section_index sindex) { yasm_intnum *nreloc; yasm_intnum *relocsize; YASM_WRITE_32_L(bufp, shead->rel_name ? shead->rel_name->index : 0); YASM_WRITE_32_L(bufp, SHT_RELA); YASM_WRITE_64Z_L(bufp, 0); YASM_WRITE_64Z_L(bufp, 0); YASM_WRITE_64Z_L(bufp, shead->rel_offset); nreloc = yasm_intnum_create_uint(shead->nreloc); relocsize = yasm_intnum_create_uint(RELOC64A_SIZE); yasm_intnum_calc(relocsize, YASM_EXPR_MUL, nreloc); YASM_WRITE_64I_L(bufp, relocsize); /* size */ yasm_intnum_destroy(nreloc); yasm_intnum_destroy(relocsize); YASM_WRITE_32_L(bufp, symtab_idx); /* link: symtab index */ YASM_WRITE_32_L(bufp, shead->index); /* info: relocated's index */ YASM_WRITE_64Z_L(bufp, RELOC64_ALIGN); /* align */ YASM_WRITE_64Z_L(bufp, RELOC64A_SIZE); /* entity size */ }
static void elf_x86_x86_handle_reloc_addend(yasm_intnum *intn, elf_reloc_entry *reloc, unsigned long offset) { if (!reloc->wrt && reloc->is_GOT_sym && reloc->valsize == 32 && offset != 0) { yasm_intnum *off_intn = yasm_intnum_create_uint(offset); yasm_intnum_calc(intn, YASM_EXPR_ADD, off_intn); yasm_intnum_destroy(off_intn); } return; /* .rel: Leave addend in intn */ }
static int run_output_test(Test_Entry *test) { char *valstr = yasm__xstrdup(test->input); yasm_intnum *intn = yasm_intnum_create_hex(valstr); unsigned long size, i; unsigned char out[100]; int bad; yasm_xfree(valstr); if (test->negate) yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); size = yasm_intnum_size_leb128(intn, test->sign); if (size != test->outsize) { yasm_intnum_destroy(intn); sprintf(failmsg, "%ssigned %s%s size() bad size: expected %lu, got %lu!", test->sign?"":"un", test->negate?"-":"", test->input, test->outsize, size); return 1; } for (i=0; i<sizeof(out); i++) out[i] = 0xFF; size = yasm_intnum_get_leb128(intn, out, test->sign); if (size != test->outsize) { yasm_intnum_destroy(intn); sprintf(failmsg, "%ssigned %s%s get() bad size: expected %lu, got %lu!", test->sign?"":"un", test->negate?"-":"", test->input, test->outsize, size); return 1; } bad = 0; for (i=0; i<test->outsize && !bad; i++) { if (out[i] != test->result[i]) bad = 1; } if (bad) { yasm_intnum_destroy(intn); sprintf(failmsg, "%ssigned %s%s get() bad output!", test->sign?"":"un", test->negate?"-":"", test->input); return 1; } yasm_intnum_destroy(intn); return 0; }
static int run_input_test(Test_Entry *test) { char *valstr = yasm__xstrdup(test->input); yasm_intnum *intn = yasm_intnum_create_hex(valstr); yasm_intnum *testn; unsigned long size; yasm_xfree(valstr); if (test->negate) yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); testn = yasm_intnum_create_leb128(test->result, test->sign, &size); if (size != test->outsize) { yasm_intnum_destroy(testn); yasm_intnum_destroy(intn); sprintf(failmsg, "%ssigned %s%s create() bad size: expected %lu, got %lu!", test->sign?"":"un", test->negate?"-":"", test->input, test->outsize, size); return 1; } yasm_intnum_calc(intn, YASM_EXPR_EQ, testn); if (!yasm_intnum_is_pos1(intn)) { yasm_intnum_destroy(testn); yasm_intnum_destroy(intn); sprintf(failmsg, "%ssigned %s%s create() bad output!", test->sign?"":"un", test->negate?"-":"", test->input); return 1; } yasm_intnum_destroy(testn); yasm_intnum_destroy(intn); return 0; }
/*@null@*/ yasm_intnum * yasm_calc_bc_dist(yasm_bytecode *precbc1, yasm_bytecode *precbc2) { unsigned long dist2, dist1; yasm_intnum *intn; if (precbc1->section != precbc2->section) return NULL; dist1 = yasm_bc_next_offset(precbc1); dist2 = yasm_bc_next_offset(precbc2); if (dist2 < dist1) { intn = yasm_intnum_create_uint(dist1 - dist2); yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); return intn; } dist2 -= dist1; return yasm_intnum_create_uint(dist2); }
int yasm_value_output_basic(yasm_value *value, /*@out@*/ unsigned char *buf, size_t destsize, yasm_bytecode *bc, int warn, yasm_arch *arch) { /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; /*@only@*/ yasm_intnum *outval; int sym_local; int retval = 1; unsigned int valsize = value->size; if (value->no_warn) warn = 0; if (value->abs) { /* Handle floating point expressions */ if (!value->rel && value->abs->op == YASM_EXPR_IDENT && value->abs->terms[0].type == YASM_EXPR_FLOAT) { if (yasm_arch_floatnum_tobytes(arch, value->abs->terms[0].data.flt, buf, destsize, valsize, 0, warn)) return -1; else return 1; } /* Check for complex float expressions */ if (yasm_expr__contains(value->abs, YASM_EXPR_FLOAT)) { yasm_error_set(YASM_ERROR_FLOATING_POINT, N_("floating point expression too complex")); return -1; } /* Handle normal integer expressions */ intn = yasm_expr_get_intnum(&value->abs, 1); if (!intn) { /* Second try before erroring: yasm_expr_get_intnum doesn't handle * SEG:OFF, so try simplifying out any to just the OFF portion, * then getting the intnum again. */ yasm_expr *seg = yasm_expr_extract_deep_segoff(&value->abs); if (seg) yasm_expr_destroy(seg); intn = yasm_expr_get_intnum(&value->abs, 1); } if (!intn) { /* Still don't have an integer! */ yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("expression too complex")); return -1; } } /* Adjust warn for signed/unsigned integer warnings */ if (warn != 0) warn = value->sign ? -1 : 1; if (value->rel) { /* If relative portion is not in bc section, don't try to handle it * here. Otherwise get the relative portion's offset. */ /*@dependent@*/ yasm_bytecode *rel_prevbc; unsigned long dist; sym_local = yasm_symrec_get_label(value->rel, &rel_prevbc); if (value->wrt || value->seg_of || value->section_rel || !sym_local) return 0; /* we can't handle SEG, WRT, or external symbols */ if (rel_prevbc->section != bc->section) return 0; /* not in this section */ if (!value->curpos_rel) return 0; /* not PC-relative */ /* Calculate value relative to current assembly position */ dist = yasm_bc_next_offset(rel_prevbc); if (dist < bc->offset) { outval = yasm_intnum_create_uint(bc->offset - dist); yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL); } else { dist -= bc->offset; outval = yasm_intnum_create_uint(dist); } if (value->rshift > 0) { /*@only@*/ yasm_intnum *shamt = yasm_intnum_create_uint((unsigned long)value->rshift); yasm_intnum_calc(outval, YASM_EXPR_SHR, shamt); yasm_intnum_destroy(shamt); } /* Add in absolute portion */ if (intn) yasm_intnum_calc(outval, YASM_EXPR_ADD, intn); /* Output! */ if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, 0, bc, warn)) retval = -1; yasm_intnum_destroy(outval); return retval; } if (value->seg_of || value->rshift || value->curpos_rel || value->ip_rel || value->section_rel) return 0; /* We can't handle this with just an absolute */ if (intn) { /* Output just absolute portion */ if (yasm_arch_intnum_tobytes(arch, intn, buf, destsize, valsize, 0, bc, warn)) retval = -1; } else { /* No absolute or relative portions: output 0 */ outval = yasm_intnum_create_uint(0); if (yasm_arch_intnum_tobytes(arch, outval, buf, destsize, valsize, 0, bc, warn)) retval = -1; yasm_intnum_destroy(outval); } return retval; }
yasm_intnum * yasm_value_get_intnum(yasm_value *value, yasm_bytecode *bc, int calc_bc_dist) { /*@dependent@*/ /*@null@*/ yasm_intnum *intn = NULL; /*@only@*/ yasm_intnum *outval; int sym_local; if (value->abs) { /* Handle integer expressions, if non-integer or too complex, return * NULL. */ intn = yasm_expr_get_intnum(&value->abs, calc_bc_dist); if (!intn) return NULL; } if (value->rel) { /* If relative portion is not in bc section, return NULL. * Otherwise get the relative portion's offset. */ /*@dependent@*/ yasm_bytecode *rel_prevbc; unsigned long dist; if (!bc) return NULL; /* Can't calculate relative value */ sym_local = yasm_symrec_get_label(value->rel, &rel_prevbc); if (value->wrt || value->seg_of || value->section_rel || !sym_local) return NULL; /* we can't handle SEG, WRT, or external symbols */ if (rel_prevbc->section != bc->section) return NULL; /* not in this section */ if (!value->curpos_rel) return NULL; /* not PC-relative */ /* Calculate value relative to current assembly position */ dist = yasm_bc_next_offset(rel_prevbc); if (dist < bc->offset) { outval = yasm_intnum_create_uint(bc->offset - dist); yasm_intnum_calc(outval, YASM_EXPR_NEG, NULL); } else { dist -= bc->offset; outval = yasm_intnum_create_uint(dist); } if (value->rshift > 0) { /*@only@*/ yasm_intnum *shamt = yasm_intnum_create_uint((unsigned long)value->rshift); yasm_intnum_calc(outval, YASM_EXPR_SHR, shamt); yasm_intnum_destroy(shamt); } /* Add in absolute portion */ if (intn) yasm_intnum_calc(outval, YASM_EXPR_ADD, intn); return outval; } if (intn) return yasm_intnum_copy(intn); /* No absolute or relative portions: output 0 */ return yasm_intnum_create_uint(0); }
int yasm_value_finalize(yasm_value *value, yasm_bytecode *precbc) { if (!value->abs) return 0; value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, 0, NULL, NULL); /* quit early if there was an issue in simplify() */ if (yasm_error_occurred()) return 1; /* Strip top-level AND masking to an all-1s mask the same size * of the value size. This allows forced avoidance of overflow warnings. */ if (value->abs->op == YASM_EXPR_AND) { int term; /* Calculate 1<<size - 1 value */ yasm_intnum *mask = yasm_intnum_create_uint(1); yasm_intnum *mask_tmp = yasm_intnum_create_uint(value->size); yasm_intnum_calc(mask, YASM_EXPR_SHL, mask_tmp); yasm_intnum_set_uint(mask_tmp, 1); yasm_intnum_calc(mask, YASM_EXPR_SUB, mask_tmp); yasm_intnum_destroy(mask_tmp); /* Walk terms and delete matching masks */ for (term=value->abs->numterms-1; term>=0; term--) { if (value->abs->terms[term].type == YASM_EXPR_INT && yasm_intnum_compare(value->abs->terms[term].data.intn, mask) == 0) { /* Delete the intnum */ yasm_intnum_destroy(value->abs->terms[term].data.intn); /* Slide everything to its right over by 1 */ if (term != value->abs->numterms-1) /* if it wasn't last.. */ memmove(&value->abs->terms[term], &value->abs->terms[term+1], (value->abs->numterms-1-term)* sizeof(yasm_expr__item)); /* Update numterms */ value->abs->numterms--; /* Indicate warnings have been disabled */ value->no_warn = 1; } } if (value->abs->numterms == 1) value->abs->op = YASM_EXPR_IDENT; yasm_intnum_destroy(mask); } /* Handle trivial (IDENT) cases immediately */ if (value->abs->op == YASM_EXPR_IDENT) { switch (value->abs->terms[0].type) { case YASM_EXPR_INT: if (yasm_intnum_is_zero(value->abs->terms[0].data.intn)) { yasm_expr_destroy(value->abs); value->abs = NULL; } return 0; case YASM_EXPR_REG: case YASM_EXPR_FLOAT: return 0; case YASM_EXPR_SYM: value->rel = value->abs->terms[0].data.sym; yasm_expr_destroy(value->abs); value->abs = NULL; return 0; case YASM_EXPR_EXPR: /* Bring up lower values. */ while (value->abs->op == YASM_EXPR_IDENT && value->abs->terms[0].type == YASM_EXPR_EXPR) { yasm_expr *sube = value->abs->terms[0].data.expn; yasm_xfree(value->abs); value->abs = sube; } break; default: yasm_internal_error(N_("unexpected expr term type")); } } if (value_finalize_scan(value, value->abs, precbc, 0)) return 1; value->abs = yasm_expr__level_tree(value->abs, 1, 1, 0, 0, NULL, NULL); /* Simplify 0 in abs to NULL */ if (value->abs->op == YASM_EXPR_IDENT && value->abs->terms[0].type == YASM_EXPR_INT && yasm_intnum_is_zero(value->abs->terms[0].data.intn)) { yasm_expr_destroy(value->abs); value->abs = NULL; } return 0; }
static int xdf_objfmt_output_value(yasm_value *value, unsigned char *buf, unsigned int destsize, unsigned long offset, yasm_bytecode *bc, int warn, /*@null@*/ void *d) { /*@null@*/ xdf_objfmt_output_info *info = (xdf_objfmt_output_info *)d; yasm_objfmt_xdf *objfmt_xdf; /*@dependent@*/ /*@null@*/ yasm_intnum *intn; unsigned long intn_minus; int retval; unsigned int valsize = value->size; assert(info != NULL); objfmt_xdf = info->objfmt_xdf; if (value->abs) value->abs = yasm_expr_simplify(value->abs, 1); /* Try to output constant and PC-relative section-local first. * Note this does NOT output any value with a SEG, WRT, external, * cross-section, or non-PC-relative reference (those are handled below). */ switch (yasm_value_output_basic(value, buf, destsize, bc, warn, info->object->arch)) { case -1: return 1; case 0: break; default: return 0; } if (value->section_rel) { yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("xdf: relocation too complex")); return 1; } intn_minus = 0; if (value->rel) { xdf_reloc *reloc; reloc = yasm_xmalloc(sizeof(xdf_reloc)); reloc->reloc.addr = yasm_intnum_create_uint(bc->offset + offset); reloc->reloc.sym = value->rel; reloc->base = NULL; reloc->size = valsize/8; reloc->shift = value->rshift; if (value->seg_of) reloc->type = XDF_RELOC_SEG; else if (value->wrt) { reloc->base = value->wrt; reloc->type = XDF_RELOC_WRT; } else if (value->curpos_rel) { reloc->type = XDF_RELOC_RIP; /* Adjust to start of section, so subtract out the bytecode * offset. */ intn_minus = bc->offset; } else reloc->type = XDF_RELOC_REL; info->xsd->nreloc++; yasm_section_add_reloc(info->sect, (yasm_reloc *)reloc, yasm_xfree); } if (intn_minus > 0) { intn = yasm_intnum_create_uint(intn_minus); yasm_intnum_calc(intn, YASM_EXPR_NEG, NULL); } else intn = yasm_intnum_create_uint(0); if (value->abs) { yasm_intnum *intn2 = yasm_expr_get_intnum(&value->abs, 0); if (!intn2) { yasm_error_set(YASM_ERROR_TOO_COMPLEX, N_("xdf: relocation too complex")); yasm_intnum_destroy(intn); return 1; } yasm_intnum_calc(intn, YASM_EXPR_ADD, intn2); } retval = yasm_arch_intnum_tobytes(info->object->arch, intn, buf, destsize, valsize, 0, bc, warn); yasm_intnum_destroy(intn); return retval; }
/*@-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; i<e->numterms; 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; j<e->terms[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; i<e->numterms; 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; }
/* 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; i<e->numterms; 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; }
/* 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; i<e->numterms; 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; }