return e;
}
-/* Transforms instances of symrec-symrec [symrec+(-1*symrec)] into integers if
- * possible. Uses a simple n^2 algorithm because n is usually quite small.
+/* 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.
*/
static /*@only@*/ yasm_expr *
-expr_xform_bc_dist(/*@returned@*/ /*@only@*/ yasm_expr *e)
+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;
yasm_symrec_get_label(e->terms[j].data.sym, &precbc2) &&
(sect = yasm_bc_get_section(precbc2)) &&
sect == sect2 &&
- (dist = yasm_calc_bc_dist(precbc, precbc2))) {
- /* Change the symrec term to an integer */
- e->terms[j].type = YASM_EXPR_INT;
- e->terms[j].data.intn = dist;
+ callback(&e->terms[j], precbc, precbc2, cbd)) {
/* Delete the matching (-1*symrec) term */
yasm_expr_destroy(sube);
e->terms[i].type = YASM_EXPR_NONE;
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;
+}
+
+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 = expr_xform_bc_dist_base(*ep, &my_cbd, expr_bc_dist_subst_cb);
+ 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)
}
}
+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:
+ case YASM_EXPR_SYMEXP:
+ /* Symbols don't need to be copied */
+ dest->data.sym = src->data.sym;
+ 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)
n->line = e->line;
n->numterms = e->numterms;
for (i=0; i<e->numterms; i++) {
- yasm_expr__item *dest = &n->terms[i];
- const yasm_expr__item *src = &e->terms[i];
-
- if (i != except) {
- dest->type = src->type;
- switch (src->type) {
- case YASM_EXPR_SYM:
- case YASM_EXPR_SYMEXP:
- /* Symbols don't need to be copied */
- dest->data.sym = src->data.sym;
- 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;
- default:
- break;
- }
- }
+ if (i != except)
+ expr_item_copy(&n->terms[i], &e->terms[i]);
}
return n;
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.
/* 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;
}
#include "section.h"
#include "objfmt.h"
+#include "expr-int.h"
#include "bc-int.h"
* 3. Final pass over bytecodes to generate final offsets.
*/
-typedef struct yasm_span {
+typedef struct yasm_span yasm_span;
+
+typedef struct yasm_span_term {
+ yasm_bytecode *precbc, *precbc2;
+ yasm_span *span; /* span this term is a member of */
+ long cur_val, new_val;
+ unsigned int subst;
+} yasm_span_term;
+
+struct yasm_span {
/*@reldef@*/ STAILQ_ENTRY(yasm_span) link; /* for allocation tracking */
/*@reldef@*/ STAILQ_ENTRY(yasm_span) linkq; /* for Q */
yasm_value depval;
+ /* span term for relative portion of value */
+ yasm_span_term *rel_term;
+ /* span terms in absolute portion of value */
+ yasm_span_term *terms;
+ yasm_expr__item *items;
+ unsigned int num_terms;
+
/* Special handling: see descriptions above */
enum {
NOT_SPECIAL = 0,
int id;
int active;
-} yasm_span;
+};
typedef struct optimize_data {
- /*@reldef@*/ STAILQ_HEAD(yasm_spanhead, yasm_span) spans;
+ /*@reldef@*/ STAILQ_HEAD(, yasm_span) spans;
+ /*@reldef@*/ STAILQ_HEAD(, yasm_span) Q;
} optimize_data;
static void
span->bc = bc;
yasm_value_init_copy(&span->depval, value);
+ span->rel_term = NULL;
+ span->terms = NULL;
+ span->items = NULL;
+ span->num_terms = 0;
span->special = NOT_SPECIAL;
span->cur_val = 0;
span->new_val = 0;
STAILQ_INSERT_TAIL(&optd->spans, span, link);
}
-/* Recalculate span value based on current bytecode offsets.
+static void
+add_span_term(unsigned int subst, yasm_bytecode *precbc,
+ yasm_bytecode *precbc2, void *d)
+{
+ yasm_span *span = d;
+ yasm_intnum *intn;
+
+ if (subst >= span->num_terms) {
+ /* Linear expansion since total number is essentially always small */
+ span->num_terms = subst+1;
+ span->terms = yasm_xrealloc(span->terms,
+ span->num_terms*sizeof(yasm_span_term));
+ }
+ span->terms[subst].precbc = precbc;
+ span->terms[subst].precbc2 = precbc2;
+ span->terms[subst].span = span;
+ span->terms[subst].subst = subst;
+
+ intn = yasm_calc_bc_dist(precbc, precbc2);
+ if (!intn)
+ yasm_internal_error(N_("could not calculate bc distance"));
+ span->terms[subst].cur_val = 0;
+ span->terms[subst].new_val = yasm_intnum_get_int(intn);
+ yasm_intnum_destroy(intn);
+}
+
+static void
+span_create_terms(yasm_span *span)
+{
+ unsigned int i;
+ yasm_intnum *intn;
+
+ /* Split out sym-sym terms in absolute portion of dependent value */
+ if (span->depval.abs) {
+ span->num_terms = yasm_expr__bc_dist_subst(&span->depval.abs, span,
+ add_span_term);
+ if (span->num_terms > 0) {
+ span->items = yasm_xmalloc(span->num_terms*sizeof(yasm_expr__item));
+ for (i=0; i<span->num_terms; i++) {
+ /* Create items with dummy value */
+ span->items[i].type = YASM_EXPR_INT;
+ span->items[i].data.intn = yasm_intnum_create_int(0);
+ }
+ }
+ }
+
+ /* Create term for relative portion of dependent value */
+ if (span->depval.rel) {
+ yasm_bytecode *rel_precbc;
+ int sym_local;
+
+ sym_local = yasm_symrec_get_label(span->depval.rel, &rel_precbc);
+ if (span->depval.wrt || span->depval.seg_of || span->depval.section_rel
+ || !sym_local)
+ return; /* we can't handle SEG, WRT, or external symbols */
+ if (rel_precbc->section != span->bc->section)
+ return; /* not in this section */
+ if (!span->depval.curpos_rel)
+ return; /* not PC-relative */
+
+ span->rel_term = yasm_xmalloc(sizeof(yasm_span_term));
+ span->rel_term->precbc = rel_precbc;
+ span->rel_term->precbc2 = NULL;
+ span->rel_term->span = span;
+ span->rel_term->subst = ~0U;
+
+ span->rel_term->cur_val = 0;
+ span->rel_term->new_val =
+ rel_precbc->offset + rel_precbc->len - span->bc->offset;
+ }
+}
+
+/* Recalculate span value based on current span replacement values.
* Returns 1 if span exceeded thresholds.
*/
static int
recalc_normal_span(yasm_span *span)
{
- yasm_value val;
- /*@null@*/ /*@only@*/ yasm_intnum *num;
-
- yasm_value_init_copy(&val, &span->depval);
- num = yasm_value_get_intnum(&val, span->bc, 1);
- if (num) {
- span->new_val = yasm_intnum_get_int(num);
- yasm_intnum_destroy(num);
- } else {
- /* external or too complex; force to longest form */
- span->new_val = LONG_MAX;
- span->active = 0;
+ span->new_val = 0;
+
+ if (span->depval.abs) {
+ yasm_expr *abs_copy = yasm_expr_copy(span->depval.abs);
+ /*@null@*/ /*@dependent@*/ yasm_intnum *num;
+
+ /* Update sym-sym terms and substitute back into expr */
+ unsigned int i;
+ for (i=0; i<span->num_terms; i++)
+ yasm_intnum_set_int(span->items[i].data.intn,
+ span->terms[i].new_val);
+ yasm_expr__subst(abs_copy, span->num_terms, span->items);
+ num = yasm_expr_get_intnum(&abs_copy, 0);
+ if (num)
+ span->new_val = yasm_intnum_get_int(num);
+ else
+ span->new_val = LONG_MAX; /* too complex; force to longest form */
+ yasm_expr_destroy(abs_copy);
+ }
+
+ if (span->depval.rel && span->new_val != LONG_MAX) {
+ if (span->rel_term) {
+ span->new_val += span->rel_term->new_val >> span->depval.rshift;
+ } else
+ span->new_val = LONG_MAX; /* too complex; force to longest form */
}
- yasm_value_delete(&val);
+
+ if (span->new_val == LONG_MAX)
+ span->active = 0;
+
return (span->new_val < span->neg_thres
|| span->new_val > span->pos_thres);
}
yasm_span *span;
long neg_thres, pos_thres;
int retval;
- /*@reldef@*/ STAILQ_HEAD(yasm_spanhead, yasm_span) Q;
+ unsigned int i;
STAILQ_INIT(&optd.spans);
span->bc = bc;
yasm_value_initialize(&span->depval, NULL, 0);
+ span->rel_term = NULL;
+ span->terms = NULL;
+ span->items = NULL;
+ span->num_terms = 0;
span->special = SPECIAL_BC_OFFSET;
span->cur_val = (long)bc->offset;
span->new_val = 0;
STAILQ_FOREACH(span, &optd.spans, link) {
if (!span->active)
continue;
+ span_create_terms(span);
switch (span->special) {
case NOT_SPECIAL:
if (recalc_normal_span(span)) {
return;
/* Step 1d */
- STAILQ_INIT(&Q);
+ STAILQ_INIT(&optd.Q);
STAILQ_FOREACH(span, &optd.spans, link) {
+ yasm_intnum *intn;
+
if (!span->active)
continue;
+
+ /* Update span terms based on new bc offsets */
+ for (i=0; i<span->num_terms; i++) {
+ intn = yasm_calc_bc_dist(span->terms[i].precbc,
+ span->terms[i].precbc2);
+ if (!intn)
+ yasm_internal_error(N_("could not calculate bc distance"));
+ span->terms[i].cur_val = span->terms[i].new_val;
+ span->terms[i].new_val = yasm_intnum_get_int(intn);
+ yasm_intnum_destroy(intn);
+ }
+ if (span->rel_term) {
+ span->rel_term->cur_val = span->rel_term->new_val;
+ span->rel_term->new_val = span->rel_term->precbc->offset
+ + span->rel_term->precbc->len - span->bc->offset;
+ }
+
switch (span->special) {
case NOT_SPECIAL:
/* Add to interval tree */
if (recalc_normal_span(span)) {
/* Exceeded threshold, add span to Q */
- STAILQ_INSERT_TAIL(&Q, span, linkq);
+ STAILQ_INSERT_TAIL(&optd.Q, span, linkq);
}
break;
case SPECIAL_BC_OFFSET:
/* Add to interval tree */
/* It's impossible to exceed any threshold here (as we just
- * adjusted this when updating the BC offsets, so just update
+ * adjusted this when updating the BC offsets), so just update
* span values and thresholds.
*/
span->new_val = (long)span->bc->offset;
}
/* Step 2 */
- while (!STAILQ_EMPTY(&Q)) {
+ while (!STAILQ_EMPTY(&optd.Q)) {
yasm_internal_error(N_("second optimization phase not implemented"));
+ span = STAILQ_FIRST(&optd.Q);
+ STAILQ_REMOVE_HEAD(&optd.Q, linkq);
+ retval = yasm_bc_expand(span->bc, span->id, span->cur_val,
+ span->new_val, &neg_thres, &pos_thres);
+ yasm_errwarn_propagate(errwarns, span->bc->line);
+ if (retval < 0)
+ saw_error = 1;
+ else if (retval > 0) {
+ span->neg_thres = neg_thres;
+ span->pos_thres = pos_thres;
+ } else
+ span->active = 0;
+
}
+ if (saw_error)
+ return;
+
/* Step 3 */
update_all_bc_offsets(object, errwarns);
}