be added by a single calc_len.
svn path=/branches/new-optimizer/; revision=1544
void (*destroy) (/*@only@*/ void *contents);
void (*print) (const void *contents, FILE *f, int indent_level);
void (*finalize) (yasm_bytecode *bc, yasm_bytecode *prev_bc);
- int (*calc_len) (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical,
- /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
- void (*set_long) (yasm_bytecode *bc);
+ int (*calc_len) (yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data);
+ int (*expand) (yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
int (*tobytes) (yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
/* number of times bytecode is repeated, NULL=1. */
/*@only@*/ /*@null@*/ yasm_expr *multiple;
- unsigned long len; /* total length of entire bytecode (including
- multiple copies), 0 if unknown */
+ unsigned long len; /* total length of entire bytecode
+ (not including multiple copies) */
/* where it came from */
unsigned long line;
*/
void yasm_bc_finalize_common(yasm_bytecode *bc, yasm_bytecode *prev_bc);
-/** Common bytecode callback set_long function, for where the bytecode is
- * always short (calc_len always returns 0, never 1). Causes an internal
+/** Common bytecode callback expand function, for where the bytecode is
+ * always short (calc_len never calls add_span). Causes an internal
* error if called.
*/
-void yasm_bc_set_long_common(yasm_bytecode *bc);
+int yasm_bc_expand_common
+ (yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
#define yasm_bc__next(x) STAILQ_NEXT(x, link)
static void bc_data_destroy(void *contents);
static void bc_data_print(const void *contents, FILE *f, int indent_level);
-static int bc_data_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_data_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_data_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void bc_leb128_destroy(void *contents);
static void bc_leb128_print(const void *contents, FILE *f, int indent_level);
static void bc_leb128_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc);
-static int bc_leb128_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_leb128_calc_len(yasm_bytecode *bc,
+ yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_leb128_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void bc_reserve_destroy(void *contents);
static void bc_reserve_print(const void *contents, FILE *f, int indent_level);
-static int bc_reserve_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_reserve_calc_len(yasm_bytecode *bc,
+ yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_reserve_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void bc_incbin_destroy(void *contents);
static void bc_incbin_print(const void *contents, FILE *f, int indent_level);
-static int bc_incbin_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_incbin_calc_len(yasm_bytecode *bc,
+ yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_incbin_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void bc_align_destroy(void *contents);
static void bc_align_print(const void *contents, FILE *f, int indent_level);
static void bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc);
-static int bc_align_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_align_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_align_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void bc_org_destroy(void *contents);
static void bc_org_print(const void *contents, FILE *f, int indent_level);
-static int bc_org_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_org_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_org_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void bc_insn_destroy(void *contents);
static void bc_insn_print(const void *contents, FILE *f, int indent_level);
static void bc_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc);
-static int bc_insn_calc_len
- (yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical, /*@out@*/ long *neg_thres,
- /*@out@*/ long *pos_thres);
+static int bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
bc_data_print,
yasm_bc_finalize_common,
bc_data_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_data_tobytes
};
bc_leb128_print,
bc_leb128_finalize,
bc_leb128_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_leb128_tobytes
};
bc_reserve_print,
yasm_bc_finalize_common,
bc_reserve_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_reserve_tobytes
};
bc_incbin_print,
yasm_bc_finalize_common,
bc_incbin_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_incbin_tobytes
};
bc_align_print,
bc_align_finalize,
bc_align_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_align_tobytes
};
bc_org_print,
yasm_bc_finalize_common,
bc_org_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_org_tobytes
};
bc_insn_print,
bc_insn_finalize,
bc_insn_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
bc_insn_tobytes
};
{
}
-void
-yasm_bc_set_long_common(yasm_bytecode *bc)
+int
+yasm_bc_expand_common(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
{
- yasm_internal_error(N_("bytecode does not have a long format"));
+ yasm_internal_error(N_("bytecode does not have any dependent spans"));
+ /*@unreached@*/
+ return 0;
}
void
}
static int
-bc_data_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_data_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
bytecode_data *bc_data = (bytecode_data *)bc->contents;
yasm_dataval *dv;
}
static int
-bc_leb128_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_leb128_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
bytecode_leb128 *bc_leb128 = (bytecode_leb128 *)bc->contents;
bc->len += bc_leb128->len;
}
static int
-bc_reserve_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_reserve_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
bytecode_reserve *reserve = (bytecode_reserve *)bc->contents;
/*@dependent@*/ /*@null@*/ const yasm_intnum *num;
}
static int
-bc_incbin_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_incbin_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
bytecode_incbin *incbin = (bytecode_incbin *)bc->contents;
FILE *f;
}
static int
-bc_align_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_align_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
yasm_internal_error(N_("align not yet implemented"));
/*
}
static int
-bc_org_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_org_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
yasm_internal_error(N_("org not yet implemented"));
#if 0
}
static int
-bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
yasm_internal_error(N_("bc_insn_calc_len() is not implemented"));
/*@notreached@*/
}
int
-yasm_bc_calc_len(yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical,
- /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
+yasm_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
int retval = 0;
bc->len = 0;
- *critical = NULL;
- *neg_thres = 0;
- *pos_thres = 0;
- *long_len = 0;
if (!bc->callback)
yasm_internal_error(N_("got empty bytecode in bc_resolve"));
else
- retval = bc->callback->calc_len(bc, long_len, critical, neg_thres,
- pos_thres);
-
+ retval = bc->callback->calc_len(bc, add_span, add_span_data);
+#if 0
/* Check for multiples */
if (bc->multiple) {
/*@dependent@*/ /*@null@*/ const yasm_intnum *num;
}
}
}
-
+#endif
/* If we got an error somewhere along the line, clear out any calc len */
- if (retval < 0) {
+ if (retval < 0)
bc->len = 0;
- *long_len = 0;
- }
return retval;
}
-void
-yasm_bc_set_long(yasm_bytecode *bc, unsigned long long_len)
+int
+yasm_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
{
- if (!bc->callback)
+ if (!bc->callback) {
yasm_internal_error(N_("got empty bytecode in bc_set_long"));
- else
- bc->callback->set_long(bc);
- bc->len = long_len;
+ /*@unreached@*/
+ return 0;
+ } else
+ return bc->callback->expand(bc, span, old_val, new_val, neg_thres,
+ pos_thres);
}
/*@null@*/ /*@only@*/ unsigned char *
/*@null@*/ yasm_intnum *yasm_common_calc_bc_dist
(/*@null@*/ yasm_bytecode *precbc1, /*@null@*/ yasm_bytecode *precbc2);
-/** Resolve EQUs in a bytecode and calculate its possible lengths.
- * Tries to minimize the length as much as possible for short_len.
- * The short length is set in the bytecode, and the long length is returned
- * in long_len (if applicable). Any bytecode multiple is NOT included in
- * the length or critical expression calculations.
+/**
+ * \param critical dependent expression for bytecode expansion
+ * \param neg_thres negative threshold for long/short decision
+ * \param pos_thres positive threshold for long/short decision
+ */
+typedef void (*yasm_bc_add_span_func)
+ (void *add_span_data, yasm_bytecode *bc, int id,
+ /*@only@*/ yasm_expr *dependent, long neg_thres, long pos_thres);
+
+/** Resolve EQUs in a bytecode and calculate its minimum size.
+ * Returns dependent bytecode spans for cases where, if the length spanned
+ * increases, it could cause the bytecode size to increase.
+ * Any bytecode multiple is NOT included in the length or spans generation;
+ * this must be handled at a higher level.
* \param bc bytecode
- * \param long_len longer length (returned). 0 returned if no longer
- * length is available (must be short).
- * \param critical critical expression if bytecode could be longer
- * (returned). NULL returned if no critical expression.
- * \param neg_thres negative threshold for long/short decision (returned)
- * \param pos_thres positive threshold for long/short decision (returned)
- * \return 0 if length is minimum (no way it can ever increase), negative if
- * there was an error recognized (and output) during execution, and
- * positive if the length may increase based on the critical expr.
+ * \return 0 if no error occurred, nonzero if there was an error recognized
+ * (and output) during execution.
* \note May store to bytecode updated expressions and the short length.
*/
-int yasm_bc_calc_len(yasm_bytecode *bc, /*@out@*/ unsigned long *long_len,
- /*@out@*/ /*@only@*/ yasm_expr **critical,
- /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
+int yasm_bc_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data);
-/** Mark a bytecode as long. Has no effect if the bytecode does not have
- * a long form. May return back a new longer threshold that can be reached.
+/** Recalculate a bytecode's length based on an expanded span length.
* \param bc bytecode
- * \param long_len long length (as given by yasm_bc_calc_len)
- * \param longer_len next stage of lengthening (returned)
+ * \param span span ID (as given to yasm_bc_add_span_func in
+ * yasm_bc_calc_len)
+ * \param old_val previous span value
+ * \param new_val new span value
* \param neg_thres negative threshold for long/short decision (returned)
* \param pos_thres postivie threshold for long/short decision (returned)
- * \return 0 if no longer threshold, positive if length may increase further
- * based on the new negative and positive thresholds.
- */
-int yasm_bc_set_long(yasm_bytecode *bc, unsigned long long_len,
- /*@out@*/ unsigned long *longer_len,
- /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
+ * \return 0 if bc no longer dependent on this span's length, negative if
+ * there was an error recognized (and output) during execution, and
+ * positive if bc size may increase for this span further based on the
+ * new negative and positive thresholds returned.
+ * \note May store to bytecode updated expressions and the updated length.
+ */
+int yasm_bc_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres);
/** Convert a bytecode into its byte representation.
* \param bc bytecode
return (Set_Max(val) < (long)size);
}
+int
+yasm_intnum_in_range(const yasm_intnum *intn, long low, long high)
+{
+ wordptr val = result;
+ wordptr lval = op1static;
+ wordptr hval = op2static;
+
+ /* If not already a bitvect, convert value to be written to a bitvect */
+ if (intn->type == INTNUM_BV)
+ val = intn->val.bv;
+ else {
+ BitVector_Empty(val);
+ BitVector_Chunk_Store(val, 32, 0, intn->val.ul);
+ }
+
+ /* Convert high and low to bitvects */
+ BitVector_Empty(lval);
+ if (low >= 0)
+ BitVector_Chunk_Store(lval, 32, 0, (unsigned long)low);
+ else {
+ BitVector_Chunk_Store(lval, 32, 0, (unsigned long)(-low));
+ BitVector_Negate(lval, lval);
+ }
+
+ BitVector_Empty(hval);
+ if (high >= 0)
+ BitVector_Chunk_Store(hval, 32, 0, (unsigned long)high);
+ else {
+ BitVector_Chunk_Store(hval, 32, 0, (unsigned long)(-high));
+ BitVector_Negate(hval, hval);
+ }
+
+ /* Compare! */
+ return (BitVector_Compare(val, lval) >= 0
+ && BitVector_Compare(val, hval) <= 0);
+}
+
unsigned long
yasm_intnum_get_leb128(const yasm_intnum *intn, unsigned char *ptr, int sign)
{
int yasm_intnum_check_size(const yasm_intnum *intn, size_t size,
size_t rshift, int rangetype);
+/** Check to see if intnum will fit into a particular numeric range.
+ * \param intn intnum
+ * \param low low end of range (inclusive)
+ * \param high high end of range (inclusive)
+ * \return Nonzero if intnum is within range.
+ */
+int yasm_intnum_in_range(const yasm_intnum *intn, long low, long high);
+
/** Output #yasm_intnum to buffer in LEB128-encoded form.
* \param intn intnum
* \param ptr pointer to storage for output bytes
#include "util.h"
/*@unused@*/ RCSID("$Id$");
+#include <limits.h>
+
#include "coretype.h"
#include "valparam.h"
#include "assocdat.h"
yasm_object_finalize(yasm_object *object)
{
yasm_section *sect;
- unsigned long bc_index = 0;
/* Iterate through sections */
STAILQ_FOREACH(sect, &object->sections, link) {
yasm_bytecode *cur = STAILQ_FIRST(§->bcs);
yasm_bytecode *prev;
- cur->bc_index = bc_index++;
-
/* Skip our locally created empty bytecode first. */
prev = cur;
cur = STAILQ_NEXT(cur, link);
/* Iterate through the remainder, if any. */
while (cur) {
- cur->bc_index = bc_index++;
/* Finalize */
yasm_bc_finalize(cur, prev);
prev = cur;
/*
* Robertson (1977) optimizer
- * Based on the algorithm given in:
+ * Based (somewhat loosely) on the algorithm given in:
* MRC Technical Summary Report # 1779
* CODE GENERATION FOR SHORT/LONG ADDRESS MACHINES
* Edward L. Robertson
*
* Each span keeps track of:
* - Associated bytecode (bytecode that depends on the span length)
+ * - Active/inactive state (starts out active)
* - Sign (negative/positive; negative being "backwards" in address)
* - Current length in bytes
* - New length in bytes
* - Negative/Positive thresholds
* - Span ID (unique within each bytecode)
- * = 0 -- need to update bytecode length on any span length change
- * > 0 -- only need to update bytecode length if exceeds thresholds
- * Span ID 0 is reserved for times, org, and align; other IDs can be
- * arbitrarily chosen by the bytecode.
*
- * How org, align, and times are handled:
+ * How org and align are handled:
* Some portions are critical values that must not depend on any bytecode
* offset (either relative or absolute).
*
- * ALIGN: Start with 0 length. Span from 0 to align bytecode, update
- * on any change (span ID 0). Alignment is critical value.
- * ORG: 0 length (always). Bump offset to org value. Span from 0 to
- * org bytecode, update on any change (span ID 0). If span's length
- * exceeds org value, error. ORG value is critical value.
+ * ALIGN: 0 length (always). Bump offset to alignment. Span from 0 to
+ * align bytecode, update on any change. If span length
+ * increases past alignment, increase offset by alignment and update
+ * dependent spans. Alignment is critical value.
+ * ORG: Same as align, but if span's length exceeds org value, error.
+ * ORG value is critical value.
+ *
+ * How times is handled:
+ *
* TIMES: Handled separately from bytecode "raw" size. If not span-dependent,
* trivial (just multiplied in at any bytecode size increase). Span
- * dependent times update on any change (span ID 0).
+ * dependent times update on any change (span ID 0). If the resultant
+ * next bytecode offset would be less than the old next bytecode offset,
+ * error. Otherwise increase offset and update dependent spans.
+ *
+ * To reduce interval tree size, a first expansion pass is performed
+ * before the spans are added to the tree.
*
* Basic algorithm outline:
*
* 1. Initialization:
* a. Number bytecodes sequentially (via bc_index) and calculate offsets
- * of all bytecodes assuming minimum length (done in object_finalize).
+ * of all bytecodes assuming minimum length, building a list of all
+ * dependent spans as we go.
* "minimum" here means absolute minimum:
- * - align and org bytecodes will be 0 length
+ * - align 0 length
* - times values (with span-dependent values) assumed to be 0
- * b. Second pass through bytecodes continues to assume absolute minimum
- * bytecode length but builds spans if necessary. To reduce interval
- * tree size, this step avoids building spans for things that are
- * considered "certainly long" such as inter-section references or if
- * the distance calculated based on minimum length is already greater
- * than the long threshold. If already certainly long, bytecode is
- * updated with new length and subsequent bytecode offsets are updated
- * with a running delta.
- * c. Iterate over spans. Update span's length based on new bytecode offsets
- * determined in 1b. If span's length exceeds long threshold, add that
+ * - org bumps offset
+ * b. Iterate over spans. Set span length based on bytecode offsets
+ * determined in 1a. If span is "certainly" long because the span
+ * is an absolute reference to another section (or external) or the
+ * distance calculated based on the minimum length is greater than the
+ * span's threshold, expand the span's bytecode, and if no further
+ * expansion can result, delete the span. Otherwise (or if the
+ * expansion results in another threshold of expansion), add span to
+ * interval tree.
+ * c. Iterate over bytecodes to update all bytecode offsets based on new
+ * (expanded) lengths calculated in 1b.
+ * d. Iterate over spans. Update span's length based on new bytecode offsets
+ * determined in 1c. If span's length exceeds long threshold, add that
* span to Q.
* 2. Main loop:
* While Q not empty:
* Expand BC dependent on span at head of Q (and remove span from Q).
- * For each span that contains BC:
+ * Update span:
+ * If BC no longer dependent on span, mark span as inactive.
+ * If BC has new thresholds for span, update span.
+ * If BC increased in size, for each active span that contains BC:
* Increase span length by difference between short and long BC length.
* If span exceeds long threshold (or is flagged to recalculate on any
* change), add it to tail of Q.
* 3. Final pass over bytecodes to generate final offsets.
*/
-void
-yasm_object_optimize(yasm_object *object, yasm_arch *arch)
+typedef struct yasm_span {
+ /*@reldef@*/ STAILQ_ENTRY(yasm_span) link;
+
+ /*@dependent@*/ yasm_bytecode *bc;
+
+ /*@owned@*/ yasm_expr *dependent;
+
+ /* Special handling: see descriptions above */
+ enum {
+ NOT_SPECIAL = 0,
+ SPECIAL_ALIGN,
+ SPECIAL_ORG,
+ SPECIAL_TIMES
+ } special;
+
+ long cur_val;
+ long new_val;
+
+ long neg_thres;
+ long pos_thres;
+
+ int id;
+
+ int active;
+} yasm_span;
+
+typedef struct optimize_data {
+ /*@reldef@*/ STAILQ_HEAD(yasm_spanhead, yasm_span) spans;
+} optimize_data;
+
+static void
+optimize_add_span(void *add_span_data, yasm_bytecode *bc, int id,
+ /*@only@*/ yasm_expr *dependent, long neg_thres,
+ long pos_thres)
+{
+ optimize_data *optd = (optimize_data *)add_span_data;
+ yasm_span *span = yasm_xmalloc(sizeof(yasm_span));
+
+ span->bc = bc;
+ span->dependent = dependent;
+ span->special = NOT_SPECIAL;
+ span->cur_val = 0;
+ span->new_val = 0;
+ span->neg_thres = neg_thres;
+ span->pos_thres = pos_thres;
+ span->id = id;
+ span->active = 1;
+
+ STAILQ_INSERT_TAIL(&optd->spans, span, link);
+}
+
+static void
+update_all_bc_offsets(yasm_object *object)
{
yasm_section *sect;
- int saw_error = 0;
- /* Step 1b */
STAILQ_FOREACH(sect, &object->sections, link) {
- unsigned long i;
+ unsigned long offset = 0;
yasm_bytecode *cur = STAILQ_FIRST(§->bcs);
yasm_bytecode *prev;
/* Iterate through the remainder, if any. */
while (cur) {
- unsigned long long_len;
- /*@only@*/ /*@null@*/ yasm_expr *critical;
- long neg_thres;
- long pos_thres;
- int retval;
-
- switch (yasm_bc_calc_len(cur, &long_len, &critical, &neg_thres,
- &pos_thres)) {
- case -1:
- saw_error = 1;
- break;
- case 0:
- /* No special action required */
- break;
- case 1:
- /* Need to build a span */
- yasm_bc_set_long(cur, long_len);
- yasm_expr_destroy(critical);
- break;
- default:
- yasm_internal_error(N_("bad return value from bc_calc_len"));
- }
-
+ cur->offset = offset;
+ offset += cur->len;
prev = cur;
cur = STAILQ_NEXT(cur, link);
}
}
+}
- if (saw_error)
- return;
+void
+yasm_object_optimize(yasm_object *object, yasm_arch *arch)
+{
+ yasm_section *sect;
+ unsigned long bc_index = 0;
+ int saw_error = 0;
+ optimize_data optd;
+ yasm_span *span;
+ long neg_thres, pos_thres;
- /* Step 3 */
+ STAILQ_INIT(&optd.spans);
+
+ /* Step 1a */
STAILQ_FOREACH(sect, &object->sections, link) {
unsigned long offset = 0;
yasm_bytecode *cur = STAILQ_FIRST(§->bcs);
yasm_bytecode *prev;
+ cur->bc_index = bc_index++;
+
/* Skip our locally created empty bytecode first. */
prev = cur;
cur = STAILQ_NEXT(cur, link);
/* Iterate through the remainder, if any. */
while (cur) {
+ cur->bc_index = bc_index++;
+
+ if (yasm_bc_calc_len(cur, optimize_add_span, &optd))
+ saw_error = 1;
+
+ /* TODO: times */
+ if (cur->multiple)
+ yasm_internal_error("multiple not yet supported");
+
cur->offset = offset;
offset += cur->len;
prev = cur;
cur = STAILQ_NEXT(cur, link);
}
}
+
+ if (saw_error)
+ return;
+
+ /* Step 1b */
+ STAILQ_FOREACH(span, &optd.spans, link) {
+ yasm_expr *depcopy = yasm_expr_copy(span->dependent);
+ yasm_intnum *intn =
+ yasm_expr_get_intnum(&depcopy, yasm_common_calc_bc_dist);
+ if (intn)
+ span->new_val = yasm_intnum_get_int(intn);
+ else {
+ /* absolute, external, or too complex; force to longer form */
+ span->new_val = LONG_MAX;
+ span->active = 0;
+ }
+
+ if (span->new_val < span->neg_thres
+ || span->new_val > span->pos_thres) {
+ int retval = yasm_bc_expand(span->bc, span->id, span->cur_val,
+ span->new_val, &neg_thres, &pos_thres);
+ if (retval < 0)
+ saw_error = 1;
+ else if (retval > 0) {
+ span->neg_thres = neg_thres;
+ span->pos_thres = pos_thres;
+ } else
+ span->active = 0;
+ }
+ span->cur_val = span->new_val;
+ yasm_expr_destroy(depcopy);
+ }
+
+ if (saw_error)
+ return;
+
+ /* Step 1c */
+ update_all_bc_offsets(object);
+
+ /* Step 1d */
+ STAILQ_FOREACH(span, &optd.spans, link) {
+ if (!span->active)
+ continue;
+ }
+
+ /* Step 2 */
+
+ /* Step 3 */
+ update_all_bc_offsets(object);
}
static void x86_bc_insn_destroy(void *contents);
static void x86_bc_insn_print(const void *contents, FILE *f,
int indent_level);
-static int x86_bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres,
- long *pos_thres);
-static void x86_bc_insn_set_long(yasm_bytecode *bc);
+static int x86_bc_insn_calc_len(yasm_bytecode *bc,
+ yasm_bc_add_span_func add_span,
+ void *add_span_data);
+static int x86_bc_insn_expand(yasm_bytecode *bc, int span, long old_val,
+ long new_val, /*@out@*/ long *neg_thres,
+ /*@out@*/ long *pos_thres);
static int x86_bc_insn_tobytes(yasm_bytecode *bc, unsigned char **bufp,
void *d, yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void x86_bc_jmp_destroy(void *contents);
static void x86_bc_jmp_print(const void *contents, FILE *f, int indent_level);
-static int x86_bc_jmp_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres,
- long *pos_thres);
-static void x86_bc_jmp_set_long(yasm_bytecode *bc);
+static int x86_bc_jmp_calc_len(yasm_bytecode *bc,
+ yasm_bc_add_span_func add_span,
+ void *add_span_data);
+static int x86_bc_jmp_expand(yasm_bytecode *bc, int span, long old_val,
+ long new_val, /*@out@*/ long *neg_thres,
+ /*@out@*/ long *pos_thres);
static int x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp,
void *d, yasm_output_expr_func output_expr,
/*@null@*/ yasm_output_reloc_func output_reloc);
static void x86_bc_jmpfar_destroy(void *contents);
static void x86_bc_jmpfar_print(const void *contents, FILE *f,
int indent_level);
-static int x86_bc_jmpfar_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres,
- long *pos_thres);
+static int x86_bc_jmpfar_calc_len(yasm_bytecode *bc,
+ yasm_bc_add_span_func add_span,
+ void *add_span_data);
static int x86_bc_jmpfar_tobytes
(yasm_bytecode *bc, unsigned char **bufp, void *d,
yasm_output_expr_func output_expr,
x86_bc_insn_print,
yasm_bc_finalize_common,
x86_bc_insn_calc_len,
- x86_bc_insn_set_long,
+ x86_bc_insn_expand,
x86_bc_insn_tobytes
};
x86_bc_jmp_print,
yasm_bc_finalize_common,
x86_bc_jmp_calc_len,
- x86_bc_jmp_set_long,
+ x86_bc_jmp_expand,
x86_bc_jmp_tobytes
};
x86_bc_jmpfar_print,
yasm_bc_finalize_common,
x86_bc_jmpfar_calc_len,
- yasm_bc_set_long_common,
+ yasm_bc_expand_common,
x86_bc_jmpfar_tobytes
};
}
static int
-x86_bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+x86_bc_insn_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
x86_insn *insn = (x86_insn *)bc->contents;
/*@null@*/ yasm_expr *temp;
x86_effaddr *x86_ea = (x86_effaddr *)insn->ea;
yasm_effaddr *ea = &x86_ea->ea;
yasm_immval *imm = insn->imm;
- int retval = 0;
- int common_len = 0;
if (ea) {
if (ea->disp) {
* critical expression.
*/
bc->len += 1;
- long_len += (insn->common.addrsize == 16) ? 2U : 4U;
- *critical = yasm_expr_copy(ea->disp);
- *neg_thres = -128;
- *pos_thres = 127;
- retval = 1;
+ add_span(add_span_data, bc, 1, yasm_expr_copy(ea->disp), -128,
+ 127);
} else
bc->len += ea->len;
}
/* Compute length of ea and add to total */
- common_len += x86_ea->need_modrm + (x86_ea->need_sib ? 1:0);
- common_len += (x86_ea->ea.segreg != 0) ? 1 : 0;
+ bc->len += x86_ea->need_modrm + (x86_ea->need_sib ? 1:0);
+ bc->len += (x86_ea->ea.segreg != 0) ? 1 : 0;
}
if (imm) {
/* We can use the sign-extended byte form: shorten
* the immediate length to 1.
*/
+ imm->len = 1;
immlen = 1;
} else {
/* We can't use the ,1. */
* expression.
*/
immlen = 1;
- long_len += imm->len;
- *critical = yasm_expr_copy(imm->val);
- *neg_thres = -128;
- *pos_thres = 127;
- retval = 1;
+ add_span(add_span_data, bc, 2, yasm_expr_copy(imm->val),
+ -128, 127);
}
}
}
insn->postop = X86_POSTOP_NONE;
} else {
- /* Unknown; default to ,1 form and set as critical
- * expression.
+ /* Just assume we can't use the ,1 form: allowing this
+ * is more work than it's worth.
*/
- immlen = 0;
- long_len += imm->len;
- *critical = yasm_expr_copy(imm->val);
- *neg_thres = 1;
- *pos_thres = 1;
- retval = 1;
+ insn->opcode.opcode[0] = insn->opcode.opcode[1];
+ insn->postop = X86_POSTOP_NONE;
}
}
}
- common_len += immlen;
+ bc->len += immlen;
}
- common_len += insn->opcode.len;
- common_len += x86_common_calc_len(&insn->common);
- common_len += (insn->special_prefix != 0) ? 1:0;
+ bc->len += insn->opcode.len;
+ bc->len += x86_common_calc_len(&insn->common);
+ bc->len += (insn->special_prefix != 0) ? 1:0;
if (insn->rex != 0xff &&
(insn->rex != 0 ||
(insn->common.mode_bits == 64 && insn->common.opersize == 64 &&
insn->def_opersize_64 != 64)))
- common_len++;
-
- bc->len += common_len;
- if (retval > 0)
- *long_len += common_len;
-
- return retval;
+ bc->len++;
+ return 0;
}
-static void
-x86_bc_insn_set_long(yasm_bytecode *bc)
+static int
+x86_bc_insn_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
{
x86_insn *insn = (x86_insn *)bc->contents;
/*@null@*/ yasm_expr *temp;
ea->len = (insn->common.addrsize == 16) ? 2U : 4U;
x86_ea->modrm &= ~0300;
x86_ea->modrm |= 0200;
+ bc->len--;
+ bc->len += ea->len;
}
}
if (imm && imm->val) {
- /* Handle shift postop special-casing */
- if (insn->postop == X86_POSTOP_SHIFT) {
- /* We can't use the ,1 form. */
- insn->opcode.opcode[0] = insn->opcode.opcode[1];
+ if (insn->postop == X86_POSTOP_SIGNEXT_IMM8) {
+ bc->len--;
+ bc->len += imm->len;
insn->postop = X86_POSTOP_NONE;
}
}
+
+ return 0;
}
static int
-x86_bc_jmp_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+x86_bc_jmp_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
x86_jmp *jmp = (x86_jmp *)bc->contents;
- int retval = 0;
- unsigned int common_len;
unsigned char opersize;
/* As opersize may be 0, figure out its "real" value. */
opersize = (jmp->common.opersize == 0) ?
jmp->common.mode_bits : jmp->common.opersize;
- if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) {
- if (jmp->shortop.len == 0) {
- yasm__error(bc->line, N_("short jump does not exist"));
- return -1;
- }
+ bc->len += x86_common_calc_len(&jmp->common);
- /* We want to be sure to error if we exceed short length, so still
- * put it in as a critical expression, but leave the return value as
- * 0.
- */
- bc->len += jmp->shortop.len + 1;
- *critical = yasm_expr_create(YASM_EXPR_SUB,
- yasm_expr_expr(yasm_expr_copy(jmp->target)),
- yasm_expr_sym(jmp->origin), bc->line);
- *neg_thres = -128;
- *pos_thres = 127;
- } else if (jmp->op_sel == JMP_NEAR_FORCED || jmp->shortop.len == 0) {
+ if (jmp->op_sel == JMP_NEAR_FORCED || jmp->shortop.len == 0) {
if (jmp->nearop.len == 0) {
yasm__error(bc->line, N_("near jump does not exist"));
return -1;
}
+ /* Near jump, no spans needed */
bc->len += jmp->nearop.len;
bc->len += (opersize == 16) ? 2 : 4;
- } else {
- /* short length goes into bytecode */
- retval = 1;
- bc->len += jmp->shortop.len + 1;
- *long_len += jmp->nearop.len;
- *long_len += (opersize == 16) ? 2 : 4;
- *critical = yasm_expr_create(YASM_EXPR_SUB,
- yasm_expr_expr(yasm_expr_copy(jmp->target)),
- yasm_expr_sym(jmp->origin), bc->line);
- *neg_thres = -128;
- *pos_thres = 127;
+ return 0;
}
- common_len = x86_common_calc_len(&jmp->common);
- bc->len += common_len;
- if (retval > 0)
- *long_len += common_len;
+ if (jmp->op_sel == JMP_SHORT_FORCED || jmp->nearop.len == 0) {
+ if (jmp->shortop.len == 0) {
+ yasm__error(bc->line, N_("short jump does not exist"));
+ return -1;
+ }
+
+ /* We want to be sure to error if we exceed short length, so
+ * put it in as a dependent expression (falling through).
+ */
+ }
- return retval;
+ /* Short jump, generate span */
+ bc->len += jmp->shortop.len + 1;
+ add_span(add_span_data, bc, 1,
+ yasm_expr_create(YASM_EXPR_SUB,
+ yasm_expr_expr(yasm_expr_copy(jmp->target)),
+ yasm_expr_sym(jmp->origin), bc->line),
+ -128, 127);
+ return 0;
}
-static void
-x86_bc_jmp_set_long(yasm_bytecode *bc)
+static int
+x86_bc_jmp_expand(yasm_bytecode *bc, int span, long old_val, long new_val,
+ /*@out@*/ long *neg_thres, /*@out@*/ long *pos_thres)
{
x86_jmp *jmp = (x86_jmp *)bc->contents;
+ unsigned char opersize;
+
+ if (span != 1)
+ yasm_internal_error(N_("unrecognized span id"));
+
+ /* As opersize may be 0, figure out its "real" value. */
+ opersize = (jmp->common.opersize == 0) ?
+ jmp->common.mode_bits : jmp->common.opersize;
if (jmp->nearop.len == 0) {
- yasm__error(bc->line,
- N_("short jump out of range"));
- return;
+ yasm__error(bc->line, N_("short jump out of range"));
+ return -1;
}
+
+ /* Upgrade to a near jump */
jmp->op_sel = JMP_NEAR;
+ bc->len -= jmp->shortop.len + 1;
+ bc->len += jmp->nearop.len;
+ bc->len += (opersize == 16) ? 2 : 4;
+
+ return 0;
}
static int
-x86_bc_jmpfar_calc_len(yasm_bytecode *bc, unsigned long *long_len,
- yasm_expr **critical, long *neg_thres, long *pos_thres)
+x86_bc_jmpfar_calc_len(yasm_bytecode *bc, yasm_bc_add_span_func add_span,
+ void *add_span_data)
{
x86_jmpfar *jmpfar = (x86_jmpfar *)bc->contents;
unsigned char opersize;