From: Peter Johnson Date: Sun, 28 May 2006 04:54:44 +0000 (-0000) Subject: Change calc_len to call back to add_span function so that multiple spans can X-Git-Tag: v0.6.0~172^2~35 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=26e2ac882f43b0965918a22b6f147f6e95d33f3e;p=yasm Change calc_len to call back to add_span function so that multiple spans can be added by a single calc_len. svn path=/branches/new-optimizer/; revision=1544 --- diff --git a/libyasm/bc-int.h b/libyasm/bc-int.h index 66c079dd..9bdea87c 100644 --- a/libyasm/bc-int.h +++ b/libyasm/bc-int.h @@ -59,10 +59,10 @@ typedef struct yasm_bytecode_callback { 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); @@ -79,8 +79,8 @@ struct yasm_bytecode { /* 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; @@ -125,11 +125,13 @@ void yasm_bc_transform(yasm_bytecode *bc, */ 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) diff --git a/libyasm/bytecode.c b/libyasm/bytecode.c index d9968adb..daffb2ba 100644 --- a/libyasm/bytecode.c +++ b/libyasm/bytecode.c @@ -134,10 +134,8 @@ typedef struct bytecode_insn { 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); @@ -145,30 +143,27 @@ static int bc_data_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, 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); @@ -176,20 +171,16 @@ static int bc_incbin_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, 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); @@ -197,10 +188,8 @@ static int bc_org_tobytes(yasm_bytecode *bc, unsigned char **bufp, void *d, 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); @@ -212,7 +201,7 @@ static const yasm_bytecode_callback bc_data_callback = { bc_data_print, yasm_bc_finalize_common, bc_data_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_data_tobytes }; @@ -221,7 +210,7 @@ static const yasm_bytecode_callback bc_leb128_callback = { bc_leb128_print, bc_leb128_finalize, bc_leb128_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_leb128_tobytes }; @@ -230,7 +219,7 @@ static const yasm_bytecode_callback bc_reserve_callback = { bc_reserve_print, yasm_bc_finalize_common, bc_reserve_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_reserve_tobytes }; @@ -239,7 +228,7 @@ static const yasm_bytecode_callback bc_incbin_callback = { bc_incbin_print, yasm_bc_finalize_common, bc_incbin_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_incbin_tobytes }; @@ -248,7 +237,7 @@ static const yasm_bytecode_callback bc_align_callback = { bc_align_print, bc_align_finalize, bc_align_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_align_tobytes }; @@ -257,7 +246,7 @@ static const yasm_bytecode_callback bc_org_callback = { bc_org_print, yasm_bc_finalize_common, bc_org_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_org_tobytes }; @@ -266,7 +255,7 @@ static const yasm_bytecode_callback bc_insn_callback = { bc_insn_print, bc_insn_finalize, bc_insn_calc_len, - yasm_bc_set_long_common, + yasm_bc_expand_common, bc_insn_tobytes }; @@ -382,10 +371,13 @@ yasm_bc_finalize_common(yasm_bytecode *bc, yasm_bytecode *prev_bc) { } -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 @@ -444,8 +436,8 @@ bc_data_print(const void *contents, FILE *f, int indent_level) } 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; @@ -588,8 +580,8 @@ bc_leb128_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) } 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; @@ -654,8 +646,8 @@ bc_reserve_print(const void *contents, FILE *f, int indent_level) } 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; @@ -734,8 +726,8 @@ bc_incbin_print(const void *contents, FILE *f, int indent_level) } 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; @@ -902,8 +894,8 @@ bc_align_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) } 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")); /* @@ -1028,8 +1020,8 @@ bc_org_print(const void *contents, FILE *f, int indent_level) } 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 @@ -1136,8 +1128,8 @@ bc_insn_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc) } 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@*/ @@ -1324,24 +1316,18 @@ yasm_common_calc_bc_dist(/*@null@*/ yasm_bytecode *precbc1, } 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; @@ -1360,24 +1346,25 @@ yasm_bc_calc_len(yasm_bytecode *bc, /*@out@*/ unsigned long *long_len, } } } - +#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 * diff --git a/libyasm/bytecode.h b/libyasm/bytecode.h index 62dea639..8260c88e 100644 --- a/libyasm/bytecode.h +++ b/libyasm/bytecode.h @@ -273,40 +273,44 @@ void yasm_bc_finalize(yasm_bytecode *bc, yasm_bytecode *prev_bc); /*@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 diff --git a/libyasm/intnum.c b/libyasm/intnum.c index 3b594e3c..b8e0a5ac 100644 --- a/libyasm/intnum.c +++ b/libyasm/intnum.c @@ -650,6 +650,43 @@ yasm_intnum_check_size(const yasm_intnum *intn, size_t size, size_t rshift, 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) { diff --git a/libyasm/intnum.h b/libyasm/intnum.h index ccda97be..5edff6eb 100644 --- a/libyasm/intnum.h +++ b/libyasm/intnum.h @@ -192,6 +192,14 @@ void yasm_intnum_get_sized(const yasm_intnum *intn, unsigned char *ptr, 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 diff --git a/libyasm/section.c b/libyasm/section.c index 1271c284..346f18c7 100644 --- a/libyasm/section.c +++ b/libyasm/section.c @@ -28,6 +28,8 @@ #include "util.h" /*@unused@*/ RCSID("$Id$"); +#include + #include "coretype.h" #include "valparam.h" #include "assocdat.h" @@ -296,22 +298,18 @@ void 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; @@ -541,7 +539,7 @@ yasm_section_print(const yasm_section *sect, FILE *f, int indent_level, /* * 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 @@ -567,67 +565,129 @@ yasm_section_print(const yasm_section *sect, FILE *f, int indent_level, * * 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; @@ -638,54 +698,103 @@ yasm_object_optimize(yasm_object *object, yasm_arch *arch) /* 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); } diff --git a/modules/arch/x86/x86bc.c b/modules/arch/x86/x86bc.c index d97399b0..7bfc4cc6 100644 --- a/modules/arch/x86/x86bc.c +++ b/modules/arch/x86/x86bc.c @@ -69,20 +69,24 @@ static void x86_ea_print(const yasm_effaddr *ea, FILE *f, int indent_level); 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); @@ -90,9 +94,9 @@ static int x86_bc_jmp_tobytes(yasm_bytecode *bc, unsigned char **bufp, 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, @@ -112,7 +116,7 @@ static const yasm_bytecode_callback x86_bc_callback_insn = { 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 }; @@ -121,7 +125,7 @@ static const yasm_bytecode_callback x86_bc_callback_jmp = { 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 }; @@ -130,7 +134,7 @@ static const yasm_bytecode_callback x86_bc_callback_jmpfar = { 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 }; @@ -534,16 +538,14 @@ x86_common_calc_len(const x86_common *common) } 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) { @@ -579,18 +581,15 @@ x86_bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len, * 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) { @@ -609,6 +608,7 @@ x86_bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len, /* 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. */ @@ -620,11 +620,8 @@ x86_bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len, * 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); } } @@ -645,40 +642,32 @@ x86_bc_insn_calc_len(yasm_bytecode *bc, unsigned long *long_len, } 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; @@ -692,93 +681,99 @@ x86_bc_insn_set_long(yasm_bytecode *bc) 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;