public inbox for binutils@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 1/2] xtensa: add --tramp-limit option to gas
  2015-08-12  0:54 [PATCH 0/2] xtensa gas improvements Max Filippov
@ 2015-08-12  0:54 ` Max Filippov
  2015-08-12 16:32   ` augustine.sterling
  2015-08-12  0:54 ` [PATCH 2/2] xtensa: add --auto-litpools option Max Filippov
  1 sibling, 1 reply; 6+ messages in thread
From: Max Filippov @ 2015-08-12  0:54 UTC (permalink / raw)
  To: binutils
  Cc: Sterling Augustine, David Weatherford, Marc Gauthier,
	linux-xtensa, Max Filippov

Hardcoded frag count limit for trampoline frag emission may not work for
all configurations. Make that limit configurable.

2015-08-12  David Weatherford  <weath@cadence.com>
gas/
	* config/tc-xtensa.c (trampoline_frag_limit): New static
	variable.
	(option_trampoline_limit): New option enum identifier.
	(md_longopts): New entry for tramp-limit option.
	(md_parse_option): Handle option_trampoline_limit.
	(md_show_usage): Help text for --tramp-limit option.
	(xtensa_check_frag_count): Fix comment, replace hard-coded limit
	with trampoline_frag_limit variable.
---
 gas/config/tc-xtensa.c | 31 +++++++++++++++++++++++++------
 1 file changed, 25 insertions(+), 6 deletions(-)

diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index 18307c1..f818ae5 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -469,6 +469,7 @@ 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 int trampoline_frag_limit = 8000;
 static void xtensa_check_frag_count (void);
 static void xtensa_create_trampoline_frag (bfd_boolean);
 static void xtensa_maybe_create_trampoline_frag (void);
@@ -698,6 +699,7 @@ enum
 
   option_trampolines,
   option_no_trampolines,
+  option_trampoline_limit,
 };
 
 const char *md_shortopts = "";
@@ -772,6 +774,7 @@ struct option md_longopts[] =
 
   { "trampolines", no_argument, NULL, option_trampolines },
   { "no-trampolines", no_argument, NULL, option_no_trampolines },
+  { "tramp-limit", required_argument, NULL, option_trampoline_limit },
 
   { NULL, no_argument, NULL, 0 }
 };
@@ -961,6 +964,21 @@ md_parse_option (int c, char *arg)
       use_trampolines = FALSE;
       return 1;
 
+    case option_trampoline_limit:
+      {
+	int value = 0;
+	if (*arg == 0 || *arg == '-')
+	  as_fatal (_("invalid tramp-limit argument"));
+	value = strtol (arg, &arg, 10);
+	if (*arg != 0)
+	  as_fatal (_("invalid tramp-limit argument"));
+	if (value < 100 || value > 10000)
+	  as_fatal (_("invalid tramp-limit argument (range is 100-10000)"));
+	trampoline_frag_limit = value;
+	use_trampolines = TRUE;
+	return 1;
+      }
+
     default:
       return 0;
     }
@@ -986,7 +1004,10 @@ Xtensa options:\n\
                           flix bundles\n\
   --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);
+                          when jumps do not reach their targets\n\
+  --tramp-limit=<value>   (default 8000, range 100-10000) Maximum number of\n\
+                          blocks of instructions to emit between trampoline\n\
+                          locations; implies --trampolines flag\n", stream);
 }
 
 \f
@@ -7303,12 +7324,10 @@ 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.  */
+  /* We create an area for possible trampolines every 8000 frags or so.
+     This function is called after each source line is processed.  */
 
-  if (get_frag_count () > 8000)
+  if (get_frag_count () > trampoline_frag_limit)
     {
       xtensa_create_trampoline_frag (TRUE);
       clear_frag_count ();
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 2/2] xtensa: add --auto-litpools option
  2015-08-12  0:54 [PATCH 0/2] xtensa gas improvements Max Filippov
  2015-08-12  0:54 ` [PATCH 1/2] xtensa: add --tramp-limit option to gas Max Filippov
@ 2015-08-12  0:54 ` Max Filippov
  2015-08-12 16:40   ` augustine.sterling
  1 sibling, 1 reply; 6+ messages in thread
From: Max Filippov @ 2015-08-12  0:54 UTC (permalink / raw)
  To: binutils
  Cc: Sterling Augustine, David Weatherford, Marc Gauthier,
	linux-xtensa, Max Filippov

Auto-litpools is the automated version of text-section-literals: literal
pool candidate frags are planted every N frags and during relaxation
they are turned into actual literal pools where literals are moved to
become reachable for their first reference by L32R instruction.

2015-08-11  David Weatherford  <weath@cadence.com>
gas/
	* config/tc-xtensa.c (struct litpool_frag, struct litpool_seg):
	New structures.
	(xtensa_maybe_create_literal_pool_frag): New function.
	(litpool_seg_list, auto_litpools, auto_litpool_limit)
	(litpool_buf, litpool_slotbuf): New static variables.
	(option_auto_litpools, option_no_auto_litpools)
	(option_auto_litpool_limit): New enum identifiers.
	(md_longopts): Add entries for auto-litpools, no-auto-litpools
	and auto-litpool-limit.
	(md_parse_option): Handle option_auto_litpools,
	option_no_auto_litpools and option_auto_litpool_limit.
	(md_show_usage): Add help for --[no-]auto-litpools and
	--auto-litpool-limit.
	(xtensa_mark_literal_pool_location): Record a place for literal
	pool with a call to xtensa_maybe_create_literal_pool_frag.
	(get_literal_pool_location): Find highest priority literal pool
	or convert candidate to literal pool when auto-litpools are used.
	(xg_assemble_vliw_tokens): Create literal pool after jump
	instruction.
	(xtensa_check_frag_count): Create candidate literal pool every
	auto_litpool_limit frags.
	(xtensa_relax_frag): Add jump around literals to non-empty
	literal pool.
	(xtensa_move_literals): Estimate literal pool addresses and move
	unreachable literals closer to their users, converting candidate
	to literal pool if needed.
	(xtensa_switch_to_non_abs_literal_fragment): Only emit error
	about missing .literal_position in case auto-litpools are not
	used.
	* config/tc-xtensa.h (xtensa_relax_statesE): New relaxation
	state: RELAX_LITERAL_POOL_CANDIDATE_BEGIN.
	* doc/as.texinfo (Xtensa options):  Document --auto-litpools and
	--no-auto-litpools options.
	* doc/c-xtensa.texi (Xtensa options): Likewise.

2015-08-11  Max Filippov  <jcmvbkbc@gmail.com>
gas/testsuite/
	* gas/xtensa/all.exp: Add auto-litpools to the list of xtensa
	tests.
	* gas/xtensa/auto-litpools.s: New file: auto-litpools test.
	* gas/xtensa/auto-litpools.s: New file: auto-litpools test
	result pattern.
---
 gas/config/tc-xtensa.c                   | 430 ++++++++++++++++++++++++++++++-
 gas/config/tc-xtensa.h                   |   1 +
 gas/doc/as.texinfo                       |   3 +-
 gas/doc/c-xtensa.texi                    |  23 +-
 gas/testsuite/gas/xtensa/all.exp         |   1 +
 gas/testsuite/gas/xtensa/auto-litpools.d |  12 +
 gas/testsuite/gas/xtensa/auto-litpools.s |  13 +
 7 files changed, 476 insertions(+), 7 deletions(-)
 create mode 100644 gas/testsuite/gas/xtensa/auto-litpools.d
 create mode 100644 gas/testsuite/gas/xtensa/auto-litpools.s

diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index f818ae5..c716066 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -440,6 +440,27 @@ bfd_boolean directive_state[] =
 #endif
 };
 
+/* A circular list of all potential and actual literal pool locations
+   in a segment.  */
+struct litpool_frag {
+  struct litpool_frag *next;
+  struct litpool_frag *prev;
+  fragS *fragP;
+  addressT addr;
+  short priority; /* 1, 2, or 3 -- 1 is highest  */
+  short original_priority;
+};
+
+/* Map a segment to its litpool_frag list.  */
+struct litpool_seg {
+  struct litpool_seg *next;
+  asection *seg;
+  struct litpool_frag frag_list;
+  int frag_count; /* since last litpool location  */
+};
+
+static struct litpool_seg litpool_seg_list;
+
 
 /* Directive functions.  */
 
@@ -475,6 +496,9 @@ 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 *);
+static void xtensa_maybe_create_literal_pool_frag (bfd_boolean, bfd_boolean);
+static bfd_boolean auto_litpools = FALSE;
+static int auto_litpool_limit = 10000;
 
 /* Alignment Functions.  */
 
@@ -700,6 +724,10 @@ enum
   option_trampolines,
   option_no_trampolines,
   option_trampoline_limit,
+
+  option_auto_litpools,
+  option_no_auto_litpools,
+  option_auto_litpool_limit,
 };
 
 const char *md_shortopts = "";
@@ -776,6 +804,10 @@ struct option md_longopts[] =
   { "no-trampolines", no_argument, NULL, option_no_trampolines },
   { "tramp-limit", required_argument, NULL, option_trampoline_limit },
 
+  { "auto-litpools", no_argument, NULL, option_auto_litpools },
+  { "no-auto-litpools", no_argument, NULL, option_no_auto_litpools },
+  { "auto-litpool-limit", required_argument, NULL, option_auto_litpool_limit },
+
   { NULL, no_argument, NULL, 0 }
 };
 
@@ -979,6 +1011,34 @@ md_parse_option (int c, char *arg)
 	return 1;
       }
 
+    case option_auto_litpools:
+      auto_litpools = TRUE;
+      use_literal_section = FALSE;
+      return 1;
+
+    case option_no_auto_litpools:
+      auto_litpools = FALSE;
+      auto_litpool_limit = -1;
+      return 1;
+
+    case option_auto_litpool_limit:
+      {
+	int value = 0;
+	if (auto_litpool_limit < 0)
+	  as_fatal (_("no-auto-litpools is incompatible with auto-litpool-limit"));
+	if (*arg == 0 || *arg == '-')
+	  as_fatal (_("invalid auto-litpool-limit argument"));
+	value = strtol (arg, &arg, 10);
+	if (*arg != 0)
+	  as_fatal (_("invalid auto-litpool-limit argument"));
+	if (value < 100 || value > 10000)
+	  as_fatal (_("invalid auto-litpool-limit argument (range is 100-10000)"));
+	auto_litpool_limit = value;
+	auto_litpools = TRUE;
+	use_literal_section = FALSE;
+	return 1;
+      }
+
     default:
       return 0;
     }
@@ -1007,7 +1067,12 @@ Xtensa options:\n\
                           when jumps do not reach their targets\n\
   --tramp-limit=<value>   (default 8000, range 100-10000) Maximum number of\n\
                           blocks of instructions to emit between trampoline\n\
-                          locations; implies --trampolines flag\n", stream);
+                          locations; implies --trampolines flag\n\
+  --[no-]auto-litpools    [Do not] automatically create literal pools\n\
+  --auto-litpool-limit=<value>\n\
+                          (range 100-10000) Maximum number of blocks of\n\
+                          instructions to emit between literal pool\n\
+                          locations; implies --auto-litpools flag\n", stream);
 }
 
 \f
@@ -4749,6 +4814,8 @@ xtensa_mark_literal_pool_location (void)
   pool_location = frag_now;
   frag_now->tc_frag_data.lit_frchain = frchain_now;
   frag_now->tc_frag_data.literal_frag = frag_now;
+  /* Just record this frag.  */
+  xtensa_maybe_create_literal_pool_frag (FALSE, FALSE);
   frag_variant (rs_machine_dependent, 0, 0,
 		RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
   xtensa_set_frag_assembly_state (frag_now);
@@ -4853,6 +4920,31 @@ get_expanded_loop_offset (xtensa_opcode opcode)
 static fragS *
 get_literal_pool_location (segT seg)
 {
+  struct litpool_seg *lps = litpool_seg_list.next;
+  struct litpool_frag *lpf;
+  for ( ; lps && lps->seg->id != seg->id; lps = lps->next)
+    ;
+  if (lps)
+    {
+      for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
+	{ /* Skip "candidates" for now.  */
+	  if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN &&
+	      lpf->priority == 1)
+	    return lpf->fragP;
+	}
+      /* Must convert a lower-priority pool.  */
+      for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
+	{
+	  if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN)
+	    return lpf->fragP;
+	}
+      /* Still no match -- try for a low priority pool.  */
+      for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
+	{
+	  if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
+	    return lpf->fragP;
+	}
+    }
   return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
 }
 
@@ -7119,6 +7211,11 @@ xg_assemble_vliw_tokens (vliw_insn *vinsn)
       frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
       frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
       frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
+      if (tinsn->opcode == xtensa_l32r_opcode)
+	{
+	  frag_now->tc_frag_data.literal_frags[slot] =
+		  tinsn->tok[1].X_add_symbol->sy_frag;
+	}
       if (tinsn->literal_space != 0)
 	xg_assemble_literal_space (tinsn->literal_space, slot);
       frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
@@ -7191,6 +7288,8 @@ xg_assemble_vliw_tokens (vliw_insn *vinsn)
 		    frag_now->fr_symbol, frag_now->fr_offset, NULL);
 	  xtensa_set_frag_assembly_state (frag_now);
 	  xtensa_maybe_create_trampoline_frag ();
+	  /* Always create one here.  */
+	  xtensa_maybe_create_literal_pool_frag (TRUE, FALSE);
 	}
       else if (is_branch && do_align_targets ())
 	{
@@ -7333,11 +7432,18 @@ xtensa_check_frag_count (void)
       clear_frag_count ();
       unreachable_count = 0;
     }
+
+  /* We create an area for a possible literal pool every N (default 5000)
+     frags or so.  */
+  xtensa_maybe_create_literal_pool_frag (TRUE, TRUE);
 }
 
 static xtensa_insnbuf trampoline_buf = NULL;
 static xtensa_insnbuf trampoline_slotbuf = NULL;
 
+static xtensa_insnbuf litpool_buf = NULL;
+static xtensa_insnbuf litpool_slotbuf = NULL;
+
 #define TRAMPOLINE_FRAG_SIZE 3000
 
 static void
@@ -7429,6 +7535,135 @@ dump_trampolines (void)
     }
 }
 
+static void dump_litpools (void) __attribute__ ((unused));
+
+static void
+dump_litpools (void)
+{
+  struct litpool_seg *lps = litpool_seg_list.next;
+  struct litpool_frag *lpf;
+
+  for ( ; lps ; lps = lps->next )
+    {
+      printf("litpool seg %s\n", lps->seg->name);
+      for ( lpf = lps->frag_list.next; lpf->fragP; lpf = lpf->next )
+	{
+	  fragS *litfrag = lpf->fragP->fr_next;
+	  int count = 0;
+	  while (litfrag && litfrag->fr_subtype != RELAX_LITERAL_POOL_END)
+	    {
+	      if (litfrag->fr_fix == 4)
+		count++;
+	      litfrag = litfrag->fr_next;
+	    }
+	  printf("   %ld <%d:%d> (%d) [%d]: ",
+		 lpf->addr, lpf->priority, lpf->original_priority,
+		 lpf->fragP->fr_line, count);
+	  //dump_frag(lpf->fragP);
+	}
+    }
+}
+
+static void
+xtensa_maybe_create_literal_pool_frag (bfd_boolean create,
+				       bfd_boolean only_if_needed)
+{
+  struct litpool_seg *lps = litpool_seg_list.next;
+  fragS *fragP;
+  struct litpool_frag *lpf;
+  bfd_boolean needed = FALSE;
+
+  if (use_literal_section || !auto_litpools)
+    return;
+
+  for ( ; lps ; lps = lps->next )
+    {
+      if (lps->seg == now_seg)
+	break;
+    }
+
+  if (lps == NULL)
+    {
+      lps = (struct litpool_seg *)xcalloc (sizeof (struct litpool_seg), 1);
+      lps->next = litpool_seg_list.next;
+      litpool_seg_list.next = lps;
+      lps->seg = now_seg;
+      lps->frag_list.next = &lps->frag_list;
+      lps->frag_list.prev = &lps->frag_list;
+    }
+
+  lps->frag_count++;
+
+  if (create)
+    {
+      if (only_if_needed)
+	{
+	  if (past_xtensa_end || !use_transform() ||
+	      frag_now->tc_frag_data.is_no_transform)
+	    {
+	      return;
+	    }
+	  if (auto_litpool_limit <= 0)
+	    {
+	      /* Don't create a litpool based only on frag count.  */
+	      return;
+	    }
+	  else if (lps->frag_count > auto_litpool_limit)
+	    {
+	      needed = TRUE;
+	    }
+	  else
+	    {
+	      return;
+	    }
+	}
+      else
+	{
+	  needed = TRUE;
+	}
+    }
+
+  if (needed)
+    {
+      int size = (only_if_needed) ? 3 : 0; /* Space for a "j" insn.  */
+      /* Create a potential site for a literal pool.  */
+      frag_wane (frag_now);
+      frag_new (0);
+      xtensa_set_frag_assembly_state (frag_now);
+      fragP = frag_now;
+      fragP->tc_frag_data.lit_frchain = frchain_now;
+      fragP->tc_frag_data.literal_frag = fragP;
+      frag_var (rs_machine_dependent, size, size,
+		    (only_if_needed) ?
+		        RELAX_LITERAL_POOL_CANDIDATE_BEGIN :
+		        RELAX_LITERAL_POOL_BEGIN,
+		    NULL, 0, NULL);
+      frag_now->tc_frag_data.lit_seg = now_seg;
+      frag_variant (rs_machine_dependent, 0, 0,
+		    RELAX_LITERAL_POOL_END, NULL, 0, NULL);
+      xtensa_set_frag_assembly_state (frag_now);
+    }
+  else
+    {
+      /* RELAX_LITERAL_POOL_BEGIN frag is being created;
+	 just record it here.  */
+      fragP = frag_now;
+    }
+
+  lpf = (struct litpool_frag *)xmalloc(sizeof (struct litpool_frag));
+  /* Insert at tail of circular list.  */
+  lpf->addr = 0;
+  lps->frag_list.prev->next = lpf;
+  lpf->next = &lps->frag_list;
+  lpf->prev = lps->frag_list.prev;
+  lps->frag_list.prev = lpf;
+  lpf->fragP = fragP;
+  lpf->priority = (needed) ? (only_if_needed) ? 3 : 2 : 1;
+  lpf->original_priority = lpf->priority;
+
+  lps->frag_count = 0;
+}
+
 static void
 xtensa_cleanup_align_frags (void)
 {
@@ -9048,7 +9283,41 @@ xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
       break;
 
     case RELAX_LITERAL_POOL_BEGIN:
+      if (fragP->fr_var != 0)
+	{
+	  /* We have a converted "candidate" literal pool;
+	     assemble a jump around it.  */
+	  TInsn insn;
+	  if (!litpool_slotbuf)
+	    {
+	      litpool_buf = xtensa_insnbuf_alloc (isa);
+	      litpool_slotbuf = xtensa_insnbuf_alloc (isa);
+	    }
+	  new_stretch += 3;
+	  fragP->tc_frag_data.relax_seen = FALSE; /* Need another pass.  */
+	  fragP->tc_frag_data.is_insn = TRUE;
+	  tinsn_init (&insn);
+	  insn.insn_type = ITYPE_INSN;
+	  insn.opcode = xtensa_j_opcode;
+	  insn.ntok = 1;
+	  set_expr_symbol_offset (&insn.tok[0], fragP->fr_symbol,
+				  fragP->fr_fix);
+	  fmt = xg_get_single_format (xtensa_j_opcode);
+	  tinsn_to_slotbuf (fmt, 0, &insn, litpool_slotbuf);
+	  xtensa_format_set_slot (isa, fmt, 0, litpool_buf, litpool_slotbuf);
+	  xtensa_insnbuf_to_chars (isa, litpool_buf,
+				   (unsigned char *)fragP->fr_literal +
+				   fragP->fr_fix, 3);
+	  fragP->fr_fix += 3;
+	  fragP->fr_var -= 3;
+	  /* Add a fix-up.  */
+	  fix_new (fragP, 0, 3, fragP->fr_symbol, 0, TRUE,
+		   BFD_RELOC_XTENSA_SLOT0_OP);
+	}
+      break;
+
     case RELAX_LITERAL_POOL_END:
+    case RELAX_LITERAL_POOL_CANDIDATE_BEGIN:
     case RELAX_MAYBE_UNREACHABLE:
     case RELAX_MAYBE_DESIRE_ALIGN:
       /* No relaxation required.  */
@@ -10808,12 +11077,115 @@ xtensa_move_literals (void)
   segT dest_seg;
   fixS *fix, *next_fix, **fix_splice;
   sym_list *lit;
+  struct litpool_seg *lps;
 
   mark_literal_frags (literal_head->next);
 
   if (use_literal_section)
     return;
 
+  /* Assign addresses (rough estimates) to the potential literal pool locations
+     and create new ones if the gaps are too large.  */
+
+  for (lps = litpool_seg_list.next; lps; lps = lps->next)
+    {
+      frchainS *frchP = seg_info (lps->seg)->frchainP;
+      struct litpool_frag *lpf = lps->frag_list.next;
+      addressT addr = 0;
+
+      for ( ; frchP; frchP = frchP->frch_next)
+	{
+	  fragS *fragP;
+	  for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+	    {
+	      if (lpf && fragP == lpf->fragP)
+		{
+		  gas_assert(fragP->fr_type == rs_machine_dependent &&
+			     (fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN ||
+			      fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN));
+		  /* Found a litpool location.  */
+		  lpf->addr = addr;
+		  lpf = lpf->next;
+		}
+	      if (fragP->fr_type == rs_machine_dependent &&
+		  fragP->fr_subtype == RELAX_SLOTS)
+		{
+		  int slot;
+		  for (slot = 0; slot < MAX_SLOTS; slot++)
+		    {
+		      if (fragP->tc_frag_data.literal_frags[slot])
+			{
+			  /* L32R; point its literal to the nearest litpool
+			     preferring non-"candidate" positions to avoid
+			     the jump-around.  */
+			  fragS *litfrag = fragP->tc_frag_data.literal_frags[slot];
+			  struct litpool_frag *lp = lpf->prev;
+			  if (!lp->fragP)
+			    {
+			      break;
+			    }
+			  while (lp->fragP->fr_subtype ==
+				 RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
+			    {
+			      lp = lp->prev;
+			      if (lp->fragP == NULL)
+				{
+				  /* End of list; have to bite the bullet.
+				     Take the nearest.  */
+				  lp = lpf->prev;
+				  break;
+				}
+			      /* Does it (conservatively) reach?  */
+			      if (addr - lp->addr <= 128 * 1024)
+				{
+				  if (lp->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN)
+				    {
+				      /* Found a good one.  */
+				      break;
+				    }
+				  else if (lp->prev->fragP &&
+					   addr - lp->prev->addr > 128 * 1024)
+				    {
+				      /* This is still a "candidate" but the next one
+				         will be too far away, so revert to the nearest
+					 one, convert it and add the jump around.  */
+				      fragS *poolbeg;
+				      fragS *poolend;
+				      symbolS *lsym;
+				      char label[10 + 2 * sizeof (fragS *)];
+				      lp = lpf->prev;
+				      poolbeg = lp->fragP;
+				      lp->priority = 1;
+				      poolbeg->fr_subtype = RELAX_LITERAL_POOL_BEGIN;
+				      poolend = poolbeg->fr_next;
+				      gas_assert (poolend->fr_type == rs_machine_dependent &&
+						  poolend->fr_subtype == RELAX_LITERAL_POOL_END);
+				      /* Create a local symbol pointing to the
+				         end of the pool.  */
+				      sprintf (label, ".L0_LT_%p", poolbeg);
+				      lsym = (symbolS *)local_symbol_make (label, lps->seg,
+									   0, poolend);
+				      poolbeg->fr_symbol = lsym;
+				      /* Rest is done in xtensa_relax_frag.  */
+				    }
+				}
+			    }
+			  if (! litfrag->tc_frag_data.literal_frag)
+			    {
+			      /* Take earliest use of this literal to avoid
+				 forward refs.  */
+			      litfrag->tc_frag_data.literal_frag = lp->fragP;
+			    }
+			}
+		    }
+		}
+	      addr += fragP->fr_fix;
+	      if (fragP->fr_type == rs_fill)
+		addr += fragP->fr_offset;
+	    }
+	}
+    }
+
   for (segment = literal_head->next; segment; segment = segment->next)
     {
       /* Keep the literals for .init and .fini in separate sections.  */
@@ -10858,9 +11230,6 @@ xtensa_move_literals (void)
       while (search_frag != frag_now)
 	{
 	  next_frag = search_frag->fr_next;
-
-	  /* First, move the frag out of the literal section and
-	     to the appropriate place.  */
 	  if (search_frag->tc_frag_data.literal_frag)
 	    {
 	      literal_pool = search_frag->tc_frag_data.literal_frag;
@@ -10868,8 +11237,56 @@ xtensa_move_literals (void)
 	      frchain_to = literal_pool->tc_frag_data.lit_frchain;
 	      gas_assert (frchain_to);
 	    }
+
+	  if (search_frag->fr_type == rs_fill && search_frag->fr_fix == 0)
+	    {
+	      /* Skip empty fill frags.  */
+	      *frag_splice = next_frag;
+	      search_frag = next_frag;
+	      continue;
+	    }
+
+	  if (search_frag->fr_type == rs_align)
+	    {
+	      /* Skip alignment frags, because the pool as a whole will be
+	         aligned if used, and we don't want to force alignment if the
+		 pool is unused.  */
+	      *frag_splice = next_frag;
+	      search_frag = next_frag;
+	      continue;
+	    }
+
+	  /* First, move the frag out of the literal section and
+	     to the appropriate place.  */
+
+	  /* Insert an aligmnent frag at start of pool.  */
+	  if (literal_pool->fr_next->fr_type == rs_machine_dependent &&
+	      literal_pool->fr_next->fr_subtype == RELAX_LITERAL_POOL_END)
+	    {
+	      segT pool_seg = literal_pool->fr_next->tc_frag_data.lit_seg;
+	      emit_state prev_state;
+	      fragS *prev_frag;
+	      fragS *align_frag;
+	      xtensa_switch_section_emit_state (&prev_state, pool_seg, 0);
+	      prev_frag = frag_now;
+	      frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+	      align_frag = frag_now;
+	      frag_align (2, 0, 0);
+	      /* Splice it into the right place.  */
+	      prev_frag->fr_next = align_frag->fr_next;
+	      align_frag->fr_next = literal_pool->fr_next;
+	      literal_pool->fr_next = align_frag;
+	      /* Insert after this one.  */
+	      literal_pool->tc_frag_data.literal_frag = align_frag;
+	      xtensa_restore_emit_state (&prev_state);
+	    }
 	  insert_after = literal_pool->tc_frag_data.literal_frag;
 	  dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
+	  /* Skip align frag.  */
+	  if (insert_after->fr_next->fr_type == rs_align)
+	    {
+	      insert_after = insert_after->fr_next;
+	    }
 
 	  *frag_splice = next_frag;
 	  search_frag->fr_next = insert_after->fr_next;
@@ -11033,7 +11450,10 @@ xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
       && !recursive
       && !is_init && ! is_fini)
     {
-      as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
+      if (!auto_litpools)
+	{
+	  as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
+	}
 
       /* When we mark a literal pool location, we want to put a frag in
 	 the literal pool that points to it.  But to do that, we want to
diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
index c6c7699..6c0d131 100644
--- a/gas/config/tc-xtensa.h
+++ b/gas/config/tc-xtensa.h
@@ -124,6 +124,7 @@ enum xtensa_relax_statesE
 
   RELAX_LITERAL_POOL_BEGIN,
   RELAX_LITERAL_POOL_END,
+  RELAX_LITERAL_POOL_CANDIDATE_BEGIN,
   /* Technically these are not relaxations at all but mark a location
      to store literals later.  Note that fr_var stores the frchain for
      BEGIN frags and fr_var stores now_seg for END frags.  */
diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
index 5710e1c..0b27c54 100644
--- a/gas/doc/as.texinfo
+++ b/gas/doc/as.texinfo
@@ -552,7 +552,8 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
 @ifset XTENSA
 
 @emph{Target Xtensa options:}
- [@b{--[no-]text-section-literals}] [@b{--[no-]absolute-literals}]
+ [@b{--[no-]text-section-literals}] [@b{--[no-]auto-litpools}]
+ [@b{--[no-]absolute-literals}]
  [@b{--[no-]target-align}] [@b{--[no-]longcalls}]
  [@b{--[no-]transform}]
  [@b{--rename-section} @var{oldname}=@var{newname}]
diff --git a/gas/doc/c-xtensa.texi b/gas/doc/c-xtensa.texi
index 7019f84..26155c4 100644
--- a/gas/doc/c-xtensa.texi
+++ b/gas/doc/c-xtensa.texi
@@ -43,9 +43,30 @@ placed in a data RAM/ROM.  With @samp{--text-@-section-@-literals}, the
 literals are interspersed in the text section in order to keep them as
 close as possible to their references.  This may be necessary for large
 assembly files, where the literals would otherwise be out of range of the
-@code{L32R} instructions in the text section.  These options only affect
+@code{L32R} instructions in the text section.  Literals are grouped into
+pools following @code{.literal_position} directives or preceding
+@code{ENTRY} instructions.  These options only affect literals referenced
+via PC-relative @code{L32R} instructions; literals for absolute mode
+@code{L32R} instructions are handled separately.
+@xref{Literal Directive, ,literal}.
+
+@item --auto-litpools | --no-auto-litpools
+@kindex --auto-litpools
+@kindex --no-auto-litpools
+Control the treatment of literal pools.  The default is
+@samp{--no-@-auto-@-litpools}, which in the absence of
+@samp{--text-@-section-@-literals} places literals in separate sections
+in the output file.  This allows the literal pool to be placed in a data
+RAM/ROM.  With @samp{--auto-@-litpools}, the literals are interspersed
+in the text section in order to keep them as close as possible to their
+references, explicit @code{.literal_position} directives are not
+required.  This may be necessary for very large functions, where single
+literal pool at the beginning of the function may not be reachable by
+@code{L32R} instructions at the end.  These options only affect
 literals referenced via PC-relative @code{L32R} instructions; literals
 for absolute mode @code{L32R} instructions are handled separately.
+When used together with @samp{--text-@-section-@-literals},
+@samp{--auto-@-litpools} takes precedence.
 @xref{Literal Directive, ,literal}.
 
 @item --absolute-literals | --no-absolute-literals
diff --git a/gas/testsuite/gas/xtensa/all.exp b/gas/testsuite/gas/xtensa/all.exp
index d197ec8..db39629 100644
--- a/gas/testsuite/gas/xtensa/all.exp
+++ b/gas/testsuite/gas/xtensa/all.exp
@@ -100,6 +100,7 @@ if [istarget xtensa*-*-*] then {
     run_dump_test "jlong"
     run_dump_test "trampoline"
     run_dump_test "first_frag_align"
+    run_dump_test "auto-litpools"
 }
 
 if [info exists errorInfo] then {
diff --git a/gas/testsuite/gas/xtensa/auto-litpools.d b/gas/testsuite/gas/xtensa/auto-litpools.d
new file mode 100644
index 0000000..4d1a690
--- /dev/null
+++ b/gas/testsuite/gas/xtensa/auto-litpools.d
@@ -0,0 +1,12 @@
+#as: --auto-litpools
+#objdump: -d
+#name: auto literal pool placement
+
+.*: +file format .*xtensa.*
+#...
+.*4:.*l32r.a2, 0 .*
+#...
+.*3e437:.*j.3e440 .*
+#...
+.*40750:.*l32r.a2, 3e43c .*
+#...
diff --git a/gas/testsuite/gas/xtensa/auto-litpools.s b/gas/testsuite/gas/xtensa/auto-litpools.s
new file mode 100644
index 0000000..9a5b26b
--- /dev/null
+++ b/gas/testsuite/gas/xtensa/auto-litpools.s
@@ -0,0 +1,13 @@
+	.text
+	.align	4
+	.literal	.L0, 0x12345
+	.literal	.L1, 0x12345
+
+f:
+	l32r	a2, .L0
+	.rep	44000
+	_nop
+	_nop
+	.endr
+	l32r	a2, .L1
+	ret
-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 0/2] xtensa gas improvements
@ 2015-08-12  0:54 Max Filippov
  2015-08-12  0:54 ` [PATCH 1/2] xtensa: add --tramp-limit option to gas Max Filippov
  2015-08-12  0:54 ` [PATCH 2/2] xtensa: add --auto-litpools option Max Filippov
  0 siblings, 2 replies; 6+ messages in thread
From: Max Filippov @ 2015-08-12  0:54 UTC (permalink / raw)
  To: binutils
  Cc: Sterling Augustine, David Weatherford, Marc Gauthier,
	linux-xtensa, Max Filippov

Hi Sterling,

this patch series adds tuning option for the jump trampolines and adds new
option for automatic literal pool placement, that allows assembling huge
functions.

Max Filippov (2):
  xtensa: add --tramp-limit option to gas
  xtensa: add --auto-litpools option

 gas/config/tc-xtensa.c                   | 459 ++++++++++++++++++++++++++++++-
 gas/config/tc-xtensa.h                   |   1 +
 gas/doc/as.texinfo                       |   3 +-
 gas/doc/c-xtensa.texi                    |  23 +-
 gas/testsuite/gas/xtensa/all.exp         |   1 +
 gas/testsuite/gas/xtensa/auto-litpools.d |  12 +
 gas/testsuite/gas/xtensa/auto-litpools.s |  13 +
 7 files changed, 500 insertions(+), 12 deletions(-)
 create mode 100644 gas/testsuite/gas/xtensa/auto-litpools.d
 create mode 100644 gas/testsuite/gas/xtensa/auto-litpools.s

-- 
1.8.1.4

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] xtensa: add --tramp-limit option to gas
  2015-08-12  0:54 ` [PATCH 1/2] xtensa: add --tramp-limit option to gas Max Filippov
@ 2015-08-12 16:32   ` augustine.sterling
  0 siblings, 0 replies; 6+ messages in thread
From: augustine.sterling @ 2015-08-12 16:32 UTC (permalink / raw)
  To: Max Filippov; +Cc: binutils, David Weatherford, Marc Gauthier, linux-xtensa

On Tue, Aug 11, 2015 at 5:54 PM, Max Filippov <jcmvbkbc@gmail.com> wrote:
> Hardcoded frag count limit for trampoline frag emission may not work for
> all configurations. Make that limit configurable.

Frags, and especially frag-counts are a gas internal implementation
detail, and exposing them to the command line is not a good idea. The
count can change depending on the other obstacks involved, and many
other things.

You can tell the exact maximum size of all frags--it's decided when
they are closed, so you would be far better off determining the
maximum distance from one trampoline to the next and basing the
decision on that. If the issue is that they need to be created prior
to closing a frag, you can check if you need to close one by keeping a
running max size, perhaps at every instruction.

So, unfortunately, I think this change needs more work.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] xtensa: add --auto-litpools option
  2015-08-12  0:54 ` [PATCH 2/2] xtensa: add --auto-litpools option Max Filippov
@ 2015-08-12 16:40   ` augustine.sterling
  2015-08-12 17:25     ` Max Filippov
  0 siblings, 1 reply; 6+ messages in thread
From: augustine.sterling @ 2015-08-12 16:40 UTC (permalink / raw)
  To: Max Filippov; +Cc: binutils, David Weatherford, Marc Gauthier, linux-xtensa

On Tue, Aug 11, 2015 at 5:54 PM, Max Filippov <jcmvbkbc@gmail.com> wrote:
> Auto-litpools is the automated version of text-section-literals: literal
> pool candidate frags are planted every N frags and during relaxation
> they are turned into actual literal pools where literals are moved to
> become reachable for their first reference by L32R instruction.
>
> 2015-08-11  David Weatherford  <weath@cadence.com>
> gas/
>         * config/tc-xtensa.c (struct litpool_frag, struct litpool_seg):
>         New structures.
>         (xtensa_maybe_create_literal_pool_frag): New function.
>         (litpool_seg_list, auto_litpools, auto_litpool_limit)
>         (litpool_buf, litpool_slotbuf): New static variables.
>         (option_auto_litpools, option_no_auto_litpools)
>         (option_auto_litpool_limit): New enum identifiers.
>         (md_longopts): Add entries for auto-litpools, no-auto-litpools
>         and auto-litpool-limit.
>         (md_parse_option): Handle option_auto_litpools,
>         option_no_auto_litpools and option_auto_litpool_limit.
>         (md_show_usage): Add help for --[no-]auto-litpools and
>         --auto-litpool-limit.
>         (xtensa_mark_literal_pool_location): Record a place for literal
>         pool with a call to xtensa_maybe_create_literal_pool_frag.
>         (get_literal_pool_location): Find highest priority literal pool
>         or convert candidate to literal pool when auto-litpools are used.
>         (xg_assemble_vliw_tokens): Create literal pool after jump
>         instruction.
>         (xtensa_check_frag_count): Create candidate literal pool every
>         auto_litpool_limit frags.
>         (xtensa_relax_frag): Add jump around literals to non-empty
>         literal pool.
>         (xtensa_move_literals): Estimate literal pool addresses and move
>         unreachable literals closer to their users, converting candidate
>         to literal pool if needed.
>         (xtensa_switch_to_non_abs_literal_fragment): Only emit error
>         about missing .literal_position in case auto-litpools are not
>         used.
>         * config/tc-xtensa.h (xtensa_relax_statesE): New relaxation
>         state: RELAX_LITERAL_POOL_CANDIDATE_BEGIN.
>         * doc/as.texinfo (Xtensa options):  Document --auto-litpools and
>         --no-auto-litpools options.
>         * doc/c-xtensa.texi (Xtensa options): Likewise.

This is a terrific change and a long time in coming. Approved after
you fix the very minor formatting issue below.

> +struct litpool_frag {

gnu-style requires the brace on a separate line. This happens in a
couple of spots. Fix those and you are set to go.

> +  struct litpool_frag *next;
> +  struct litpool_frag *prev;
> +  fragS *fragP;
> +  addressT addr;
> +  short priority; /* 1, 2, or 3 -- 1 is highest  */
> +  short original_priority;
> +};
> +
> +/* Map a segment to its litpool_frag list.  */
> +struct litpool_seg {
> +  struct litpool_seg *next;
> +  asection *seg;
> +  struct litpool_frag frag_list;
> +  int frag_count; /* since last litpool location  */
> +};
> +
> +static struct litpool_seg litpool_seg_list;
> +
>
>  /* Directive functions.  */
>
> @@ -475,6 +496,9 @@ 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 *);
> +static void xtensa_maybe_create_literal_pool_frag (bfd_boolean, bfd_boolean);
> +static bfd_boolean auto_litpools = FALSE;
> +static int auto_litpool_limit = 10000;
>
>  /* Alignment Functions.  */
>
> @@ -700,6 +724,10 @@ enum
>    option_trampolines,
>    option_no_trampolines,
>    option_trampoline_limit,
> +
> +  option_auto_litpools,
> +  option_no_auto_litpools,
> +  option_auto_litpool_limit,
>  };
>
>  const char *md_shortopts = "";
> @@ -776,6 +804,10 @@ struct option md_longopts[] =
>    { "no-trampolines", no_argument, NULL, option_no_trampolines },
>    { "tramp-limit", required_argument, NULL, option_trampoline_limit },
>
> +  { "auto-litpools", no_argument, NULL, option_auto_litpools },
> +  { "no-auto-litpools", no_argument, NULL, option_no_auto_litpools },
> +  { "auto-litpool-limit", required_argument, NULL, option_auto_litpool_limit },
> +
>    { NULL, no_argument, NULL, 0 }
>  };
>
> @@ -979,6 +1011,34 @@ md_parse_option (int c, char *arg)
>         return 1;
>        }
>
> +    case option_auto_litpools:
> +      auto_litpools = TRUE;
> +      use_literal_section = FALSE;
> +      return 1;
> +
> +    case option_no_auto_litpools:
> +      auto_litpools = FALSE;
> +      auto_litpool_limit = -1;
> +      return 1;
> +
> +    case option_auto_litpool_limit:
> +      {
> +       int value = 0;
> +       if (auto_litpool_limit < 0)
> +         as_fatal (_("no-auto-litpools is incompatible with auto-litpool-limit"));
> +       if (*arg == 0 || *arg == '-')
> +         as_fatal (_("invalid auto-litpool-limit argument"));
> +       value = strtol (arg, &arg, 10);
> +       if (*arg != 0)
> +         as_fatal (_("invalid auto-litpool-limit argument"));
> +       if (value < 100 || value > 10000)
> +         as_fatal (_("invalid auto-litpool-limit argument (range is 100-10000)"));
> +       auto_litpool_limit = value;
> +       auto_litpools = TRUE;
> +       use_literal_section = FALSE;
> +       return 1;
> +      }
> +
>      default:
>        return 0;
>      }
> @@ -1007,7 +1067,12 @@ Xtensa options:\n\
>                            when jumps do not reach their targets\n\
>    --tramp-limit=<value>   (default 8000, range 100-10000) Maximum number of\n\
>                            blocks of instructions to emit between trampoline\n\
> -                          locations; implies --trampolines flag\n", stream);
> +                          locations; implies --trampolines flag\n\
> +  --[no-]auto-litpools    [Do not] automatically create literal pools\n\
> +  --auto-litpool-limit=<value>\n\
> +                          (range 100-10000) Maximum number of blocks of\n\
> +                          instructions to emit between literal pool\n\
> +                          locations; implies --auto-litpools flag\n", stream);
>  }
>
>
> @@ -4749,6 +4814,8 @@ xtensa_mark_literal_pool_location (void)
>    pool_location = frag_now;
>    frag_now->tc_frag_data.lit_frchain = frchain_now;
>    frag_now->tc_frag_data.literal_frag = frag_now;
> +  /* Just record this frag.  */
> +  xtensa_maybe_create_literal_pool_frag (FALSE, FALSE);
>    frag_variant (rs_machine_dependent, 0, 0,
>                 RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
>    xtensa_set_frag_assembly_state (frag_now);
> @@ -4853,6 +4920,31 @@ get_expanded_loop_offset (xtensa_opcode opcode)
>  static fragS *
>  get_literal_pool_location (segT seg)
>  {
> +  struct litpool_seg *lps = litpool_seg_list.next;
> +  struct litpool_frag *lpf;
> +  for ( ; lps && lps->seg->id != seg->id; lps = lps->next)
> +    ;
> +  if (lps)
> +    {
> +      for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
> +       { /* Skip "candidates" for now.  */
> +         if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN &&
> +             lpf->priority == 1)
> +           return lpf->fragP;
> +       }
> +      /* Must convert a lower-priority pool.  */
> +      for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
> +       {
> +         if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN)
> +           return lpf->fragP;
> +       }
> +      /* Still no match -- try for a low priority pool.  */
> +      for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
> +       {
> +         if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
> +           return lpf->fragP;
> +       }
> +    }
>    return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
>  }
>
> @@ -7119,6 +7211,11 @@ xg_assemble_vliw_tokens (vliw_insn *vinsn)
>        frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
>        frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
>        frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
> +      if (tinsn->opcode == xtensa_l32r_opcode)
> +       {
> +         frag_now->tc_frag_data.literal_frags[slot] =
> +                 tinsn->tok[1].X_add_symbol->sy_frag;
> +       }
>        if (tinsn->literal_space != 0)
>         xg_assemble_literal_space (tinsn->literal_space, slot);
>        frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
> @@ -7191,6 +7288,8 @@ xg_assemble_vliw_tokens (vliw_insn *vinsn)
>                     frag_now->fr_symbol, frag_now->fr_offset, NULL);
>           xtensa_set_frag_assembly_state (frag_now);
>           xtensa_maybe_create_trampoline_frag ();
> +         /* Always create one here.  */
> +         xtensa_maybe_create_literal_pool_frag (TRUE, FALSE);
>         }
>        else if (is_branch && do_align_targets ())
>         {
> @@ -7333,11 +7432,18 @@ xtensa_check_frag_count (void)
>        clear_frag_count ();
>        unreachable_count = 0;
>      }
> +
> +  /* We create an area for a possible literal pool every N (default 5000)
> +     frags or so.  */
> +  xtensa_maybe_create_literal_pool_frag (TRUE, TRUE);
>  }
>
>  static xtensa_insnbuf trampoline_buf = NULL;
>  static xtensa_insnbuf trampoline_slotbuf = NULL;
>
> +static xtensa_insnbuf litpool_buf = NULL;
> +static xtensa_insnbuf litpool_slotbuf = NULL;
> +
>  #define TRAMPOLINE_FRAG_SIZE 3000
>
>  static void
> @@ -7429,6 +7535,135 @@ dump_trampolines (void)
>      }
>  }
>
> +static void dump_litpools (void) __attribute__ ((unused));
> +
> +static void
> +dump_litpools (void)
> +{
> +  struct litpool_seg *lps = litpool_seg_list.next;
> +  struct litpool_frag *lpf;
> +
> +  for ( ; lps ; lps = lps->next )
> +    {
> +      printf("litpool seg %s\n", lps->seg->name);
> +      for ( lpf = lps->frag_list.next; lpf->fragP; lpf = lpf->next )
> +       {
> +         fragS *litfrag = lpf->fragP->fr_next;
> +         int count = 0;
> +         while (litfrag && litfrag->fr_subtype != RELAX_LITERAL_POOL_END)
> +           {
> +             if (litfrag->fr_fix == 4)
> +               count++;
> +             litfrag = litfrag->fr_next;
> +           }
> +         printf("   %ld <%d:%d> (%d) [%d]: ",
> +                lpf->addr, lpf->priority, lpf->original_priority,
> +                lpf->fragP->fr_line, count);
> +         //dump_frag(lpf->fragP);
> +       }
> +    }
> +}
> +
> +static void
> +xtensa_maybe_create_literal_pool_frag (bfd_boolean create,
> +                                      bfd_boolean only_if_needed)
> +{
> +  struct litpool_seg *lps = litpool_seg_list.next;
> +  fragS *fragP;
> +  struct litpool_frag *lpf;
> +  bfd_boolean needed = FALSE;
> +
> +  if (use_literal_section || !auto_litpools)
> +    return;
> +
> +  for ( ; lps ; lps = lps->next )
> +    {
> +      if (lps->seg == now_seg)
> +       break;
> +    }
> +
> +  if (lps == NULL)
> +    {
> +      lps = (struct litpool_seg *)xcalloc (sizeof (struct litpool_seg), 1);
> +      lps->next = litpool_seg_list.next;
> +      litpool_seg_list.next = lps;
> +      lps->seg = now_seg;
> +      lps->frag_list.next = &lps->frag_list;
> +      lps->frag_list.prev = &lps->frag_list;
> +    }
> +
> +  lps->frag_count++;
> +
> +  if (create)
> +    {
> +      if (only_if_needed)
> +       {
> +         if (past_xtensa_end || !use_transform() ||
> +             frag_now->tc_frag_data.is_no_transform)
> +           {
> +             return;
> +           }
> +         if (auto_litpool_limit <= 0)
> +           {
> +             /* Don't create a litpool based only on frag count.  */
> +             return;
> +           }
> +         else if (lps->frag_count > auto_litpool_limit)
> +           {
> +             needed = TRUE;
> +           }
> +         else
> +           {
> +             return;
> +           }
> +       }
> +      else
> +       {
> +         needed = TRUE;
> +       }
> +    }
> +
> +  if (needed)
> +    {
> +      int size = (only_if_needed) ? 3 : 0; /* Space for a "j" insn.  */
> +      /* Create a potential site for a literal pool.  */
> +      frag_wane (frag_now);
> +      frag_new (0);
> +      xtensa_set_frag_assembly_state (frag_now);
> +      fragP = frag_now;
> +      fragP->tc_frag_data.lit_frchain = frchain_now;
> +      fragP->tc_frag_data.literal_frag = fragP;
> +      frag_var (rs_machine_dependent, size, size,
> +                   (only_if_needed) ?
> +                       RELAX_LITERAL_POOL_CANDIDATE_BEGIN :
> +                       RELAX_LITERAL_POOL_BEGIN,
> +                   NULL, 0, NULL);
> +      frag_now->tc_frag_data.lit_seg = now_seg;
> +      frag_variant (rs_machine_dependent, 0, 0,
> +                   RELAX_LITERAL_POOL_END, NULL, 0, NULL);
> +      xtensa_set_frag_assembly_state (frag_now);
> +    }
> +  else
> +    {
> +      /* RELAX_LITERAL_POOL_BEGIN frag is being created;
> +        just record it here.  */
> +      fragP = frag_now;
> +    }
> +
> +  lpf = (struct litpool_frag *)xmalloc(sizeof (struct litpool_frag));
> +  /* Insert at tail of circular list.  */
> +  lpf->addr = 0;
> +  lps->frag_list.prev->next = lpf;
> +  lpf->next = &lps->frag_list;
> +  lpf->prev = lps->frag_list.prev;
> +  lps->frag_list.prev = lpf;
> +  lpf->fragP = fragP;
> +  lpf->priority = (needed) ? (only_if_needed) ? 3 : 2 : 1;
> +  lpf->original_priority = lpf->priority;
> +
> +  lps->frag_count = 0;
> +}
> +
>  static void
>  xtensa_cleanup_align_frags (void)
>  {
> @@ -9048,7 +9283,41 @@ xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
>        break;
>
>      case RELAX_LITERAL_POOL_BEGIN:
> +      if (fragP->fr_var != 0)
> +       {
> +         /* We have a converted "candidate" literal pool;
> +            assemble a jump around it.  */
> +         TInsn insn;
> +         if (!litpool_slotbuf)
> +           {
> +             litpool_buf = xtensa_insnbuf_alloc (isa);
> +             litpool_slotbuf = xtensa_insnbuf_alloc (isa);
> +           }
> +         new_stretch += 3;
> +         fragP->tc_frag_data.relax_seen = FALSE; /* Need another pass.  */
> +         fragP->tc_frag_data.is_insn = TRUE;
> +         tinsn_init (&insn);
> +         insn.insn_type = ITYPE_INSN;
> +         insn.opcode = xtensa_j_opcode;
> +         insn.ntok = 1;
> +         set_expr_symbol_offset (&insn.tok[0], fragP->fr_symbol,
> +                                 fragP->fr_fix);
> +         fmt = xg_get_single_format (xtensa_j_opcode);
> +         tinsn_to_slotbuf (fmt, 0, &insn, litpool_slotbuf);
> +         xtensa_format_set_slot (isa, fmt, 0, litpool_buf, litpool_slotbuf);
> +         xtensa_insnbuf_to_chars (isa, litpool_buf,
> +                                  (unsigned char *)fragP->fr_literal +
> +                                  fragP->fr_fix, 3);
> +         fragP->fr_fix += 3;
> +         fragP->fr_var -= 3;
> +         /* Add a fix-up.  */
> +         fix_new (fragP, 0, 3, fragP->fr_symbol, 0, TRUE,
> +                  BFD_RELOC_XTENSA_SLOT0_OP);
> +       }
> +      break;
> +
>      case RELAX_LITERAL_POOL_END:
> +    case RELAX_LITERAL_POOL_CANDIDATE_BEGIN:
>      case RELAX_MAYBE_UNREACHABLE:
>      case RELAX_MAYBE_DESIRE_ALIGN:
>        /* No relaxation required.  */
> @@ -10808,12 +11077,115 @@ xtensa_move_literals (void)
>    segT dest_seg;
>    fixS *fix, *next_fix, **fix_splice;
>    sym_list *lit;
> +  struct litpool_seg *lps;
>
>    mark_literal_frags (literal_head->next);
>
>    if (use_literal_section)
>      return;
>
> +  /* Assign addresses (rough estimates) to the potential literal pool locations
> +     and create new ones if the gaps are too large.  */
> +
> +  for (lps = litpool_seg_list.next; lps; lps = lps->next)
> +    {
> +      frchainS *frchP = seg_info (lps->seg)->frchainP;
> +      struct litpool_frag *lpf = lps->frag_list.next;
> +      addressT addr = 0;
> +
> +      for ( ; frchP; frchP = frchP->frch_next)
> +       {
> +         fragS *fragP;
> +         for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
> +           {
> +             if (lpf && fragP == lpf->fragP)
> +               {
> +                 gas_assert(fragP->fr_type == rs_machine_dependent &&
> +                            (fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN ||
> +                             fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN));
> +                 /* Found a litpool location.  */
> +                 lpf->addr = addr;
> +                 lpf = lpf->next;
> +               }
> +             if (fragP->fr_type == rs_machine_dependent &&
> +                 fragP->fr_subtype == RELAX_SLOTS)
> +               {
> +                 int slot;
> +                 for (slot = 0; slot < MAX_SLOTS; slot++)
> +                   {
> +                     if (fragP->tc_frag_data.literal_frags[slot])
> +                       {
> +                         /* L32R; point its literal to the nearest litpool
> +                            preferring non-"candidate" positions to avoid
> +                            the jump-around.  */
> +                         fragS *litfrag = fragP->tc_frag_data.literal_frags[slot];
> +                         struct litpool_frag *lp = lpf->prev;
> +                         if (!lp->fragP)
> +                           {
> +                             break;
> +                           }
> +                         while (lp->fragP->fr_subtype ==
> +                                RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
> +                           {
> +                             lp = lp->prev;
> +                             if (lp->fragP == NULL)
> +                               {
> +                                 /* End of list; have to bite the bullet.
> +                                    Take the nearest.  */
> +                                 lp = lpf->prev;
> +                                 break;
> +                               }
> +                             /* Does it (conservatively) reach?  */
> +                             if (addr - lp->addr <= 128 * 1024)
> +                               {
> +                                 if (lp->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN)
> +                                   {
> +                                     /* Found a good one.  */
> +                                     break;
> +                                   }
> +                                 else if (lp->prev->fragP &&
> +                                          addr - lp->prev->addr > 128 * 1024)
> +                                   {
> +                                     /* This is still a "candidate" but the next one
> +                                        will be too far away, so revert to the nearest
> +                                        one, convert it and add the jump around.  */
> +                                     fragS *poolbeg;
> +                                     fragS *poolend;
> +                                     symbolS *lsym;
> +                                     char label[10 + 2 * sizeof (fragS *)];
> +                                     lp = lpf->prev;
> +                                     poolbeg = lp->fragP;
> +                                     lp->priority = 1;
> +                                     poolbeg->fr_subtype = RELAX_LITERAL_POOL_BEGIN;
> +                                     poolend = poolbeg->fr_next;
> +                                     gas_assert (poolend->fr_type == rs_machine_dependent &&
> +                                                 poolend->fr_subtype == RELAX_LITERAL_POOL_END);
> +                                     /* Create a local symbol pointing to the
> +                                        end of the pool.  */
> +                                     sprintf (label, ".L0_LT_%p", poolbeg);
> +                                     lsym = (symbolS *)local_symbol_make (label, lps->seg,
> +                                                                          0, poolend);
> +                                     poolbeg->fr_symbol = lsym;
> +                                     /* Rest is done in xtensa_relax_frag.  */
> +                                   }
> +                               }
> +                           }
> +                         if (! litfrag->tc_frag_data.literal_frag)
> +                           {
> +                             /* Take earliest use of this literal to avoid
> +                                forward refs.  */
> +                             litfrag->tc_frag_data.literal_frag = lp->fragP;
> +                           }
> +                       }
> +                   }
> +               }
> +             addr += fragP->fr_fix;
> +             if (fragP->fr_type == rs_fill)
> +               addr += fragP->fr_offset;
> +           }
> +       }
> +    }
> +
>    for (segment = literal_head->next; segment; segment = segment->next)
>      {
>        /* Keep the literals for .init and .fini in separate sections.  */
> @@ -10858,9 +11230,6 @@ xtensa_move_literals (void)
>        while (search_frag != frag_now)
>         {
>           next_frag = search_frag->fr_next;
> -
> -         /* First, move the frag out of the literal section and
> -            to the appropriate place.  */
>           if (search_frag->tc_frag_data.literal_frag)
>             {
>               literal_pool = search_frag->tc_frag_data.literal_frag;
> @@ -10868,8 +11237,56 @@ xtensa_move_literals (void)
>               frchain_to = literal_pool->tc_frag_data.lit_frchain;
>               gas_assert (frchain_to);
>             }
> +
> +         if (search_frag->fr_type == rs_fill && search_frag->fr_fix == 0)
> +           {
> +             /* Skip empty fill frags.  */
> +             *frag_splice = next_frag;
> +             search_frag = next_frag;
> +             continue;
> +           }
> +
> +         if (search_frag->fr_type == rs_align)
> +           {
> +             /* Skip alignment frags, because the pool as a whole will be
> +                aligned if used, and we don't want to force alignment if the
> +                pool is unused.  */
> +             *frag_splice = next_frag;
> +             search_frag = next_frag;
> +             continue;
> +           }
> +
> +         /* First, move the frag out of the literal section and
> +            to the appropriate place.  */
> +
> +         /* Insert an aligmnent frag at start of pool.  */
> +         if (literal_pool->fr_next->fr_type == rs_machine_dependent &&
> +             literal_pool->fr_next->fr_subtype == RELAX_LITERAL_POOL_END)
> +           {
> +             segT pool_seg = literal_pool->fr_next->tc_frag_data.lit_seg;
> +             emit_state prev_state;
> +             fragS *prev_frag;
> +             fragS *align_frag;
> +             xtensa_switch_section_emit_state (&prev_state, pool_seg, 0);
> +             prev_frag = frag_now;
> +             frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
> +             align_frag = frag_now;
> +             frag_align (2, 0, 0);
> +             /* Splice it into the right place.  */
> +             prev_frag->fr_next = align_frag->fr_next;
> +             align_frag->fr_next = literal_pool->fr_next;
> +             literal_pool->fr_next = align_frag;
> +             /* Insert after this one.  */
> +             literal_pool->tc_frag_data.literal_frag = align_frag;
> +             xtensa_restore_emit_state (&prev_state);
> +           }
>           insert_after = literal_pool->tc_frag_data.literal_frag;
>           dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
> +         /* Skip align frag.  */
> +         if (insert_after->fr_next->fr_type == rs_align)
> +           {
> +             insert_after = insert_after->fr_next;
> +           }
>
>           *frag_splice = next_frag;
>           search_frag->fr_next = insert_after->fr_next;
> @@ -11033,7 +11450,10 @@ xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
>        && !recursive
>        && !is_init && ! is_fini)
>      {
> -      as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
> +      if (!auto_litpools)
> +       {
> +         as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
> +       }
>
>        /* When we mark a literal pool location, we want to put a frag in
>          the literal pool that points to it.  But to do that, we want to
> diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
> index c6c7699..6c0d131 100644
> --- a/gas/config/tc-xtensa.h
> +++ b/gas/config/tc-xtensa.h
> @@ -124,6 +124,7 @@ enum xtensa_relax_statesE
>
>    RELAX_LITERAL_POOL_BEGIN,
>    RELAX_LITERAL_POOL_END,
> +  RELAX_LITERAL_POOL_CANDIDATE_BEGIN,
>    /* Technically these are not relaxations at all but mark a location
>       to store literals later.  Note that fr_var stores the frchain for
>       BEGIN frags and fr_var stores now_seg for END frags.  */
> diff --git a/gas/doc/as.texinfo b/gas/doc/as.texinfo
> index 5710e1c..0b27c54 100644
> --- a/gas/doc/as.texinfo
> +++ b/gas/doc/as.texinfo
> @@ -552,7 +552,8 @@ gcc(1), ld(1), and the Info entries for @file{binutils} and @file{ld}.
>  @ifset XTENSA
>
>  @emph{Target Xtensa options:}
> - [@b{--[no-]text-section-literals}] [@b{--[no-]absolute-literals}]
> + [@b{--[no-]text-section-literals}] [@b{--[no-]auto-litpools}]
> + [@b{--[no-]absolute-literals}]
>   [@b{--[no-]target-align}] [@b{--[no-]longcalls}]
>   [@b{--[no-]transform}]
>   [@b{--rename-section} @var{oldname}=@var{newname}]
> diff --git a/gas/doc/c-xtensa.texi b/gas/doc/c-xtensa.texi
> index 7019f84..26155c4 100644
> --- a/gas/doc/c-xtensa.texi
> +++ b/gas/doc/c-xtensa.texi
> @@ -43,9 +43,30 @@ placed in a data RAM/ROM.  With @samp{--text-@-section-@-literals}, the
>  literals are interspersed in the text section in order to keep them as
>  close as possible to their references.  This may be necessary for large
>  assembly files, where the literals would otherwise be out of range of the
> -@code{L32R} instructions in the text section.  These options only affect
> +@code{L32R} instructions in the text section.  Literals are grouped into
> +pools following @code{.literal_position} directives or preceding
> +@code{ENTRY} instructions.  These options only affect literals referenced
> +via PC-relative @code{L32R} instructions; literals for absolute mode
> +@code{L32R} instructions are handled separately.
> +@xref{Literal Directive, ,literal}.
> +
> +@item --auto-litpools | --no-auto-litpools
> +@kindex --auto-litpools
> +@kindex --no-auto-litpools
> +Control the treatment of literal pools.  The default is
> +@samp{--no-@-auto-@-litpools}, which in the absence of
> +@samp{--text-@-section-@-literals} places literals in separate sections
> +in the output file.  This allows the literal pool to be placed in a data
> +RAM/ROM.  With @samp{--auto-@-litpools}, the literals are interspersed
> +in the text section in order to keep them as close as possible to their
> +references, explicit @code{.literal_position} directives are not
> +required.  This may be necessary for very large functions, where single
> +literal pool at the beginning of the function may not be reachable by
> +@code{L32R} instructions at the end.  These options only affect
>  literals referenced via PC-relative @code{L32R} instructions; literals
>  for absolute mode @code{L32R} instructions are handled separately.
> +When used together with @samp{--text-@-section-@-literals},
> +@samp{--auto-@-litpools} takes precedence.
>  @xref{Literal Directive, ,literal}.
>
>  @item --absolute-literals | --no-absolute-literals
> diff --git a/gas/testsuite/gas/xtensa/all.exp b/gas/testsuite/gas/xtensa/all.exp
> index d197ec8..db39629 100644
> --- a/gas/testsuite/gas/xtensa/all.exp
> +++ b/gas/testsuite/gas/xtensa/all.exp
> @@ -100,6 +100,7 @@ if [istarget xtensa*-*-*] then {
>      run_dump_test "jlong"
>      run_dump_test "trampoline"
>      run_dump_test "first_frag_align"
> +    run_dump_test "auto-litpools"
>  }
>
>  if [info exists errorInfo] then {
> diff --git a/gas/testsuite/gas/xtensa/auto-litpools.d b/gas/testsuite/gas/xtensa/auto-litpools.d
> new file mode 100644
> index 0000000..4d1a690
> --- /dev/null
> +++ b/gas/testsuite/gas/xtensa/auto-litpools.d
> @@ -0,0 +1,12 @@
> +#as: --auto-litpools
> +#objdump: -d
> +#name: auto literal pool placement
> +
> +.*: +file format .*xtensa.*
> +#...
> +.*4:.*l32r.a2, 0 .*
> +#...
> +.*3e437:.*j.3e440 .*
> +#...
> +.*40750:.*l32r.a2, 3e43c .*
> +#...
> diff --git a/gas/testsuite/gas/xtensa/auto-litpools.s b/gas/testsuite/gas/xtensa/auto-litpools.s
> new file mode 100644
> index 0000000..9a5b26b
> --- /dev/null
> +++ b/gas/testsuite/gas/xtensa/auto-litpools.s
> @@ -0,0 +1,13 @@
> +       .text
> +       .align  4
> +       .literal        .L0, 0x12345
> +       .literal        .L1, 0x12345
> +
> +f:
> +       l32r    a2, .L0
> +       .rep    44000
> +       _nop
> +       _nop
> +       .endr
> +       l32r    a2, .L1
> +       ret
> --
> 1.8.1.4
>

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] xtensa: add --auto-litpools option
  2015-08-12 16:40   ` augustine.sterling
@ 2015-08-12 17:25     ` Max Filippov
  0 siblings, 0 replies; 6+ messages in thread
From: Max Filippov @ 2015-08-12 17:25 UTC (permalink / raw)
  To: augustine.sterling
  Cc: binutils, David Weatherford, Marc Gauthier, linux-xtensa

On Wed, Aug 12, 2015 at 7:40 PM, augustine.sterling@gmail.com
<augustine.sterling@gmail.com> wrote:
> On Tue, Aug 11, 2015 at 5:54 PM, Max Filippov <jcmvbkbc@gmail.com> wrote:
>> Auto-litpools is the automated version of text-section-literals: literal
>> pool candidate frags are planted every N frags and during relaxation
>> they are turned into actual literal pools where literals are moved to
>> become reachable for their first reference by L32R instruction.
>
> This is a terrific change and a long time in coming. Approved after
> you fix the very minor formatting issue below.
>
>> +struct litpool_frag {
>
> gnu-style requires the brace on a separate line. This happens in a
> couple of spots. Fix those and you are set to go.

Fixed both spots and checked in. Thanks!

-- Max

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2015-08-12 17:25 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-12  0:54 [PATCH 0/2] xtensa gas improvements Max Filippov
2015-08-12  0:54 ` [PATCH 1/2] xtensa: add --tramp-limit option to gas Max Filippov
2015-08-12 16:32   ` augustine.sterling
2015-08-12  0:54 ` [PATCH 2/2] xtensa: add --auto-litpools option Max Filippov
2015-08-12 16:40   ` augustine.sterling
2015-08-12 17:25     ` Max Filippov

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).