This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
Re: [PATCH 2/2] xtensa: add --auto-litpools option
- From: "augustine dot sterling at gmail dot com" <augustine dot sterling at gmail dot com>
- To: Max Filippov <jcmvbkbc at gmail dot com>
- Cc: binutils at sourceware dot org, David Weatherford <weath at cadence dot com>, Marc Gauthier <marc at cadence dot com>, "linux-xtensa at linux-xtensa dot org" <linux-xtensa at linux-xtensa dot org>
- Date: Wed, 12 Aug 2015 09:40:44 -0700
- Subject: Re: [PATCH 2/2] xtensa: add --auto-litpools option
- Authentication-results: sourceware.org; auth=none
- References: <1439340867-980-1-git-send-email-jcmvbkbc at gmail dot com> <1439340867-980-3-git-send-email-jcmvbkbc at gmail dot com>
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
>