123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846 |
- From a82c7d9030b67a6a76a5403d0e1641f9e42141ac Mon Sep 17 00:00:00 2001
- From: David Weatherford <weath@cadence.com>
- Date: Fri, 21 Mar 2014 11:53:42 +0000
- Subject: [PATCH] Add support to the Xtensa target for creating trampolines for
- out-of-range branches.
- * tc-xtensa.c (xtensa_check_frag_count, xtensa_create_trampoline_frag)
- (xtensa_maybe_create_trampoline_frag, init_trampoline_frag)
- (find_trampoline_seg, search_trampolines, get_best_trampoline)
- (check_and_update_trampolines, add_jump_to_trampoline)
- (dump_trampolines): New function.
- (md_parse_option): Add cases for --[no-]trampolines options.
- (md_assemble, finish_vinsn, xtensa_end): Add call to
- xtensa_check_frag_count.
- (xg_assemble_vliw_tokens): Add call to
- xtensa_maybe_create_trampoline_frag.
- (xtensa_relax_frag): Relax fragments with RELAX_TRAMPOLINE state.
- (relax_frag_immed): Relax jump instructions that cannot reach its
- target.
- * tc-xtensa.h (xtensa_relax_statesE::RELAX_TRAMPOLINE): New relax
- state.
- * as.texinfo: Document --[no-]trampolines command-line options.
- * c-xtensa.texi: Document trampolines relaxation and command line
- options.
- * frags.c (get_frag_count, clear_frag_count): New function.
- (frag_alloc): Increment totalfrags counter.
- * frags.h (get_frag_count, clear_frag_count): New function.
- * all.exp: Add test for trampoline relaxation.
- * trampoline.d: Trampoline relaxation expected dump.
- * trampoline.s: Trampoline relaxation test source.
- ---
- Backported from: a82c7d9030b67a6a76a5403d0e1641f9e42141ac
- Changes to Changelog files are dropped.
- gas/config/tc-xtensa.c | 558 +++++++++++++++++++++++++++++++++-
- gas/config/tc-xtensa.h | 5 +
- gas/frags.c | 15 +
- gas/frags.h | 3 +
- gas/testsuite/gas/xtensa/all.exp | 1 +
- gas/testsuite/gas/xtensa/trampoline.d | 26 ++
- gas/testsuite/gas/xtensa/trampoline.s | 21 ++
- 11 files changed, 753 insertions(+), 2 deletions(-)
- create mode 100644 gas/testsuite/gas/xtensa/trampoline.d
- create mode 100644 gas/testsuite/gas/xtensa/trampoline.s
- diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
- index fe8ec0f..ea23c96 100644
- --- a/gas/config/tc-xtensa.c
- +++ b/gas/config/tc-xtensa.c
- @@ -468,6 +468,12 @@ static void xtensa_set_frag_assembly_state (fragS *);
- static void finish_vinsn (vliw_insn *);
- static bfd_boolean emit_single_op (TInsn *);
- static int total_frag_text_expansion (fragS *);
- +static bfd_boolean use_trampolines = TRUE;
- +static void xtensa_check_frag_count (void);
- +static void xtensa_create_trampoline_frag (bfd_boolean);
- +static void xtensa_maybe_create_trampoline_frag (void);
- +struct trampoline_frag;
- +static int init_trampoline_frag (struct trampoline_frag *);
-
- /* Alignment Functions. */
-
- @@ -520,6 +526,7 @@ static void tinsn_from_chars (TInsn *, char *, int);
- static void tinsn_immed_from_frag (TInsn *, fragS *, int);
- static int get_num_stack_text_bytes (IStack *);
- static int get_num_stack_literal_bytes (IStack *);
- +static bfd_boolean tinsn_to_slotbuf (xtensa_format, int, TInsn *, xtensa_insnbuf);
-
- /* vliw_insn functions. */
-
- @@ -687,7 +694,10 @@ enum
- option_prefer_l32r,
- option_prefer_const16,
-
- - option_target_hardware
- + option_target_hardware,
- +
- + option_trampolines,
- + option_no_trampolines,
- };
-
- const char *md_shortopts = "";
- @@ -760,6 +770,9 @@ struct option md_longopts[] =
-
- { "target-hardware", required_argument, NULL, option_target_hardware },
-
- + { "trampolines", no_argument, NULL, option_trampolines },
- + { "no-trampolines", no_argument, NULL, option_no_trampolines },
- +
- { NULL, no_argument, NULL, 0 }
- };
-
- @@ -940,6 +953,14 @@ md_parse_option (int c, char *arg)
- directive_state[directive_transform] = FALSE;
- return 1;
-
- + case option_trampolines:
- + use_trampolines = TRUE;
- + return 1;
- +
- + case option_no_trampolines:
- + use_trampolines = FALSE;
- + return 1;
- +
- default:
- return 0;
- }
- @@ -963,7 +984,9 @@ Xtensa options:\n\
- flix bundles\n\
- --no-allow-flix neither allow hand-written nor generate\n\
- flix bundles\n\
- - --rename-section old=new Rename section 'old' to 'new'\n", stream);
- + --rename-section old=new Rename section 'old' to 'new'\n\
- + --[no-]trampolines [Do not] generate trampolines (jumps to jumps)\n\
- + when jumps do not reach their targets\n", stream);
- }
-
-
- @@ -5568,6 +5591,8 @@ md_assemble (char *str)
-
- /* We've just emitted a new instruction so clear the list of labels. */
- xtensa_clear_insn_labels ();
- +
- + xtensa_check_frag_count ();
- }
-
-
- @@ -6372,6 +6397,8 @@ finish_vinsn (vliw_insn *vinsn)
- xg_assemble_vliw_tokens (vinsn);
-
- xg_clear_vinsn (vinsn);
- +
- + xtensa_check_frag_count ();
- }
-
-
- @@ -7140,6 +7167,7 @@ xg_assemble_vliw_tokens (vliw_insn *vinsn)
- RELAX_UNREACHABLE,
- frag_now->fr_symbol, frag_now->fr_offset, NULL);
- xtensa_set_frag_assembly_state (frag_now);
- + xtensa_maybe_create_trampoline_frag ();
- }
- else if (is_branch && do_align_targets ())
- {
- @@ -7222,9 +7250,164 @@ xtensa_end (void)
- xtensa_sanity_check ();
-
- xtensa_add_config_info ();
- +
- + xtensa_check_frag_count ();
- +}
- +
- +
- +struct trampoline_frag
- +{
- + struct trampoline_frag *next;
- + bfd_boolean needs_jump_around;
- + fragS *fragP;
- + fixS *fixP;
- +};
- +
- +struct trampoline_seg
- +{
- + struct trampoline_seg *next;
- + asection *seg;
- + struct trampoline_frag trampoline_list;
- +};
- +
- +static struct trampoline_seg trampoline_seg_list;
- +#define J_RANGE (128 * 1024)
- +
- +static int unreachable_count = 0;
- +
- +
- +static void
- +xtensa_maybe_create_trampoline_frag (void)
- +{
- + if (!use_trampolines)
- + return;
- +
- + /* We create an area for possible trampolines every 10 unreachable frags.
- + These are preferred over the ones not preceded by an unreachable frag,
- + because we don't have to jump around them. This function is called after
- + each RELAX_UNREACHABLE frag is created. */
- +
- + if (++unreachable_count > 10)
- + {
- + xtensa_create_trampoline_frag (FALSE);
- + clear_frag_count ();
- + unreachable_count = 0;
- + }
- +}
- +
- +static void
- +xtensa_check_frag_count (void)
- +{
- + if (!use_trampolines || frag_now->tc_frag_data.is_no_transform)
- + return;
- +
- + /* We create an area for possible trampolines every 8000 frags or so. This
- + is an estimate based on the max range of a "j" insn (+/-128K) divided
- + by a typical frag byte count (16), minus a few for safety. This function
- + is called after each source line is processed. */
- +
- + if (get_frag_count () > 8000)
- + {
- + xtensa_create_trampoline_frag (TRUE);
- + clear_frag_count ();
- + unreachable_count = 0;
- + }
- +}
- +
- +static xtensa_insnbuf trampoline_buf = NULL;
- +static xtensa_insnbuf trampoline_slotbuf = NULL;
- +
- +#define TRAMPOLINE_FRAG_SIZE 3000
- +
- +static void
- +xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
- +{
- + /* Emit a frag where we can place intermediate jump instructions,
- + in case we need to jump farther than 128K bytes.
- + Each jump instruction takes three bytes.
- + We allocate enough for 1000 trampolines in each frag.
- + If that's not enough, oh well. */
- +
- + struct trampoline_seg *ts = trampoline_seg_list.next;
- + struct trampoline_frag *tf;
- + char *varP;
- + fragS *fragP;
- + int size = TRAMPOLINE_FRAG_SIZE;
- +
- + for ( ; ts; ts = ts->next)
- + {
- + if (ts->seg == now_seg)
- + break;
- + }
- +
- + if (ts == NULL)
- + {
- + ts = (struct trampoline_seg *)xcalloc(sizeof (struct trampoline_seg), 1);
- + ts->next = trampoline_seg_list.next;
- + trampoline_seg_list.next = ts;
- + ts->seg = now_seg;
- + }
- +
- + frag_wane (frag_now);
- + frag_new (0);
- + xtensa_set_frag_assembly_state (frag_now);
- + varP = frag_var (rs_machine_dependent, size, size, RELAX_TRAMPOLINE, NULL, 0, NULL);
- + fragP = (fragS *)(varP - SIZEOF_STRUCT_FRAG);
- + if (trampoline_buf == NULL)
- + {
- + trampoline_buf = xtensa_insnbuf_alloc (xtensa_default_isa);
- + trampoline_slotbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- + }
- + tf = (struct trampoline_frag *)xmalloc(sizeof (struct trampoline_frag));
- + tf->next = ts->trampoline_list.next;
- + ts->trampoline_list.next = tf;
- + tf->needs_jump_around = needs_jump_around;
- + tf->fragP = fragP;
- + tf->fixP = NULL;
- +}
- +
- +
- +static struct trampoline_seg *
- +find_trampoline_seg (asection *seg)
- +{
- + struct trampoline_seg *ts = trampoline_seg_list.next;
- +
- + for ( ; ts; ts = ts->next)
- + {
- + if (ts->seg == seg)
- + return ts;
- + }
- +
- + return NULL;
- }
-
-
- +void dump_trampolines (void);
- +
- +void
- +dump_trampolines (void)
- +{
- + struct trampoline_seg *ts = trampoline_seg_list.next;
- +
- + for ( ; ts; ts = ts->next)
- + {
- + asection *seg = ts->seg;
- +
- + if (seg == NULL)
- + continue;
- + fprintf(stderr, "SECTION %s\n", seg->name);
- + struct trampoline_frag *tf = ts->trampoline_list.next;
- + for ( ; tf; tf = tf->next)
- + {
- + if (tf->fragP == NULL)
- + continue;
- + fprintf(stderr, " 0x%08x: fix=%d, jump_around=%s\n",
- + (int)tf->fragP->fr_address, (int)tf->fragP->fr_fix,
- + tf->needs_jump_around ? "T" : "F");
- + }
- + }
- +}
- +
- static void
- xtensa_cleanup_align_frags (void)
- {
- @@ -8708,6 +8891,149 @@ xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
- new_stretch += relax_frag_for_align (fragP, stretch);
- break;
-
- + case RELAX_TRAMPOLINE:
- + if (fragP->tc_frag_data.relax_seen)
- + {
- + segment_info_type *seginfo = seg_info (now_seg);
- + fragS *fP; /* The out-of-range jump. */
- + fixS *fixP;
- +
- + /* Scan for jumps that will not reach. */
- + for (fixP = seginfo->fix_root; fixP ; fixP = fixP->fx_next)
- + {
- + symbolS *s = fixP->fx_addsy;
- + xtensa_opcode opcode;
- + int target;
- + int addr;
- + int delta;
- +
- + if (fixP->fx_r_type < BFD_RELOC_XTENSA_SLOT0_OP ||
- + fixP->fx_r_type > BFD_RELOC_XTENSA_SLOT14_OP)
- + continue;
- + xtensa_insnbuf_from_chars (isa, trampoline_buf,
- + (unsigned char *) fixP->fx_frag->fr_literal + fixP->fx_where,
- + 0);
- + fmt = xtensa_format_decode (isa, trampoline_buf);
- + gas_assert (fmt != XTENSA_UNDEFINED);
- + slot = fixP->tc_fix_data.slot;
- + xtensa_format_get_slot (isa, fmt, slot, trampoline_buf, trampoline_slotbuf);
- + opcode = xtensa_opcode_decode (isa, fmt, slot, trampoline_slotbuf);
- + if (opcode != xtensa_j_opcode)
- + continue;
- + target = S_GET_VALUE (s);
- + addr = fixP->fx_frag->fr_address;
- + delta = target - addr + stretch;
- + if (delta > J_RANGE || delta < -1 * J_RANGE)
- + { /* Found an out-of-range jump; scan the list of trampolines for the best match. */
- + struct trampoline_seg *ts = find_trampoline_seg (now_seg);
- + struct trampoline_frag *tf = ts->trampoline_list.next;
- + struct trampoline_frag *prev = &ts->trampoline_list;
- + int lower = (target < addr) ? target : addr;
- + int upper = (target > addr) ? target : addr;
- + int midpoint = lower + (upper - lower) / 2;
- +
- + if ((upper - lower) > 2 * J_RANGE)
- + {
- + /* One trampoline won't suffice; we need multiple jumps.
- + Jump to the trampoline that's farthest, but still in
- + range relative to the original "j" instruction. */
- + for ( ; tf; prev = tf, tf = tf->next )
- + {
- + int this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
- + int next_addr = (tf->next) ? tf->next->fragP->fr_address + tf->next->fragP->fr_fix : 0 ;
- +
- + if (addr == lower)
- + {
- + /* Forward jump. */
- + if (this_addr - addr < J_RANGE)
- + break;
- + }
- + else
- + {
- + /* Backward jump. */
- + if (next_addr == 0 || addr - next_addr > J_RANGE)
- + break;
- + }
- + }
- + }
- + else
- + {
- + struct trampoline_frag *best_tf = NULL;
- + int best_delta = 0;
- +
- + for ( ; tf; prev = tf, tf = tf->next )
- + {
- + int this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
- + int this_delta = abs (this_addr - midpoint);
- +
- + if (!best_tf || this_delta < best_delta)
- + {
- + best_tf = tf;
- + best_delta = this_delta;
- + }
- + }
- + tf = best_tf;
- + }
- + if (tf->fragP == fragP)
- + {
- + int trampaddr = fragP->fr_address + fragP->fr_fix;
- +
- + if (abs (addr - trampaddr) < J_RANGE)
- + { /* The trampoline is in range of original; fix it! */
- + fixS *newfixP;
- + int offset;
- + TInsn insn;
- + symbolS *lsym;
- +
- + new_stretch += init_trampoline_frag (tf);
- + offset = fragP->fr_fix; /* Where to assemble the j insn. */
- + lsym = fragP->fr_symbol;
- + fP = fixP->fx_frag;
- + /* Assemble a jump to the target label here. */
- + tinsn_init (&insn);
- + insn.insn_type = ITYPE_INSN;
- + insn.opcode = xtensa_j_opcode;
- + insn.ntok = 1;
- + set_expr_symbol_offset (&insn.tok[0], lsym, offset);
- + fmt = xg_get_single_format (xtensa_j_opcode);
- + tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
- + xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
- + xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)fragP->fr_literal + offset, 3);
- + fragP->fr_fix += 3;
- + fragP->fr_var -= 3;
- + /* Add a fix-up for the original j insn. */
- + newfixP = fix_new (fP, fixP->fx_where, fixP->fx_size, lsym, fragP->fr_fix - 3, TRUE, fixP->fx_r_type);
- + newfixP->fx_no_overflow = 1;
- + newfixP->tc_fix_data.X_add_symbol = lsym;
- + newfixP->tc_fix_data.X_add_number = offset;
- + newfixP->tc_fix_data.slot = slot;
- + /* Move the fix-up from the original j insn to this one. */
- + fixP->fx_frag = fragP;
- + fixP->fx_where = fragP->fr_fix - 3;
- + fixP->tc_fix_data.slot = 0;
- + /* Adjust the jump around this trampoline (if present). */
- + if (tf->fixP != NULL)
- + {
- + tf->fixP->fx_offset += 3;
- + }
- + new_stretch += 3;
- + fragP->tc_frag_data.relax_seen = FALSE; /* Need another pass. */
- + /* Do we have room for more? */
- + if (fragP->fr_var < 3)
- + { /* No, convert to fill. */
- + frag_wane (fragP);
- + fragP->fr_subtype = 0;
- + /* Remove from the trampoline_list. */
- + prev->next = tf->next;
- + break;
- + }
- + }
- + }
- + }
- + }
- + }
- + break;
- +
- default:
- as_bad (_("bad relaxation state"));
- }
- @@ -9146,6 +9472,200 @@ bytes_to_stretch (fragS *this_frag,
- }
-
-
- +static struct trampoline_frag *
- +search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
- +{
- + struct trampoline_seg *ts = find_trampoline_seg (now_seg);
- + struct trampoline_frag *tf = (ts) ? ts->trampoline_list.next : NULL;
- + struct trampoline_frag *best_tf = NULL;
- + int best_delta = 0;
- + int best_addr = 0;
- + symbolS *sym = tinsn->tok[0].X_add_symbol;
- + offsetT target = S_GET_VALUE (sym) + tinsn->tok[0].X_add_number;
- + offsetT addr = fragP->fr_address;
- + offsetT lower = (addr < target) ? addr : target;
- + offsetT upper = (addr > target) ? addr : target;
- + int delta = upper - lower;
- + offsetT midpoint = lower + delta / 2;
- + int this_delta = -1;
- + int this_addr = -1;
- +
- + if (delta > 2 * J_RANGE)
- + {
- + /* One trampoline won't do; we need multiple.
- + Choose the farthest trampoline that's still in range of the original
- + and let a later pass finish the job. */
- + for ( ; tf; tf = tf->next)
- + {
- + int next_addr = (tf->next) ? tf->next->fragP->fr_address + tf->next->fragP->fr_fix : 0;
- +
- + this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
- + if (lower == addr)
- + {
- + /* Forward jump. */
- + if (this_addr - addr < J_RANGE)
- + break;
- + }
- + else
- + {
- + /* Backward jump. */
- + if (next_addr == 0 || addr - next_addr > J_RANGE)
- + break;
- + }
- + if (abs (addr - this_addr) < J_RANGE)
- + return tf;
- +
- + return NULL;
- + }
- + }
- + for ( ; tf; tf = tf->next)
- + {
- + this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
- + this_delta = abs (this_addr - midpoint);
- + if (unreachable_only && tf->needs_jump_around)
- + continue;
- + if (!best_tf || this_delta < best_delta)
- + {
- + best_tf = tf;
- + best_delta = this_delta;
- + best_addr = this_addr;
- + }
- + }
- +
- + if (best_tf &&
- + best_delta < J_RANGE &&
- + abs(best_addr - lower) < J_RANGE &&
- + abs(best_addr - upper) < J_RANGE)
- + return best_tf;
- +
- + return NULL; /* No suitable trampoline found. */
- +}
- +
- +
- +static struct trampoline_frag *
- +get_best_trampoline (TInsn *tinsn, fragS *fragP)
- +{
- + struct trampoline_frag *tf = NULL;
- +
- + tf = search_trampolines (tinsn, fragP, TRUE); /* Try unreachable first. */
- +
- + if (tf == NULL)
- + tf = search_trampolines (tinsn, fragP, FALSE); /* Try ones needing a jump-around, too. */
- +
- + return tf;
- +}
- +
- +
- +static void
- +check_and_update_trampolines (void)
- +{
- + struct trampoline_seg *ts = find_trampoline_seg (now_seg);
- + struct trampoline_frag *tf = ts->trampoline_list.next;
- + struct trampoline_frag *prev = &ts->trampoline_list;
- +
- + for ( ; tf; prev = tf, tf = tf->next)
- + {
- + if (tf->fragP->fr_var < 3)
- + {
- + frag_wane (tf->fragP);
- + prev->next = tf->next;
- + tf->fragP = NULL;
- + }
- + }
- +}
- +
- +
- +static int
- +init_trampoline_frag (struct trampoline_frag *trampP)
- +{
- + fragS *fp = trampP->fragP;
- + int growth = 0;
- +
- + if (fp->fr_fix == 0)
- + {
- + symbolS *lsym;
- + char label[10 + 2 * sizeof(fp)];
- + sprintf (label, ".L0_TR_%p", fp);
- +
- + lsym = (symbolS *)local_symbol_make (label, now_seg, 0, fp);
- + fp->fr_symbol = lsym;
- + if (trampP->needs_jump_around)
- + {
- + /* Add a jump around this block of jumps, in case
- + control flows into this block. */
- + fixS *fixP;
- + TInsn insn;
- + xtensa_format fmt;
- + xtensa_isa isa = xtensa_default_isa;
- +
- + fp->tc_frag_data.is_insn = 1;
- + /* Assemble a jump insn. */
- + tinsn_init (&insn);
- + insn.insn_type = ITYPE_INSN;
- + insn.opcode = xtensa_j_opcode;
- + insn.ntok = 1;
- + set_expr_symbol_offset (&insn.tok[0], lsym, 3);
- + fmt = xg_get_single_format (xtensa_j_opcode);
- + tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
- + xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
- + xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)fp->fr_literal, 3);
- + fp->fr_fix += 3;
- + fp->fr_var -= 3;
- + growth = 3;
- + fixP = fix_new (fp, 0, 3, lsym, 3, TRUE, BFD_RELOC_XTENSA_SLOT0_OP);
- + trampP->fixP = fixP;
- + }
- + }
- + return growth;
- +}
- +
- +
- +static int
- +add_jump_to_trampoline (struct trampoline_frag *trampP, fragS *origfrag)
- +{
- + fragS *tramp = trampP->fragP;
- + fixS *fixP;
- + int offset = tramp->fr_fix; /* Where to assemble the j insn. */
- + TInsn insn;
- + symbolS *lsym;
- + symbolS *tsym;
- + int toffset;
- + xtensa_format fmt;
- + xtensa_isa isa = xtensa_default_isa;
- + int growth = 0;
- +
- + lsym = tramp->fr_symbol;
- + /* Assemble a jump to the target label in the trampoline frag. */
- + tsym = origfrag->tc_frag_data.slot_symbols[0];
- + toffset = origfrag-> tc_frag_data.slot_offsets[0];
- + tinsn_init (&insn);
- + insn.insn_type = ITYPE_INSN;
- + insn.opcode = xtensa_j_opcode;
- + insn.ntok = 1;
- + set_expr_symbol_offset (&insn.tok[0], tsym, toffset);
- + fmt = xg_get_single_format (xtensa_j_opcode);
- + tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
- + xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
- + xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)tramp->fr_literal + offset, 3);
- + tramp->fr_fix += 3;
- + tramp->fr_var -= 3;
- + growth = 3;
- + /* add a fix-up for the trampoline jump. */
- + fixP = fix_new (tramp, tramp->fr_fix - 3, 3, tsym, toffset, TRUE, BFD_RELOC_XTENSA_SLOT0_OP);
- + /* Modify the jump at the start of this trampoline to point past the newly-added jump. */
- + fixP = trampP->fixP;
- + if (fixP)
- + fixP->fx_offset += 3;
- + /* Modify the original j to point here. */
- + origfrag->tc_frag_data.slot_symbols[0] = lsym;
- + origfrag->tc_frag_data.slot_offsets[0] = tramp->fr_fix - 3;
- + /* If trampoline is full, remove it from the list. */
- + check_and_update_trampolines ();
- +
- + return growth;
- +}
- +
- +
- static long
- relax_frag_immed (segT segP,
- fragS *fragP,
- @@ -9284,6 +9804,37 @@ relax_frag_immed (segT segP,
- if (negatable_branch && istack.ninsn > 1)
- update_next_frag_state (fragP);
-
- + /* If last insn is a jump, and it cannot reach its target, try to find a trampoline. */
- + if (istack.ninsn > 2 &&
- + istack.insn[istack.ninsn - 1].insn_type == ITYPE_LABEL &&
- + istack.insn[istack.ninsn - 2].insn_type == ITYPE_INSN &&
- + istack.insn[istack.ninsn - 2].opcode == xtensa_j_opcode)
- + {
- + TInsn *jinsn = &istack.insn[istack.ninsn - 2];
- +
- + if (!xg_symbolic_immeds_fit (jinsn, segP, fragP, fragP->fr_offset, total_text_diff))
- + {
- + struct trampoline_frag *tf = get_best_trampoline (jinsn, fragP);
- +
- + if (tf)
- + {
- + this_text_diff += init_trampoline_frag (tf);
- + this_text_diff += add_jump_to_trampoline (tf, fragP);
- + }
- + else
- + {
- + /* If target symbol is undefined, assume it will reach once linked. */
- + expressionS *exp = &istack.insn[istack.ninsn - 2].tok[0];
- +
- + if (exp->X_op == O_symbol && S_IS_DEFINED (exp->X_add_symbol))
- + {
- + as_bad_where (fragP->fr_file, fragP->fr_line,
- + _("jump target out of range; no usable trampoline found"));
- + }
- + }
- + }
- + }
- +
- return this_text_diff;
- }
-
- @@ -9404,6 +9955,9 @@ md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
- else
- as_bad (_("invalid relaxation fragment result"));
- break;
- +
- + case RELAX_TRAMPOLINE:
- + break;
- }
-
- fragp->fr_var = 0;
- diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
- index 0bf1240..4672bc6 100644
- --- a/gas/config/tc-xtensa.h
- +++ b/gas/config/tc-xtensa.h
- @@ -180,6 +180,11 @@ enum xtensa_relax_statesE
- prevent the linker from changing the size of any frag between the
- section start and the org frag. */
-
- + RELAX_TRAMPOLINE,
- + /* Every few thousand frags, we insert one of these, just in case we may
- + need some space for a trampoline (jump to a jump) because the function
- + has gotten too big. If not needed, it disappears. */
- +
- RELAX_NONE
- };
-
- diff --git a/gas/frags.c b/gas/frags.c
- index 5f68480..e14099d 100644
- --- a/gas/frags.c
- +++ b/gas/frags.c
- @@ -24,6 +24,20 @@
-
- extern fragS zero_address_frag;
- extern fragS predefined_address_frag;
- +
- +static int totalfrags;
- +
- +int
- +get_frag_count (void)
- +{
- + return totalfrags;
- +}
- +
- +void
- +clear_frag_count (void)
- +{
- + totalfrags = 0;
- +}
-
- /* Initialization for frag routines. */
-
- @@ -70,6 +84,7 @@ frag_alloc (struct obstack *ob)
- ptr = (fragS *) obstack_alloc (ob, SIZEOF_STRUCT_FRAG);
- obstack_alignment_mask (ob) = oalign;
- memset (ptr, 0, SIZEOF_STRUCT_FRAG);
- + totalfrags++;
- return ptr;
- }
-
- diff --git a/gas/frags.h b/gas/frags.h
- index 319898f..2f9e1b5 100644
- --- a/gas/frags.h
- +++ b/gas/frags.h
- @@ -155,4 +155,7 @@ char *frag_var (relax_stateT type,
-
- bfd_boolean frag_offset_fixed_p (const fragS *, const fragS *, offsetT *);
-
- +int get_frag_count (void);
- +void clear_frag_count (void);
- +
- #endif /* FRAGS_H */
- diff --git a/gas/testsuite/gas/xtensa/all.exp b/gas/testsuite/gas/xtensa/all.exp
- index 2b2c294..3683b78 100644
- --- a/gas/testsuite/gas/xtensa/all.exp
- +++ b/gas/testsuite/gas/xtensa/all.exp
- @@ -98,6 +98,7 @@ if [istarget xtensa*-*-*] then {
- run_dump_test "pcrel"
- run_dump_test "weak-call"
- run_dump_test "jlong"
- + run_dump_test "trampoline"
- }
-
- if [info exists errorInfo] then {
- diff --git a/gas/testsuite/gas/xtensa/trampoline.d b/gas/testsuite/gas/xtensa/trampoline.d
- new file mode 100644
- index 0000000..b4f65dc
- --- /dev/null
- +++ b/gas/testsuite/gas/xtensa/trampoline.d
- @@ -0,0 +1,26 @@
- +#as:
- +#objdump: -d
- +#name: trampolines relaxation
- +
- +.*: +file format .*xtensa.*
- +#...
- +.*0:.*j.0x1194c
- +.*3:.*j.0x1194f
- +.*6:.*j.0x11952
- +.*9:.*j.0x1d4e4
- +#...
- +.*11949:.*j.0x11955
- +.*1194c:.*j.0x24a0e
- +.*1194f:.*j.0x24a0e
- +.*11952:.*j.0x24a11
- +#...
- +.*1d4e1:.*j.0x1d4e7
- +.*1d4e4:.*j.0x33462
- +#...
- +.*24a0e:.*j.0x24a0e
- +.*24a11:.*j.0x24a11
- +#...
- +.*3345f:.*ret
- +.*33462:.*j.0x49407
- +#...
- +.*49407:.*j.0x49407
- diff --git a/gas/testsuite/gas/xtensa/trampoline.s b/gas/testsuite/gas/xtensa/trampoline.s
- new file mode 100644
- index 0000000..259a3bb
- --- /dev/null
- +++ b/gas/testsuite/gas/xtensa/trampoline.s
- @@ -0,0 +1,21 @@
- + .text
- + j 1f
- + j 1f
- + j 2f
- + j 3f
- + .rep 25000
- +99:
- + and a2, a2, a3
- + bne a2, a3, 99b
- + .endr
- +1:
- + j 1b
- +2:
- + j 2b
- +
- + .rep 25000
- + and a2, a2, a3
- + _ret
- + .endr
- +3:
- + j 3b
- --
- 1.8.1.4
|