| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846 | From a82c7d9030b67a6a76a5403d0e1641f9e42141ac Mon Sep 17 00:00:00 2001From: David Weatherford <weath@cadence.com>Date: Fri, 21 Mar 2014 11:53:42 +0000Subject: [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: a82c7d9030b67a6a76a5403d0e1641f9e42141acChanges 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.sdiff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.cindex 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.hindex 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.cindex 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.hindex 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.expindex 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.dnew file mode 100644index 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.0x49407diff --git a/gas/testsuite/gas/xtensa/trampoline.s b/gas/testsuite/gas/xtensa/trampoline.snew file mode 100644index 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
 |