From 978adaaa4cd3921842e2be8a31c05f081fb17fcf Mon Sep 17 00:00:00 2001 From: Max Filippov Date: Wed, 29 Jul 2015 17:42:54 +0300 Subject: [PATCH] xtensa: add --auto-litpools option 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-12 David Weatherford 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. 2015-08-12 Max Filippov 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. Signed-off-by: Max Filippov --- Backported from: b46824bd49648c575372e6d9bc6a6defeabd6ed5 Changes to ChangeLogs and documentation are dropped. gas/config/tc-xtensa.c | 432 ++++++++++++++++++++++++++++++- gas/config/tc-xtensa.h | 1 + gas/testsuite/gas/xtensa/all.exp | 1 + gas/testsuite/gas/xtensa/auto-litpools.d | 12 + gas/testsuite/gas/xtensa/auto-litpools.s | 13 + 5 files changed, 454 insertions(+), 5 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 7311a05..b8b1e7d 100644 --- a/gas/config/tc-xtensa.c +++ b/gas/config/tc-xtensa.c @@ -440,6 +440,29 @@ 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. */ @@ -474,6 +497,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. */ @@ -698,6 +724,10 @@ enum option_trampolines, option_no_trampolines, + + option_auto_litpools, + option_no_auto_litpools, + option_auto_litpool_limit, }; const char *md_shortopts = ""; @@ -773,6 +803,10 @@ struct option md_longopts[] = { "trampolines", no_argument, NULL, option_trampolines }, { "no-trampolines", no_argument, NULL, option_no_trampolines }, + { "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 } }; @@ -961,6 +995,34 @@ md_parse_option (int c, char *arg) use_trampolines = FALSE; 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; } @@ -986,7 +1048,12 @@ 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\ + --[no-]auto-litpools [Do not] automatically create literal pools\n\ + --auto-litpool-limit=\n\ + (range 100-10000) Maximum number of blocks of\n\ + instructions to emit between literal pool\n\ + locations; implies --auto-litpools flag\n", stream); } @@ -4728,6 +4795,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); @@ -4832,6 +4901,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; } @@ -7098,6 +7192,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; @@ -7170,6 +7269,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 ()) { @@ -7314,11 +7415,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 @@ -7410,6 +7518,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) { @@ -9029,7 +9266,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. */ @@ -10789,12 +11060,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. */ @@ -10839,9 +11213,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; @@ -10849,8 +11220,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; @@ -11014,7 +11433,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 b2e43fa..290d902 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/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,5 +100,6 @@ if [istarget xtensa*-*-*] then { run_dump_test "jlong" run_dump_test "trampoline" + 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