This uses the same interface as my previous patch: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg00473.html , but I refined the algorithm for the get_insn_variants mechanism to work properly with the reworked ARC port - http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01891.html - the only user so far, and added some documentation.
Bootstrapped in r193731 on i686-pc-linux-gnu . As mentioned before, I can adjust the config part of the previous hookization approach to convert the existing ADJUST_INSN_LENGTH targets to use TARGET_ADJUST_INSN_LNEGTH if that is desired.
2012-11-13 Joern Rennecke <joern.renne...@embecosm.com> * doc/tm.texi.in (@hook TARGET_ADJUST_INSN_LENGTH): Add. (@hook TARGET_INSN_LENGTH_PARAMETERS): Add. * doc/tm.texi: Regenerate. * final.c (get_attr_length_1): Assert HAVE_ATTR_length. Use targetm.adjust_insn_length instead of ADJUST_INSN_LENGTH. (shorten_branches_context_t): New typedef. (adjust_length): New function. (shorten_branches): Use adjust_length instead of ADJUST_INSN_LENGTH. Try to satidfy alignment by using variable length instructions. * target.def (adjust_insn_length, insn_length_parameters): New hooks. * target.h (insn_length_variant_t, insn_length_parameters_t): New types. * targhooks.c (default_adjust_insn_length): New function. * targhooks.h (default_adjust_insn_length): Declare. * genattrtab.c (make_length_attrs): Generate an insn_current_length function that is also valid for prima facie invariant length instructions. Index: doc/tm.texi =================================================================== --- doc/tm.texi (revision 2691) +++ doc/tm.texi (working copy) @@ -11365,3 +11365,15 @@ @deftypefn {Target Hook} {unsigned HOST_ @deftypevr {Target Hook} {unsigned char} TARGET_ATOMIC_TEST_AND_SET_TRUEVAL This value should be set if the result written by @code{atomic_test_and_set} is not exactly 1, i.e. the @code{bool} @code{true}. @end deftypevr + +@deftypefn {Target Hook} int TARGET_ADJUST_INSN_LENGTH (rtx @var{insn}, int @var{length}, bool @var{in_delay_sequence}) +Return an adjusted length for @var{insn}. @var{length} is the value that has been calculated using the @code{length} instruction attribute. @var{in_delay_sequence} if @var{insn} forms part of a delay sequence. The default implementation uses @code{ADJUST_INSN_LENGTH}, if defined. +@end deftypefn + +@deftypefn {Target Hook} void TARGET_INSN_LENGTH_PARAMETERS (insn_length_parameters_t *@var{insn_length_parameters}) +Fill in values used for branch shortening. The type @code{insn_length_parameters_t} is defined in @file{target-def.h}. The main feature is the @code{get_variants} function. @smallexample +int (*get_variants) (rtx insn, int length, bool sequence_p, bool target_p, + insn_length_variant_t *variants) +@end smallexample + For instructions where the ordinary monotonic branch shortening is insufficeint to describe the alternatives, e.g. because there is alignemnt involved, get_variants can provide two or more variants for the instruction. The return value is the number of variants filled in, which must never exceed the number filled in by @code{insn_length_parameters} in the @var{max_variants} field. The set of variants for any given instruction filled in should not vary during branch shortening, but rather unavailable variants should be flagged with a @samp{false} @var{enabled} field. This allows @code{shorten_branches} to keep track of varaints that have been ever disabled in a previous iteration and keep them disabled, so as to avoid infinite looping inside @code{shorten_branches}. The @var{length} parameter provides the length calculated previously from attributes. @var{sequence_p} indicates if the instruction presented is inside a @code{SEQUENCE}. Note, if you make @code{get_variants} provide variants for an entire @code{SEQUENCE}, the @code{SEQUENCE} will henceforth be handled as a single entity for branch shortening. @var{target_p} indicates if the instruction is considered the target of a branch, function call, or function return. +@end deftypefn Index: doc/tm.texi.in =================================================================== --- doc/tm.texi.in (revision 2691) +++ doc/tm.texi.in (working copy) @@ -11205,3 +11205,7 @@ @hook TARGET_MEMMODEL_CHECK @end deftypefn @hook TARGET_ATOMIC_TEST_AND_SET_TRUEVAL + +@hook TARGET_ADJUST_INSN_LENGTH + +@hook TARGET_INSN_LENGTH_PARAMETERS Index: final.c =================================================================== --- final.c (revision 2691) +++ final.c (working copy) @@ -82,6 +82,7 @@ Software Foundation; either version 3, o #include "cfgloop.h" #include "params.h" #include "tree-pretty-print.h" /* for dump_function_header */ +#include "sbitmap.h" #ifdef XCOFF_DEBUGGING_INFO #include "xcoffout.h" /* Needed for external data @@ -377,8 +378,7 @@ get_attr_length_1 (rtx insn, int (*fallb int i; int length = 0; - if (!HAVE_ATTR_length) - return 0; + gcc_assert (HAVE_ATTR_length); if (insn_lengths_max_uid > INSN_UID (insn)) return insn_lengths[INSN_UID (insn)]; @@ -424,10 +424,7 @@ get_attr_length_1 (rtx insn, int (*fallb break; } -#ifdef ADJUST_INSN_LENGTH - ADJUST_INSN_LENGTH (insn, length); -#endif - return length; + return targetm.adjust_insn_length (insn, length, false); } /* Obtain the current length of an insn. If branch shortening has been done, @@ -828,6 +825,243 @@ struct rtl_opt_pass pass_compute_alignme }; +/* Context to pass from shorten_branches to adjust_length. */ +typedef struct { + /* For each varying length insn, a length to keep across iterations, to + avoid cycles. */ + int *uid_lock_length; + /* Number of iterations since last lock_length change. */ + int niter; + /* Values obtained from targetm.insn_length_parameters call. */ + insn_length_parameters_t parameters; + /* A scratch space to hold all the variants of the insn currently being + considered. */ + insn_length_variant_t *variants; + /* For each variant number, an sbitmap that says if that variant is still + being considered for this shuid. */ + sbitmap *shuid_variants; + /* indexed by shuid, alignment offset required at end of insn. */ + signed char *request_align; + /* Indexed by uid, indicates if and how an insn length varies - see + varying_length variable in shorten_branches. */ + char *varying_length; + /* uid of last insn that provides a choice of lengths. */ + int last_aligning_insn; + bool something_changed; +} shorten_branches_context_t; + +/* NEW_LENGTH is the length for INSN that has been computed by evaluating + the raw instruction length attribute. Return an adjusted length by + avaluating the adjust_insn_length target hook and the get_variants hook + in parameters. + If INSN is inside a SEQUENCE, then SEQ is that SEQUENCE. + TARGET_P indicates if INSN is the target of a call, branch, or call + return. */ + +static int +adjust_length (rtx insn, int new_length, bool seq_p, + shorten_branches_context_t *ctx, bool target_p) +{ + int uid = INSN_UID (insn); + new_length = targetm.adjust_insn_length (insn, new_length, seq_p); + /* If the sequence as a whole is subject to variant selection, don't + try it for the constituent instructions too; at best, it'd be a waste + of time; at worst, it leads to chaos when trying to align the sequence + by tweaking a constituent instruction. + Also, in general, if we have previously established that + variant selection doesn't apply, stick with that decision. */ + bool select_variant = (ctx->varying_length[uid] & 4); + int n_variants; + + if (new_length < 0) + fatal_insn ("negative insn length", insn); + int iter_threshold = 0; + memset (ctx->variants, 0, + sizeof *ctx->variants * ctx->parameters.max_variants); + if (select_variant + && (n_variants + = ctx->parameters.get_variants (insn, new_length, seq_p, target_p, + ctx->variants)) != 0) + { + unsigned align_base = 1 << ctx->parameters.align_base_log; + unsigned align_base_mask = align_base - 1; + unsigned align_base_set = (1U << align_base) - 1; + unsigned align_unit_mask = (1 << ctx->parameters.align_unit_log) - 1; + gcc_assert ((insn_current_address & align_unit_mask) == 0); + int best_cost = new_length = INT_MAX; + bool can_align = false; + int best_need_align = -1; + bool best_right_align = false; + int shuid = uid_shuid[uid]; + + /* Freeze disabled variants, and find cheapest variant; + with any align if we have last_aligning_insn, otherwise with + actual align. + See if we can provide alignment at no extra cost. */ + for (int i = 0; i < n_variants; i++) + { + insn_length_variant_t *variant = &ctx->variants[i]; + if (bitmap_bit_p (ctx->shuid_variants[i], shuid)) + { + if (!variant->enabled) + { + /* We could have the variant provide an iter_threshold + field here, to use instead of 0 ... if there is any + point in having variants that get frozen out only + after a few tries. */ + if (ctx->niter >= 0) + { + bitmap_clear_bit (ctx->shuid_variants[i], shuid); + ctx->something_changed = true; + ctx->niter = 0; + } + continue; + } + int need_align; + unsigned align_offset + = insn_current_address >> ctx->parameters.align_unit_log; + bool right_align = false; + align_offset &= align_base_mask; + if (variant->align_set == align_base_set) + { + need_align = -1; + right_align = ctx->last_aligning_insn != 0; + } + else if ((1 << align_offset) & variant->align_set) + need_align = 0; /* OK. */ + /* Check if adding one unit provides alignment. + FIXME: this works for the current ARC port, + but we should more generally search for an offset + such that a variable length insn can provide it. */ + else if ((1 << ((align_offset + 1) & align_base_mask) + & variant->align_set) + && ctx->last_aligning_insn) + need_align = 1; + else + continue; + int length = variant->length; + /* Add extra length needed for alignment to insn length. + This should go away in the next iteration, when the + desired alignment is provided. If that doesn't happen, + we have a bug, possibly giving the target a length that + doesn't correspond to an available variant. */ + if (need_align >= 0) + length += need_align << ctx->parameters.align_unit_log; + /* FIXME: Add probabilistic weighting and target cost. */ + int cost = (variant->fallthrough_cost + variant->branch_cost); + if (ctx->request_align[shuid] >= 0 && need_align >= 0) + { + unsigned offset = insn_current_address + length; + offset >>= ctx->parameters.align_unit_log; + offset = ctx->request_align[shuid] - offset; + /* Fixme: might want to apply smaller mask if alignment + requirement has matching bitmask. */ + offset &= align_base_mask; + if (offset == 0) + right_align = true; + } + /* ??? if we had remembered the cost of not getting the desired + alignment, we could weigh cost in this insn against cost + for alignment in following insn. */ + if (cost > best_cost) + continue; + if (cost < best_cost) + { + best_cost = cost; + new_length = length; + best_need_align = need_align; + best_right_align = right_align; + can_align = false; + continue; + } + /* FIXME: to cover the general case, we should really + build a bitmap of the offsets that we can manage for + alignment purposes. */ + if (((length - new_length) + & (align_base_mask << ctx->parameters.align_unit_log)) + && new_length <= ctx->uid_lock_length[uid] + && length <= ctx->uid_lock_length[uid]) + can_align = true; + if (length < new_length && (right_align || !best_right_align)) + { + new_length = length; + best_need_align = need_align; + best_right_align = right_align; + } + else if ((length == new_length + && need_align < best_need_align + && (right_align || !best_right_align)) + || (right_align && !best_right_align)) + { + new_length = length; + best_need_align = need_align; + best_right_align = right_align; + } + } + } + gcc_assert (best_cost < INT_MAX); + if (best_need_align >= 0 && ctx->last_aligning_insn) + { + if (best_need_align != 0) + gcc_assert (ctx->something_changed); + + int aluid = ctx->last_aligning_insn; + unsigned offset = INSN_ADDRESSES (aluid) + insn_lengths[aluid]; + offset >>= ctx->parameters.align_unit_log; + offset += best_need_align; + ctx->request_align[uid_shuid[aluid]] = (offset & align_base_mask); + ctx->last_aligning_insn = 0; + } + else + gcc_assert (best_need_align <= 0); + if (can_align) + { + if (ctx->request_align[shuid] >= 0) + { + unsigned offset = insn_current_address + new_length; + offset >>= ctx->parameters.align_unit_log; + offset = ctx->request_align[shuid] - offset; + /* Fixme: might want to apply smaller mask if alignment + requirement has matching bitmask. */ + offset &= align_base_mask; + offset <<= ctx->parameters.align_unit_log; + new_length += offset; + } + ctx->last_aligning_insn = uid; + } + else if (ctx->request_align[shuid] >= 0) + { + ctx->something_changed = true; + ctx->request_align[shuid] = -1; + } + /* Since we are freezing out variants that have been disabled once, + cycles should generally not happen, so bump up iter_threshold. */ + iter_threshold = 7; + } + /* Stabilizing by length should generally happen for entire delay slot + sequences, so doing it inside should be a last resort. */ + if (seq_p) + iter_threshold = 9; + if (new_length != insn_lengths[uid]) + { + if (new_length < ctx->uid_lock_length[uid]) + new_length = ctx->uid_lock_length[uid]; + if (new_length == insn_lengths[uid]) + ; /* done here. */ + else if (ctx->niter < iter_threshold + || new_length > insn_lengths[uid]) + { + if (ctx->niter >= iter_threshold) + ctx->uid_lock_length[uid] = new_length, ctx->niter = 0; + insn_lengths[uid] = new_length; + ctx->something_changed = true; + } + else + new_length = insn_lengths[uid]; + } + return new_length; +} + /* Make a pass over all insns and compute their actual lengths by shortening any branches of variable length if possible. */ @@ -849,7 +1083,10 @@ shorten_branches (rtx first) int max_skip; #define MAX_CODE_ALIGN 16 rtx seq; - int something_changed = 1; + /* Indexed by uid, indicates if and how an insn length varies. + Bit 0: varying length instruction + Bit 1: aligned label + Bit 2: apply instruction variant selection. */ char *varying_length; rtx body; int uid; @@ -899,7 +1136,14 @@ shorten_branches (rtx first) INSN_SHUID (insn) = i++; if (INSN_P (insn)) - continue; + { + if (GET_CODE (PATTERN (insn)) == SEQUENCE) + { + insn = XVECEXP (PATTERN (insn), 0, 0); + INSN_SHUID (insn) = i++; + } + continue; + } if (LABEL_P (insn)) { @@ -964,14 +1208,39 @@ shorten_branches (rtx first) if (!HAVE_ATTR_length) return; + int max_shuid = INSN_SHUID (get_last_insn ()) + 1; + + shorten_branches_context_t ctx; + insn_length_parameters_t *parameters = &ctx.parameters; + memset (parameters, 0, sizeof *parameters); + ctx.variants = 0; + if (targetm.insn_length_parameters) + { + targetm.insn_length_parameters (parameters); + ctx.variants = XCNEWVEC (insn_length_variant_t, parameters->max_variants); + } + unsigned align_base = 1 << parameters->align_base_log; + ctx.shuid_variants + = sbitmap_vector_alloc (parameters->max_variants, max_shuid); + bitmap_vector_ones (ctx.shuid_variants, parameters->max_variants); + + ctx.request_align = 0; + if (parameters->align_base_log) + { + ctx.request_align = XNEWVEC (signed char, max_shuid); + gcc_assert ((1 << parameters->align_base_log) - 1 <= SCHAR_MAX); + memset (ctx.request_align, -1, max_shuid); + } + /* Allocate the rest of the arrays. */ - insn_lengths = XNEWVEC (int, max_uid); + insn_lengths = XCNEWVEC (int, max_uid); + ctx.uid_lock_length = XCNEWVEC (int, max_uid); insn_lengths_max_uid = max_uid; /* Syntax errors can lead to labels being outside of the main insn stream. Initialize insn_addresses, so that we get reproducible results. */ INSN_ADDRESSES_ALLOC (max_uid); - varying_length = XCNEWVEC (char, max_uid); + ctx.varying_length = varying_length = XCNEWVEC (char, max_uid); /* Initialize uid_align. We scan instructions from end to start, and keep in align_tab[n] the last seen insn @@ -1011,7 +1280,6 @@ shorten_branches (rtx first) label fields. */ int min_shuid = INSN_SHUID (get_insns ()) - 1; - int max_shuid = INSN_SHUID (get_last_insn ()) + 1; int rel; for (insn = first; insn != 0; insn = NEXT_INSN (insn)) @@ -1066,15 +1334,17 @@ shorten_branches (rtx first) /* Compute initial lengths, addresses, and varying flags for each insn. */ int (*length_fun) (rtx) = increasing ? insn_min_length : insn_default_length; + ctx.niter = increasing ? 0 : -1; + ctx.last_aligning_insn = 0; + gcc_assert (INSN_UID (get_insns ()) > ctx.last_aligning_insn); + bool target_p = true; for (insn_current_address = 0, insn = first; insn != 0; insn_current_address += insn_lengths[uid], insn = NEXT_INSN (insn)) { uid = INSN_UID (insn); - insn_lengths[uid] = 0; - if (LABEL_P (insn)) { int log = LABEL_TO_ALIGNMENT (insn); @@ -1084,16 +1354,19 @@ shorten_branches (rtx first) int new_address = (insn_current_address + align - 1) & -align; insn_lengths[uid] = new_address - insn_current_address; } + target_p = true; } INSN_ADDRESSES (uid) = insn_current_address + insn_lengths[uid]; if (NOTE_P (insn) || BARRIER_P (insn) - || LABEL_P (insn) || DEBUG_INSN_P(insn)) + || LABEL_P (insn) || DEBUG_INSN_P (insn)) continue; if (INSN_DELETED_P (insn)) continue; + int length = 0; + body = PATTERN (insn); if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC) { @@ -1101,13 +1374,15 @@ shorten_branches (rtx first) section. */ if (JUMP_TABLES_IN_TEXT_SECTION || readonly_data_section == text_section) - insn_lengths[uid] = (XVECLEN (body, - GET_CODE (body) == ADDR_DIFF_VEC) - * GET_MODE_SIZE (GET_MODE (body))); + length = (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC) + * GET_MODE_SIZE (GET_MODE (body))); /* Alignment is handled by ADDR_VEC_ALIGN. */ } else if (GET_CODE (body) == ASM_INPUT || asm_noperands (body) >= 0) - insn_lengths[uid] = asm_insn_count (body) * insn_default_length (insn); + { + length = asm_insn_count (body) * insn_default_length (insn); + target_p = false; + } else if (GET_CODE (body) == SEQUENCE) { int i; @@ -1135,50 +1410,81 @@ shorten_branches (rtx first) else inner_length = inner_length_fun (inner_insn); - insn_lengths[inner_uid] = inner_length; + inner_length = adjust_length (inner_insn, inner_length, true, + &ctx, target_p); if (const_delay_slots) { - if ((varying_length[inner_uid] - = insn_variable_length_p (inner_insn)) != 0) - varying_length[uid] = 1; + if (parameters->get_variants + && parameters->get_variants (inner_insn, inner_length, + true, target_p && i == 0, + ctx.variants) > 1) + varying_length[inner_uid] = 5; + else + varying_length[inner_uid] + = insn_variable_length_p (inner_insn); + varying_length[uid] |= (varying_length[inner_uid] & 1); INSN_ADDRESSES (inner_uid) = (insn_current_address + insn_lengths[uid]); } else varying_length[inner_uid] = 0; - insn_lengths[uid] += inner_length; + insn_lengths[inner_uid] = inner_length; + length += inner_length; + } + if (parameters->get_variants + && (parameters->get_variants (insn, length, false, target_p, + ctx.variants) + > 1)) + { + varying_length[uid] = 5; + for (i = 0; i < XVECLEN (body, 0); i++) + { + rtx inner_insn = XVECEXP (body, 0, i); + int inner_uid = INSN_UID (inner_insn); + varying_length[inner_uid] &= ~4; + } } + target_p = false; } else if (GET_CODE (body) != USE && GET_CODE (body) != CLOBBER) { - insn_lengths[uid] = length_fun (insn); - varying_length[uid] = insn_variable_length_p (insn); + length = length_fun (insn); + if (parameters->get_variants + && (parameters->get_variants (insn, length, false, target_p, + ctx.variants) + > 1)) + varying_length[uid] = 5; + else + varying_length[uid] = insn_variable_length_p (insn); + target_p = false; } - + /* If needed, do any adjustment. */ -#ifdef ADJUST_INSN_LENGTH - ADJUST_INSN_LENGTH (insn, insn_lengths[uid]); - if (insn_lengths[uid] < 0) - fatal_insn ("negative insn length", insn); -#endif + insn_lengths[uid] = adjust_length (insn, length, false, &ctx, target_p); + target_p + = (CALL_P (body) + || (GET_CODE (body) == SEQUENCE && CALL_P (XVECEXP (body, 0, 0)))); } /* Now loop over all the insns finding varying length insns. For each, get the current insn length. If it has changed, reflect the change. When nothing changes for a full pass, we are done. */ - while (something_changed) + ctx.something_changed = true; + while (ctx.something_changed) { - something_changed = 0; + if (increasing) + ctx.niter++; + + ctx.something_changed = false; + ctx.last_aligning_insn = 0; insn_current_align = MAX_CODE_ALIGN - 1; + target_p = true; for (insn_current_address = 0, insn = first; insn != 0; insn = NEXT_INSN (insn)) { int new_length; -#ifdef ADJUST_INSN_LENGTH - int tmp_length; -#endif int length_align; uid = INSN_UID (insn); @@ -1197,6 +1503,17 @@ shorten_branches (rtx first) else insn_lengths[uid] = 0; INSN_ADDRESSES (uid) = insn_current_address; + if (log > parameters->align_base_log && ctx.last_aligning_insn) + { + unsigned offset = insn_lengths[uid]; + offset -= INSN_ADDRESSES (ctx.last_aligning_insn); + offset -= insn_lengths[ctx.last_aligning_insn]; + offset >>= parameters->align_unit_log; + offset &= align_base - 1; + ctx.request_align[uid_shuid[ctx.last_aligning_insn]] = offset; + ctx.last_aligning_insn = 0; + } + target_p = true; continue; } @@ -1228,6 +1545,8 @@ shorten_branches (rtx first) flags = ADDR_DIFF_VEC_FLAGS (body); /* Try to find a known alignment for rel_lab. */ + /* FIXME: We seem to have lost the code that sets + varying_length & 2 for labels without max_skip. */ for (prev = rel_lab; prev && ! insn_lengths[INSN_UID (prev)] @@ -1314,7 +1633,7 @@ shorten_branches (rtx first) = (XVECLEN (body, 1) * GET_MODE_SIZE (GET_MODE (body))); insn_current_address += insn_lengths[uid]; if (insn_lengths[uid] != old_length) - something_changed = 1; + ctx.something_changed = true; } continue; @@ -1338,9 +1657,15 @@ shorten_branches (rtx first) insn_current_address += insn_lengths[inner_uid]; } + target_p = CALL_P (XVECEXP (body, 0, 0)); } else - insn_current_address += insn_lengths[uid]; + { + insn_current_address += insn_lengths[uid]; + if (BARRIER_P (insn)) + ctx.last_aligning_insn = 0; + target_p = CALL_P (insn); + } continue; } @@ -1364,49 +1689,42 @@ shorten_branches (rtx first) if (! varying_length[inner_uid]) inner_length = insn_lengths[inner_uid]; else - inner_length = insn_current_length (inner_insn); - - if (inner_length != insn_lengths[inner_uid]) { - if (!increasing || inner_length > insn_lengths[inner_uid]) - { - insn_lengths[inner_uid] = inner_length; - something_changed = 1; - } - else - inner_length = insn_lengths[inner_uid]; + inner_length = insn_current_length (inner_insn); + + inner_length + = adjust_length (inner_insn, inner_length, true, &ctx, + target_p && i == 0); } insn_current_address += inner_length; new_length += inner_length; } + /* Set INSN_CURRENT_ADDRESS back to the start of INSN, so that + we have the right context when we process INSN as a whole + in adjust_length later. */ + insn_current_address -= new_length; } else - { - new_length = insn_current_length (insn); - insn_current_address += new_length; - } + new_length = insn_current_length (insn); -#ifdef ADJUST_INSN_LENGTH /* If needed, do any adjustment. */ - tmp_length = new_length; - ADJUST_INSN_LENGTH (insn, new_length); - insn_current_address += (new_length - tmp_length); -#endif - - if (new_length != insn_lengths[uid] - && (!increasing || new_length > insn_lengths[uid])) - { - insn_lengths[uid] = new_length; - something_changed = 1; - } - else - insn_current_address += insn_lengths[uid] - new_length; + new_length = adjust_length (insn, new_length, false, &ctx, target_p); + target_p = (CALL_P (insn) + || (NONJUMP_INSN_P (insn) + && GET_CODE (body = PATTERN (insn)) == SEQUENCE + && CALL_P (XVECEXP (body, 0, 0)))); + insn_current_address += new_length; } /* For a non-optimizing compile, do only a single pass. */ if (!increasing) break; } + if (ctx.variants) + free (ctx.variants); + if (ctx.request_align) + free (ctx.request_align); + sbitmap_vector_free (ctx.shuid_variants); free (varying_length); } Index: target.def =================================================================== --- target.def (revision 2691) +++ target.def (working copy) @@ -2859,6 +2859,47 @@ HOOK_VECTOR_END (target_option) enum unwind_info_type, (void), default_debug_unwind_info) +DEFHOOK +(adjust_insn_length, + "Return an adjusted length for @var{insn}. @var{length} is the value that\ + has been calculated using the @code{length} instruction attribute. \ + @var{in_delay_sequence} if @var{insn} forms part of a delay sequence. \ + The default\ + implementation uses @code{ADJUST_INSN_LENGTH}, if defined.", + int, (rtx insn, int length, bool in_delay_sequence), + default_adjust_insn_length) + +DEFHOOK +(insn_length_parameters, + "Fill in values used for branch shortening. The type \ + @code{insn_length_parameters_t} is defined in @file{target-def.h}. \ + The main feature is the @code{get_variants} function. \ +@smallexample\n\ +int (*get_variants) (rtx insn, int length, bool sequence_p, bool target_p,\n\ + insn_length_variant_t *variants)\n\ +@end smallexample\n\ + For instructions where the ordinary monotonic branch shortening is\ + insufficeint to describe the alternatives, e.g. because there is alignemnt\ + involved, get_variants can provide two or more variants for the instruction. \ + The return value is the number of variants filled in, which must never exceed\ + the number filled in by @code{insn_length_parameters} in the\ + @var{max_variants} field. The set of variants for any given instruction\ + filled in should not vary during branch shortening, but rather unavailable\ + variants should be flagged with a @samp{false} @var{enabled} field. \ + This allows @code{shorten_branches} to keep track of varaints that have\ + been ever disabled in a previous iteration and keep them disabled, so\ + as to avoid infinite looping inside @code{shorten_branches}. \ + The @var{length} parameter provides the length calculated previously from\ + attributes. \ + @var{sequence_p} indicates if the instruction presented is inside a\ + @code{SEQUENCE}. Note, if you make @code{get_variants} provide variants\ + for an entire @code{SEQUENCE}, the @code{SEQUENCE} will henceforth be handled\ + as a single entity for branch shortening. \ + @var{target_p} indicates if the instruction is considered the target of \ + a branch, function call, or function return.", + void, (insn_length_parameters_t *insn_length_parameters), + NULL) + DEFHOOKPOD (atomic_test_and_set_trueval, "This value should be set if the result written by\ Index: target.h =================================================================== --- target.h (revision 2691) +++ target.h (working copy) @@ -165,6 +165,28 @@ enum vect_cost_model_location { vect_epilogue = 2 }; +typedef struct +{ + unsigned align_set; + /* Cost as a branch / call target or call return address. */ + int target_cost; + int fallthrough_cost; + int branch_cost; + int length; + /* 0 for not length sensitive, 1 for largest offset range, + 2 for next smaller etc. */ + unsigned length_sensitive : 8; + bool enabled; +} insn_length_variant_t; + +typedef struct insn_length_parameters_s +{ + int align_unit_log; + int align_base_log; + int max_variants; + int (*get_variants) (rtx, int, bool, bool, insn_length_variant_t *); +} insn_length_parameters_t; + /* The target structure. This holds all the backend hooks. */ #define DEFHOOKPOD(NAME, DOC, TYPE, INIT) TYPE NAME; #define DEFHOOK(NAME, DOC, TYPE, PARAMS, INIT) TYPE (* NAME) PARAMS; Index: targhooks.c =================================================================== --- targhooks.c (revision 2691) +++ targhooks.c (working copy) @@ -1540,4 +1540,18 @@ default_member_type_forces_blk (const_tr return false; } +/* Default version of adjust_insn_length. */ + +int +default_adjust_insn_length (rtx insn ATTRIBUTE_UNUSED, int length, + bool in_delay_sequence ATTRIBUTE_UNUSED) +{ +#ifdef ADJUST_INSN_LENGTH + if (!in_delay_sequence) + ADJUST_INSN_LENGTH (insn, length); +#endif + return length; +} + + #include "gt-targhooks.h" Index: targhooks.h =================================================================== --- targhooks.h (revision 2691) +++ targhooks.h (working copy) @@ -193,3 +193,4 @@ extern const char *default_pch_valid_p ( extern void default_asm_output_ident_directive (const char*); extern bool default_member_type_forces_blk (const_tree, enum machine_mode); +extern int default_adjust_insn_length (rtx, int, bool); Index: genattrtab.c =================================================================== --- genattrtab.c (revision 2691) +++ genattrtab.c (working copy) @@ -1551,7 +1551,7 @@ make_length_attrs (void) "*insn_current_length" }; static rtx (*const no_address_fn[]) (rtx) - = {identity_fn,identity_fn, zero_fn, zero_fn}; + = {identity_fn,identity_fn, zero_fn, identity_fn}; static rtx (*const address_fn[]) (rtx) = {max_fn, min_fn, one_fn, identity_fn}; size_t i;