]> granicus.if.org Git - yasm/commitdiff
Move towards building interval tree by splitting dependent value into
authorPeter Johnson <peter@tortall.net>
Mon, 12 Jun 2006 07:53:49 +0000 (07:53 -0000)
committerPeter Johnson <peter@tortall.net>
Mon, 12 Jun 2006 07:53:49 +0000 (07:53 -0000)
sym-sym terms.  Starting to get some code duplication that should get
refactored somehow, but the first priority is getting it working.

svn path=/branches/new-optimizer/; revision=1575

libyasm/expr-int.h
libyasm/expr.c
libyasm/section.c

index 456da9cabbed9206ca6e3dc1b7de9fd8cd93dcde..0a1bc5a9bd63248b19eb6f7d0d26f2ac96ebe715 100644 (file)
@@ -32,10 +32,11 @@ typedef enum {
     YASM_EXPR_NONE = 0,
     YASM_EXPR_REG = 1<<0,
     YASM_EXPR_INT = 1<<1,
-    YASM_EXPR_FLOAT = 1<<2,
-    YASM_EXPR_SYM = 1<<3,
-    YASM_EXPR_SYMEXP = 1<<4, /* post-expanded sym (due to absolute expansion) */
-    YASM_EXPR_EXPR = 1<<5
+    YASM_EXPR_SUBST = 1<<2,
+    YASM_EXPR_FLOAT = 1<<3,
+    YASM_EXPR_SYM = 1<<4,
+    YASM_EXPR_SYMEXP = 1<<5, /* post-expanded sym (due to absolute expansion) */
+    YASM_EXPR_EXPR = 1<<6
 } yasm_expr__type;
 
 struct yasm_expr__item {
@@ -46,6 +47,7 @@ struct yasm_expr__item {
        yasm_intnum *intn;
        yasm_floatnum *flt;
        unsigned long reg;
+       unsigned int subst;
     } data;
 };
 
@@ -85,4 +87,28 @@ yasm_expr *yasm_expr__copy_except(const yasm_expr *e, int except);
 
 int yasm_expr__contains(const yasm_expr *e, yasm_expr__type t);
 
+/** Transform symrec-symrec terms in expression into YASM_EXPR_SUBST items.
+ * Calls the callback function for each symrec-symrec term.
+ * \param ep           expression (pointer to)
+ * \param cbd          callback data passed to callback function
+ * \param callback     callback function: given subst index for bytecode
+ *                     pair, bytecode pair (bc2-bc1), and cbd (callback data)
+ * \return Number of transformations made.
+ */
+int yasm_expr__bc_dist_subst(yasm_expr **ep, void *cbd,
+                            void (*callback) (unsigned int subst,
+                                              yasm_bytecode *precbc,
+                                              yasm_bytecode *precbc2,
+                                              void *cbd));
+
+/** Substitute items into expr YASM_EXPR_SUBST items (by index).  Items are
+ * copied, so caller is responsible for freeing array of items.
+ * \param e            expression
+ * \param num_items    number of items in items array
+ * \param items                items array
+ * \return 1 on error (index out of range).
+ */
+int yasm_expr__subst(yasm_expr *e, unsigned int num_items,
+                    const yasm_expr__item *items);
+
 #endif
index 95522b372993475e6cd6b668c1abef293daebf38..5332f6b7ce1fb7fc4c0d18a6858728f7ab11ee1c 100644 (file)
@@ -183,11 +183,17 @@ yasm_expr_reg(unsigned long reg)
     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;
@@ -243,10 +249,7 @@ expr_xform_bc_dist(/*@returned@*/ /*@only@*/ yasm_expr *e)
                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;
@@ -272,6 +275,65 @@ expr_xform_bc_dist(/*@returned@*/ /*@only@*/ yasm_expr *e)
     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)
@@ -892,6 +954,36 @@ yasm_expr__order_terms(yasm_expr *e)
     }
 }
 
+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)
@@ -906,34 +998,8 @@ 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;
@@ -992,6 +1058,33 @@ 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.
@@ -1308,6 +1401,9 @@ yasm_expr_print(const yasm_expr *e, FILE *f)
                /* 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;
        }
index e55472844b3d111ae770a7b13690a9f11bba9113..c8bedbfdc6ce9ead54b64212ba3a4e248614ad89 100644 (file)
@@ -46,6 +46,7 @@
 #include "section.h"
 #include "objfmt.h"
 
+#include "expr-int.h"
 #include "bc-int.h"
 
 
@@ -697,7 +698,16 @@ yasm_section_print(const yasm_section *sect, FILE *f, int indent_level,
  * 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 */
 
@@ -705,6 +715,13 @@ typedef struct yasm_span {
 
     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,
@@ -721,10 +738,11 @@ typedef struct yasm_span {
     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
@@ -736,6 +754,10 @@ optimize_add_span(void *add_span_data, yasm_bytecode *bc, int id,
 
     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;
@@ -747,26 +769,114 @@ optimize_add_span(void *add_span_data, yasm_bytecode *bc, int id,
     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);
 }
@@ -823,7 +933,7 @@ yasm_object_optimize(yasm_object *object, yasm_arch *arch,
     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);
 
@@ -855,6 +965,10 @@ yasm_object_optimize(yasm_object *object, yasm_arch *arch,
 
                    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;
@@ -890,6 +1004,7 @@ yasm_object_optimize(yasm_object *object, yasm_arch *arch,
     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)) {
@@ -930,24 +1045,43 @@ yasm_object_optimize(yasm_object *object, yasm_arch *arch,
        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;
@@ -960,10 +1094,26 @@ yasm_object_optimize(yasm_object *object, yasm_arch *arch,
     }
 
     /* 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);
 }