mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-06-28 23:39:35 +08:00
gas: xtensa: rewrite xg_relax_trampoline
Replace linked list of trampoline frags with an ordered array, so that instead of indexing fixups trampolines could be indexed. Keep each array in the trampoline_seg structure, so there's no need to rebuild it for every new processed segment. Don't run relaxation for each trampoline frag, instead run it for each fixup in the current segment that needs relaxation at the beginning of each relaxation pass. This way the complexity of this process drops from about O(n^2 * m) to about O(log n * m), where n is the number of trampoline frags and m is the number of fixups that need relaxation in the segment. gas/ 2017-11-27 Max Filippov <jcmvbkbc@gmail.com> * config/tc-xtensa.c (trampoline_index): New structure. (trampoline_seg): Replace trampoline list with trampoline index. (xg_find_trampoline, xg_add_trampoline_to_index) (xg_remove_trampoline_from_index, xg_add_trampoline_to_seg) (xg_is_trampoline_frag_full, xg_get_fulcrum) (xg_find_best_trampoline, xg_relax_fixup, xg_relax_fixups) (xg_is_relaxable_fixup): New functions. (J_MARGIN): New macro. (xtensa_create_trampoline_frag): Use xg_add_trampoline_to_seg instead of open-coded addition to the linked list. (dump_trampolines): Iterate through the trampoline_seg::index. (cached_fixupS, cached_fixup, fixup_cacheS, fixup_cache) (fixup_order, xtensa_make_cached_fixup) (xtensa_realloc_fixup_cache, xtensa_cache_relaxable_fixups) (xtensa_find_first_cached_fixup, xtensa_delete_cached_fixup) (xtensa_add_cached_fixup, check_and_update_trampolines): Remove definitions. (xg_relax_trampoline): Extract logic into separate functions, replace body with a call to xg_relax_fixups. (search_trampolines): Replace search in linked list with search in index. Change data type of address-tracking variables from int to offsetT. Replace abs with labs. (xg_append_jump): Finish the trampoline frag if it's full. (add_jump_to_trampoline): Remove trampoline frag from the index if the frag is full. * config/tc-xtensa.h (xtensa_frag_type): Remove next_trampoline. * testsuite/gas/xtensa/trampoline.d: Adjust absolute addresses as the placement of trampolines has slightly changed. * testsuite/gas/xtensa/trampoline.s: Add _nop so that objdump stays in sync with instruction stream.
This commit is contained in:
@ -1,3 +1,36 @@
|
|||||||
|
2017-11-27 Max Filippov <jcmvbkbc@gmail.com>
|
||||||
|
|
||||||
|
* config/tc-xtensa.c (trampoline_index): New structure.
|
||||||
|
(trampoline_seg): Replace trampoline list with trampoline index.
|
||||||
|
(xg_find_trampoline, xg_add_trampoline_to_index)
|
||||||
|
(xg_remove_trampoline_from_index, xg_add_trampoline_to_seg)
|
||||||
|
(xg_is_trampoline_frag_full, xg_get_fulcrum)
|
||||||
|
(xg_find_best_trampoline, xg_relax_fixup, xg_relax_fixups)
|
||||||
|
(xg_is_relaxable_fixup): New functions.
|
||||||
|
(J_MARGIN): New macro.
|
||||||
|
(xtensa_create_trampoline_frag): Use xg_add_trampoline_to_seg
|
||||||
|
instead of open-coded addition to the linked list.
|
||||||
|
(dump_trampolines): Iterate through the trampoline_seg::index.
|
||||||
|
(cached_fixupS, cached_fixup, fixup_cacheS, fixup_cache)
|
||||||
|
(fixup_order, xtensa_make_cached_fixup)
|
||||||
|
(xtensa_realloc_fixup_cache, xtensa_cache_relaxable_fixups)
|
||||||
|
(xtensa_find_first_cached_fixup, xtensa_delete_cached_fixup)
|
||||||
|
(xtensa_add_cached_fixup, check_and_update_trampolines): Remove
|
||||||
|
definitions.
|
||||||
|
(xg_relax_trampoline): Extract logic into separate functions,
|
||||||
|
replace body with a call to xg_relax_fixups.
|
||||||
|
(search_trampolines): Replace search in linked list with search
|
||||||
|
in index. Change data type of address-tracking variables from
|
||||||
|
int to offsetT. Replace abs with labs.
|
||||||
|
(xg_append_jump): Finish the trampoline frag if it's full.
|
||||||
|
(add_jump_to_trampoline): Remove trampoline frag from the index
|
||||||
|
if the frag is full.
|
||||||
|
* config/tc-xtensa.h (xtensa_frag_type): Remove next_trampoline.
|
||||||
|
* testsuite/gas/xtensa/trampoline.d: Adjust absolute addresses
|
||||||
|
as the placement of trampolines has slightly changed.
|
||||||
|
* testsuite/gas/xtensa/trampoline.s: Add _nop so that objdump
|
||||||
|
stays in sync with instruction stream.
|
||||||
|
|
||||||
2017-11-27 Max Filippov <jcmvbkbc@gmail.com>
|
2017-11-27 Max Filippov <jcmvbkbc@gmail.com>
|
||||||
|
|
||||||
* config/tc-xtensa.c (init_trampoline_frag): Replace pointer to
|
* config/tc-xtensa.c (init_trampoline_frag): Replace pointer to
|
||||||
|
@ -7348,16 +7348,23 @@ xtensa_end (void)
|
|||||||
xtensa_check_frag_count ();
|
xtensa_check_frag_count ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct trampoline_index
|
||||||
|
{
|
||||||
|
fragS **entry;
|
||||||
|
size_t n_entries;
|
||||||
|
size_t n_max;
|
||||||
|
};
|
||||||
|
|
||||||
struct trampoline_seg
|
struct trampoline_seg
|
||||||
{
|
{
|
||||||
struct trampoline_seg *next;
|
struct trampoline_seg *next;
|
||||||
asection *seg;
|
asection *seg;
|
||||||
fragS trampoline_list;
|
struct trampoline_index index;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct trampoline_seg trampoline_seg_list;
|
static struct trampoline_seg trampoline_seg_list;
|
||||||
#define J_RANGE (128 * 1024)
|
#define J_RANGE (128 * 1024)
|
||||||
|
#define J_MARGIN 4096
|
||||||
|
|
||||||
static int unreachable_count = 0;
|
static int unreachable_count = 0;
|
||||||
|
|
||||||
@ -7426,6 +7433,51 @@ find_trampoline_seg (asection *seg)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static size_t xg_find_trampoline (const struct trampoline_index *idx,
|
||||||
|
addressT addr)
|
||||||
|
{
|
||||||
|
size_t a = 0;
|
||||||
|
size_t b = idx->n_entries;
|
||||||
|
|
||||||
|
while (b - a > 1)
|
||||||
|
{
|
||||||
|
size_t c = (a + b) / 2;
|
||||||
|
|
||||||
|
if (idx->entry[c]->fr_address <= addr)
|
||||||
|
a = c;
|
||||||
|
else
|
||||||
|
b = c;
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xg_add_trampoline_to_index (struct trampoline_index *idx,
|
||||||
|
fragS *fragP)
|
||||||
|
{
|
||||||
|
if (idx->n_entries == idx->n_max)
|
||||||
|
{
|
||||||
|
idx->n_max = (idx->n_entries + 1) * 2;
|
||||||
|
idx->entry = xrealloc (idx->entry,
|
||||||
|
sizeof (*idx->entry) * idx->n_max);
|
||||||
|
}
|
||||||
|
idx->entry[idx->n_entries] = fragP;
|
||||||
|
++idx->n_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xg_remove_trampoline_from_index (struct trampoline_index *idx,
|
||||||
|
size_t i)
|
||||||
|
{
|
||||||
|
gas_assert (i < idx->n_entries);
|
||||||
|
memmove (idx->entry + i, idx->entry + i + 1,
|
||||||
|
(idx->n_entries - i - 1) * sizeof (*idx->entry));
|
||||||
|
--idx->n_entries;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void xg_add_trampoline_to_seg (struct trampoline_seg *ts,
|
||||||
|
fragS *fragP)
|
||||||
|
{
|
||||||
|
xg_add_trampoline_to_index (&ts->index, fragP);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
|
xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
|
||||||
@ -7459,12 +7511,14 @@ xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
|
|||||||
trampoline_buf = xtensa_insnbuf_alloc (xtensa_default_isa);
|
trampoline_buf = xtensa_insnbuf_alloc (xtensa_default_isa);
|
||||||
trampoline_slotbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
|
trampoline_slotbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
|
||||||
}
|
}
|
||||||
fragP->tc_frag_data.next_trampoline =
|
|
||||||
ts->trampoline_list.tc_frag_data.next_trampoline;
|
|
||||||
ts->trampoline_list.tc_frag_data.next_trampoline = fragP;
|
|
||||||
fragP->tc_frag_data.needs_jump_around = needs_jump_around;
|
fragP->tc_frag_data.needs_jump_around = needs_jump_around;
|
||||||
|
xg_add_trampoline_to_seg (ts, fragP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bfd_boolean xg_is_trampoline_frag_full (const fragS *fragP)
|
||||||
|
{
|
||||||
|
return fragP->fr_var < 3;
|
||||||
|
}
|
||||||
|
|
||||||
void dump_trampolines (void);
|
void dump_trampolines (void);
|
||||||
|
|
||||||
@ -7475,14 +7529,17 @@ dump_trampolines (void)
|
|||||||
|
|
||||||
for ( ; ts; ts = ts->next)
|
for ( ; ts; ts = ts->next)
|
||||||
{
|
{
|
||||||
|
size_t i;
|
||||||
asection *seg = ts->seg;
|
asection *seg = ts->seg;
|
||||||
|
|
||||||
if (seg == NULL)
|
if (seg == NULL)
|
||||||
continue;
|
continue;
|
||||||
fprintf(stderr, "SECTION %s\n", seg->name);
|
fprintf(stderr, "SECTION %s\n", seg->name);
|
||||||
fragS *tf = ts->trampoline_list.tc_frag_data.next_trampoline;
|
|
||||||
for ( ; tf; tf = tf->tc_frag_data.next_trampoline)
|
for (i = 0; i < ts->index.n_entries; ++i)
|
||||||
{
|
{
|
||||||
|
fragS *tf = ts->index.entry[i];
|
||||||
|
|
||||||
fprintf(stderr, " 0x%08x: fix=%d, jump_around=%s\n",
|
fprintf(stderr, " 0x%08x: fix=%d, jump_around=%s\n",
|
||||||
(int)tf->fr_address, (int)tf->fr_fix,
|
(int)tf->fr_address, (int)tf->fr_fix,
|
||||||
tf->tc_frag_data.needs_jump_around ? "T" : "F");
|
tf->tc_frag_data.needs_jump_around ? "T" : "F");
|
||||||
@ -8998,50 +9055,116 @@ static long relax_frag_for_align (fragS *, long);
|
|||||||
static long relax_frag_immed
|
static long relax_frag_immed
|
||||||
(segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
|
(segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
|
||||||
|
|
||||||
typedef struct cached_fixup cached_fixupS;
|
/* Get projected address for the first fulcrum on a path from source to
|
||||||
struct cached_fixup
|
target. */
|
||||||
|
static addressT xg_get_fulcrum (addressT source, addressT target)
|
||||||
{
|
{
|
||||||
int addr;
|
offsetT delta = target - source;
|
||||||
int target;
|
int n;
|
||||||
int delta;
|
|
||||||
fixS *fixP;
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef struct fixup_cache fixup_cacheS;
|
n = (labs (delta) + J_RANGE - J_MARGIN - 1) / (J_RANGE - J_MARGIN);
|
||||||
struct fixup_cache
|
return source + delta / n;
|
||||||
{
|
|
||||||
cached_fixupS *fixups;
|
|
||||||
unsigned n_fixups;
|
|
||||||
unsigned n_max;
|
|
||||||
|
|
||||||
segT seg;
|
|
||||||
fragS *first_frag;
|
|
||||||
};
|
|
||||||
|
|
||||||
static int fixup_order (const void *a, const void *b)
|
|
||||||
{
|
|
||||||
const cached_fixupS *pa = a;
|
|
||||||
const cached_fixupS *pb = b;
|
|
||||||
|
|
||||||
if (pa->addr == pb->addr)
|
|
||||||
{
|
|
||||||
if (pa->target == pb->target)
|
|
||||||
{
|
|
||||||
if (pa->fixP->fx_r_type == pb->fixP->fx_r_type)
|
|
||||||
return 0;
|
|
||||||
return pa->fixP->fx_r_type < pb->fixP->fx_r_type ? -1 : 1;
|
|
||||||
}
|
|
||||||
return pa->target - pb->target;
|
|
||||||
}
|
|
||||||
return pa->addr - pb->addr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static bfd_boolean xtensa_make_cached_fixup (cached_fixupS *o, fixS *fixP)
|
/* Given trampoline index, source and target of a jump find the best
|
||||||
|
candidate trampoline for the first fulcrum. The best trampoline is
|
||||||
|
the one in the reach of "j' instruction from the source, closest to
|
||||||
|
the projected fulcrum address, and preferrably w/o a jump around or
|
||||||
|
with already initialized jump around. */
|
||||||
|
static size_t xg_find_best_trampoline (struct trampoline_index *idx,
|
||||||
|
addressT source, addressT target)
|
||||||
|
{
|
||||||
|
addressT fulcrum = xg_get_fulcrum (source, target);
|
||||||
|
size_t dist = 0;
|
||||||
|
size_t best = -1;
|
||||||
|
size_t base_tr = xg_find_trampoline (idx, fulcrum);
|
||||||
|
int checked = 1;
|
||||||
|
|
||||||
|
/* Check trampoline frags around the base_tr to find the best. */
|
||||||
|
for (dist = 0; checked; ++dist)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
size_t tr = base_tr - dist;
|
||||||
|
|
||||||
|
checked = 0;
|
||||||
|
|
||||||
|
/* Trampolines are checked in the following order:
|
||||||
|
base_tr, base_tr + 1, base_tr - 1, base_tr + 2, base_tr - 2 */
|
||||||
|
for (i = 0; i < 2; ++i, tr = base_tr + dist + 1)
|
||||||
|
if (tr < idx->n_entries)
|
||||||
|
{
|
||||||
|
fragS *trampoline_frag = idx->entry[tr];
|
||||||
|
offsetT off;
|
||||||
|
|
||||||
|
/* Don't check trampolines outside source - target interval. */
|
||||||
|
if ((trampoline_frag->fr_address < source &&
|
||||||
|
trampoline_frag->fr_address < target) ||
|
||||||
|
(trampoline_frag->fr_address > source &&
|
||||||
|
trampoline_frag->fr_address > target))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
off = trampoline_frag->fr_address - fulcrum;
|
||||||
|
/* Stop if some trampoline is found and the search is more than
|
||||||
|
J_RANGE / 4 from the projected fulcrum. A trampoline w/o jump
|
||||||
|
around is nice, but it shouldn't have much overhead. */
|
||||||
|
if (best < idx->n_entries && labs (off) > J_RANGE / 4)
|
||||||
|
return best;
|
||||||
|
|
||||||
|
off = trampoline_frag->fr_address - source;
|
||||||
|
if (labs (off) < J_RANGE - J_MARGIN)
|
||||||
|
{
|
||||||
|
++checked;
|
||||||
|
/* Stop if a trampoline w/o jump around is found or initialized
|
||||||
|
trampoline with jump around is found. */
|
||||||
|
if (!trampoline_frag->tc_frag_data.needs_jump_around ||
|
||||||
|
trampoline_frag->fr_fix)
|
||||||
|
return tr;
|
||||||
|
else if (best >= idx->n_entries)
|
||||||
|
best = tr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (best < idx->n_entries)
|
||||||
|
return best;
|
||||||
|
else
|
||||||
|
as_fatal (_("cannot find suitable trampoline"));
|
||||||
|
}
|
||||||
|
|
||||||
|
static fixS *xg_relax_fixup (struct trampoline_index *idx, fixS *fixP)
|
||||||
|
{
|
||||||
|
symbolS *s = fixP->fx_addsy;
|
||||||
|
addressT source = fixP->fx_frag->fr_address;
|
||||||
|
addressT target = S_GET_VALUE (s) + fixP->fx_offset;
|
||||||
|
size_t tr = xg_find_best_trampoline (idx, source, target);
|
||||||
|
fragS *trampoline_frag = idx->entry[tr];
|
||||||
|
fixS *newfixP;
|
||||||
|
|
||||||
|
init_trampoline_frag (trampoline_frag);
|
||||||
|
newfixP = xg_append_jump (trampoline_frag,
|
||||||
|
fixP->fx_addsy, fixP->fx_offset);
|
||||||
|
|
||||||
|
/* Adjust the fixup for the original "j" instruction to
|
||||||
|
point to the newly added jump. */
|
||||||
|
fixP->fx_addsy = trampoline_frag->fr_symbol;
|
||||||
|
fixP->fx_offset = trampoline_frag->fr_fix - 3;
|
||||||
|
fixP->tc_fix_data.X_add_symbol = trampoline_frag->fr_symbol;
|
||||||
|
fixP->tc_fix_data.X_add_number = trampoline_frag->fr_fix - 3;
|
||||||
|
|
||||||
|
trampoline_frag->tc_frag_data.relax_seen = FALSE;
|
||||||
|
|
||||||
|
if (xg_is_trampoline_frag_full (trampoline_frag))
|
||||||
|
xg_remove_trampoline_from_index (idx, tr);
|
||||||
|
|
||||||
|
return newfixP;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bfd_boolean xg_is_relaxable_fixup (fixS *fixP)
|
||||||
{
|
{
|
||||||
xtensa_isa isa = xtensa_default_isa;
|
xtensa_isa isa = xtensa_default_isa;
|
||||||
int addr = fixP->fx_frag->fr_address;
|
addressT addr = fixP->fx_frag->fr_address;
|
||||||
int target;
|
addressT target;
|
||||||
int delta;
|
offsetT delta;
|
||||||
symbolS *s = fixP->fx_addsy;
|
symbolS *s = fixP->fx_addsy;
|
||||||
int slot;
|
int slot;
|
||||||
xtensa_format fmt;
|
xtensa_format fmt;
|
||||||
@ -9050,10 +9173,11 @@ static bfd_boolean xtensa_make_cached_fixup (cached_fixupS *o, fixS *fixP)
|
|||||||
if (fixP->fx_r_type < BFD_RELOC_XTENSA_SLOT0_OP ||
|
if (fixP->fx_r_type < BFD_RELOC_XTENSA_SLOT0_OP ||
|
||||||
fixP->fx_r_type > BFD_RELOC_XTENSA_SLOT14_OP)
|
fixP->fx_r_type > BFD_RELOC_XTENSA_SLOT14_OP)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
target = S_GET_VALUE (s);
|
|
||||||
|
target = S_GET_VALUE (s) + fixP->fx_offset;
|
||||||
delta = target - addr;
|
delta = target - addr;
|
||||||
|
|
||||||
if (abs(delta) < J_RANGE / 2)
|
if (labs (delta) < J_RANGE - J_MARGIN)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
xtensa_insnbuf_from_chars (isa, trampoline_buf,
|
xtensa_insnbuf_from_chars (isa, trampoline_buf,
|
||||||
@ -9064,221 +9188,37 @@ static bfd_boolean xtensa_make_cached_fixup (cached_fixupS *o, fixS *fixP)
|
|||||||
slot = fixP->tc_fix_data.slot;
|
slot = fixP->tc_fix_data.slot;
|
||||||
xtensa_format_get_slot (isa, fmt, slot, trampoline_buf, trampoline_slotbuf);
|
xtensa_format_get_slot (isa, fmt, slot, trampoline_buf, trampoline_slotbuf);
|
||||||
opcode = xtensa_opcode_decode (isa, fmt, slot, trampoline_slotbuf);
|
opcode = xtensa_opcode_decode (isa, fmt, slot, trampoline_slotbuf);
|
||||||
if (opcode != xtensa_j_opcode)
|
return opcode == xtensa_j_opcode;
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
o->addr = addr;
|
|
||||||
o->target = target;
|
|
||||||
o->delta = delta;
|
|
||||||
o->fixP = fixP;
|
|
||||||
|
|
||||||
return TRUE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xtensa_realloc_fixup_cache (fixup_cacheS *cache, unsigned add)
|
static void xg_relax_fixups (struct trampoline_seg *ts)
|
||||||
{
|
{
|
||||||
if (cache->n_fixups + add > cache->n_max)
|
struct trampoline_index *idx = &ts->index;
|
||||||
{
|
|
||||||
cache->n_max = (cache->n_fixups + add) * 2;
|
|
||||||
cache->fixups = XRESIZEVEC (cached_fixupS, cache->fixups, cache->n_max);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xtensa_cache_relaxable_fixups (fixup_cacheS *cache,
|
|
||||||
segment_info_type *seginfo)
|
|
||||||
{
|
|
||||||
fixS *fixP;
|
|
||||||
|
|
||||||
cache->n_fixups = 0;
|
|
||||||
|
|
||||||
for (fixP = seginfo->fix_root; fixP ; fixP = fixP->fx_next)
|
|
||||||
{
|
|
||||||
xtensa_realloc_fixup_cache (cache, 1);
|
|
||||||
|
|
||||||
if (xtensa_make_cached_fixup (cache->fixups + cache->n_fixups, fixP))
|
|
||||||
++cache->n_fixups;
|
|
||||||
}
|
|
||||||
qsort (cache->fixups, cache->n_fixups, sizeof (*cache->fixups), fixup_order);
|
|
||||||
}
|
|
||||||
|
|
||||||
static unsigned xtensa_find_first_cached_fixup (const fixup_cacheS *cache,
|
|
||||||
int addr)
|
|
||||||
{
|
|
||||||
unsigned a = 0;
|
|
||||||
unsigned b = cache->n_fixups;
|
|
||||||
|
|
||||||
while (b - a > 1)
|
|
||||||
{
|
|
||||||
unsigned c = (a + b) / 2;
|
|
||||||
|
|
||||||
if (cache->fixups[c].addr < addr)
|
|
||||||
a = c;
|
|
||||||
else
|
|
||||||
b = c;
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xtensa_delete_cached_fixup (fixup_cacheS *cache, unsigned i)
|
|
||||||
{
|
|
||||||
memmove (cache->fixups + i, cache->fixups + i + 1,
|
|
||||||
(cache->n_fixups - i - 1) * sizeof (*cache->fixups));
|
|
||||||
--cache->n_fixups;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bfd_boolean xtensa_add_cached_fixup (fixup_cacheS *cache, fixS *fixP)
|
|
||||||
{
|
|
||||||
cached_fixupS o;
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
if (!xtensa_make_cached_fixup (&o, fixP))
|
|
||||||
return FALSE;
|
|
||||||
xtensa_realloc_fixup_cache (cache, 1);
|
|
||||||
i = xtensa_find_first_cached_fixup (cache, o.addr);
|
|
||||||
if (i < cache->n_fixups)
|
|
||||||
{
|
|
||||||
++i;
|
|
||||||
memmove (cache->fixups + i + 1, cache->fixups + i,
|
|
||||||
(cache->n_fixups - i) * sizeof (*cache->fixups));
|
|
||||||
}
|
|
||||||
cache->fixups[i] = o;
|
|
||||||
++cache->n_fixups;
|
|
||||||
return TRUE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void xg_relax_trampoline (fragS *fragP, long stretch, long *new_stretch)
|
|
||||||
{
|
|
||||||
static fixup_cacheS fixup_cache;
|
|
||||||
segment_info_type *seginfo = seg_info (now_seg);
|
segment_info_type *seginfo = seg_info (now_seg);
|
||||||
int trampaddr = fragP->fr_address + fragP->fr_fix;
|
fixS *fx;
|
||||||
int searchaddr = trampaddr < J_RANGE ? 0 : trampaddr - J_RANGE;
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
if (now_seg != fixup_cache.seg ||
|
for (fx = seginfo->fix_root; fx; fx = fx->fx_next)
|
||||||
fragP == fixup_cache.first_frag ||
|
|
||||||
fixup_cache.first_frag == NULL)
|
|
||||||
{
|
{
|
||||||
xtensa_cache_relaxable_fixups (&fixup_cache, seginfo);
|
fixS *fixP = fx;
|
||||||
fixup_cache.seg = now_seg;
|
|
||||||
fixup_cache.first_frag = fragP;
|
while (xg_is_relaxable_fixup (fixP))
|
||||||
|
fixP = xg_relax_fixup (idx, fixP);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scan for jumps that will not reach. */
|
/* Given a trampoline frag relax all jumps that might want to use this
|
||||||
for (i = xtensa_find_first_cached_fixup (&fixup_cache, searchaddr);
|
trampoline. Only do real work once per relaxation cycle, when
|
||||||
i < fixup_cache.n_fixups; ++i)
|
xg_relax_trampoline is called for the first trampoline in the now_seg.
|
||||||
|
Don't use stretch, don't update new_stretch: place fulcrums with a
|
||||||
|
slack to tolerate code movement. In the worst case if a jump between
|
||||||
|
two trampolines wouldn't reach the next relaxation pass will fix it. */
|
||||||
|
static void xg_relax_trampoline (fragS *fragP, long stretch ATTRIBUTE_UNUSED,
|
||||||
|
long *new_stretch ATTRIBUTE_UNUSED)
|
||||||
{
|
{
|
||||||
fixS *fixP = fixup_cache.fixups[i].fixP;
|
|
||||||
int target = fixup_cache.fixups[i].target;
|
|
||||||
int addr = fixup_cache.fixups[i].addr;
|
|
||||||
int delta = fixup_cache.fixups[i].delta + stretch;
|
|
||||||
|
|
||||||
trampaddr = fragP->fr_address + fragP->fr_fix;
|
|
||||||
|
|
||||||
if (addr + J_RANGE < trampaddr)
|
|
||||||
continue;
|
|
||||||
if (addr > trampaddr + J_RANGE)
|
|
||||||
break;
|
|
||||||
if (abs (delta) < J_RANGE)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (delta > J_RANGE || delta < -1 * J_RANGE)
|
|
||||||
{ /* Found an out-of-range jump; scan the list of trampolines for the best match. */
|
|
||||||
struct trampoline_seg *ts = find_trampoline_seg (now_seg);
|
struct trampoline_seg *ts = find_trampoline_seg (now_seg);
|
||||||
fragS *tf = ts->trampoline_list.tc_frag_data.next_trampoline;
|
|
||||||
fragS *prev = &ts->trampoline_list;
|
|
||||||
int lower = (target < addr) ? target : addr;
|
|
||||||
int upper = (target > addr) ? target : addr;
|
|
||||||
int midpoint = lower + (upper - lower) / 2;
|
|
||||||
|
|
||||||
if ((upper - lower) > 2 * J_RANGE)
|
if (ts->index.n_entries && ts->index.entry[0] == fragP)
|
||||||
{
|
xg_relax_fixups (ts);
|
||||||
/* One trampoline won't suffice; we need multiple jumps.
|
|
||||||
Jump to the trampoline that's farthest, but still in
|
|
||||||
range relative to the original "j" instruction. */
|
|
||||||
for ( ; tf; prev = tf, tf = tf->tc_frag_data.next_trampoline)
|
|
||||||
{
|
|
||||||
fragS *next = tf->tc_frag_data.next_trampoline;
|
|
||||||
int this_addr = tf->fr_address + tf->fr_fix;
|
|
||||||
int next_addr = next ? next->fr_address + next->fr_fix : 0 ;
|
|
||||||
|
|
||||||
if (addr == lower)
|
|
||||||
{
|
|
||||||
/* Forward jump. */
|
|
||||||
if (this_addr - addr < J_RANGE)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
/* Backward jump. */
|
|
||||||
if (next_addr == 0 || addr - next_addr > J_RANGE)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fragS *best_tf = NULL;
|
|
||||||
fragS *best_tf_prev = NULL;
|
|
||||||
int best_delta = 0;
|
|
||||||
|
|
||||||
for ( ; tf; prev = tf, tf = tf->tc_frag_data.next_trampoline)
|
|
||||||
{
|
|
||||||
int this_addr = tf->fr_address + tf->fr_fix;
|
|
||||||
int this_delta = abs (this_addr - midpoint);
|
|
||||||
|
|
||||||
if (!best_tf || this_delta < best_delta)
|
|
||||||
{
|
|
||||||
best_tf = tf;
|
|
||||||
best_delta = this_delta;
|
|
||||||
best_tf_prev = prev;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tf = best_tf;
|
|
||||||
prev = best_tf_prev;
|
|
||||||
}
|
|
||||||
if (tf == fragP)
|
|
||||||
{
|
|
||||||
if (abs (addr - trampaddr) < J_RANGE)
|
|
||||||
{ /* The trampoline is in range of original; fix it! */
|
|
||||||
fixS *newfixP;
|
|
||||||
|
|
||||||
new_stretch += init_trampoline_frag (tf) + 3;
|
|
||||||
/* Assemble a jump to the target label in the trampoline frag. */
|
|
||||||
newfixP = xg_append_jump (fragP,
|
|
||||||
fixP->fx_addsy, fixP->fx_offset);
|
|
||||||
|
|
||||||
/* Adjust the fixup for the original "j" instruction to
|
|
||||||
point to the newly added jump. */
|
|
||||||
fixP->fx_addsy = fragP->fr_symbol;
|
|
||||||
fixP->fx_offset = fragP->fr_fix - 3;
|
|
||||||
fixP->tc_fix_data.X_add_symbol = fragP->fr_symbol;
|
|
||||||
fixP->tc_fix_data.X_add_number = fragP->fr_fix - 3;
|
|
||||||
|
|
||||||
fixP = newfixP;
|
|
||||||
xtensa_delete_cached_fixup (&fixup_cache, i);
|
|
||||||
xtensa_add_cached_fixup (&fixup_cache, newfixP);
|
|
||||||
|
|
||||||
/* re-do current fixup */
|
|
||||||
--i;
|
|
||||||
|
|
||||||
fragP->tc_frag_data.relax_seen = FALSE; /* Need another pass. */
|
|
||||||
/* Do we have room for more? */
|
|
||||||
if (fragP->fr_var < 3)
|
|
||||||
{ /* No, convert to fill. */
|
|
||||||
frag_wane (fragP);
|
|
||||||
fragP->fr_subtype = 0;
|
|
||||||
/* Remove from the trampoline_list. */
|
|
||||||
prev->tc_frag_data.next_trampoline =
|
|
||||||
tf->tc_frag_data.next_trampoline;
|
|
||||||
if (fragP == fixup_cache.first_frag)
|
|
||||||
fixup_cache.first_frag = NULL;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return the number of bytes added to this fragment, given that the
|
/* Return the number of bytes added to this fragment, given that the
|
||||||
@ -9869,53 +9809,65 @@ static fragS *
|
|||||||
search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
|
search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
|
||||||
{
|
{
|
||||||
struct trampoline_seg *ts = find_trampoline_seg (now_seg);
|
struct trampoline_seg *ts = find_trampoline_seg (now_seg);
|
||||||
fragS *tf = ts ? ts->trampoline_list.tc_frag_data.next_trampoline : NULL;
|
fragS *tf = NULL;
|
||||||
|
size_t i;
|
||||||
fragS *best_tf = NULL;
|
fragS *best_tf = NULL;
|
||||||
int best_delta = 0;
|
offsetT best_delta = 0;
|
||||||
int best_addr = 0;
|
offsetT best_addr = 0;
|
||||||
symbolS *sym = tinsn->tok[0].X_add_symbol;
|
symbolS *sym = tinsn->tok[0].X_add_symbol;
|
||||||
offsetT target = S_GET_VALUE (sym) + tinsn->tok[0].X_add_number;
|
offsetT target = S_GET_VALUE (sym) + tinsn->tok[0].X_add_number;
|
||||||
offsetT addr = fragP->fr_address;
|
offsetT addr = fragP->fr_address;
|
||||||
offsetT lower = (addr < target) ? addr : target;
|
offsetT lower = (addr < target) ? addr : target;
|
||||||
offsetT upper = (addr > target) ? addr : target;
|
offsetT upper = (addr > target) ? addr : target;
|
||||||
int delta = upper - lower;
|
offsetT delta = upper - lower;
|
||||||
offsetT midpoint = lower + delta / 2;
|
offsetT midpoint = lower + delta / 2;
|
||||||
int this_delta = -1;
|
offsetT this_delta = -1;
|
||||||
int this_addr = -1;
|
offsetT this_addr = -1;
|
||||||
|
|
||||||
|
if (!ts)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
if (delta > 2 * J_RANGE)
|
if (delta > 2 * J_RANGE)
|
||||||
{
|
{
|
||||||
/* One trampoline won't do; we need multiple.
|
/* One trampoline won't do; we need multiple.
|
||||||
Choose the farthest trampoline that's still in range of the original
|
Choose the farthest trampoline that's still in range of the original
|
||||||
and let a later pass finish the job. */
|
and let a later pass finish the job. */
|
||||||
for ( ; tf; tf = tf->tc_frag_data.next_trampoline)
|
for (i = 0; i < ts->index.n_entries; ++i)
|
||||||
{
|
{
|
||||||
fragS *next = tf->tc_frag_data.next_trampoline;
|
tf = ts->index.entry[i];
|
||||||
int next_addr = next ? next->fr_address + next->fr_fix : 0;
|
|
||||||
|
|
||||||
this_addr = tf->fr_address + tf->fr_fix;
|
this_addr = tf->fr_address + tf->fr_fix;
|
||||||
if (lower == addr)
|
if (upper == addr)
|
||||||
|
{
|
||||||
|
/* Backward jump. */
|
||||||
|
if (addr - this_addr < J_RANGE)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if (i + 1 < ts->index.n_entries)
|
||||||
{
|
{
|
||||||
/* Forward jump. */
|
/* Forward jump. */
|
||||||
if (this_addr - addr < J_RANGE)
|
fragS *next = ts->index.entry[i + 1];
|
||||||
|
offsetT next_addr = next->fr_address + next->fr_fix;
|
||||||
|
|
||||||
|
if (next_addr - addr > J_RANGE)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* Backward jump. */
|
|
||||||
if (next_addr == 0 || addr - next_addr > J_RANGE)
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (abs (addr - this_addr) < J_RANGE)
|
if (i < ts->index.n_entries &&
|
||||||
|
labs (addr - this_addr) < J_RANGE)
|
||||||
return tf;
|
return tf;
|
||||||
|
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
for ( ; tf; tf = tf->tc_frag_data.next_trampoline)
|
|
||||||
|
for (i = 0; i < ts->index.n_entries; ++i)
|
||||||
{
|
{
|
||||||
|
tf = ts->index.entry[i];
|
||||||
this_addr = tf->fr_address + tf->fr_fix;
|
this_addr = tf->fr_address + tf->fr_fix;
|
||||||
this_delta = abs (this_addr - midpoint);
|
this_delta = labs (this_addr - midpoint);
|
||||||
if (unreachable_only && tf->tc_frag_data.needs_jump_around)
|
if (unreachable_only && tf->tc_frag_data.needs_jump_around)
|
||||||
continue;
|
continue;
|
||||||
if (!best_tf || this_delta < best_delta)
|
if (!best_tf || this_delta < best_delta)
|
||||||
@ -9928,8 +9880,8 @@ search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
|
|||||||
|
|
||||||
if (best_tf &&
|
if (best_tf &&
|
||||||
best_delta < J_RANGE &&
|
best_delta < J_RANGE &&
|
||||||
abs(best_addr - lower) < J_RANGE &&
|
labs(best_addr - lower) < J_RANGE &&
|
||||||
abs(best_addr - upper) < J_RANGE)
|
labs(best_addr - upper) < J_RANGE)
|
||||||
return best_tf;
|
return best_tf;
|
||||||
|
|
||||||
return NULL; /* No suitable trampoline found. */
|
return NULL; /* No suitable trampoline found. */
|
||||||
@ -9950,24 +9902,10 @@ get_best_trampoline (TInsn *tinsn, fragS *fragP)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
/* Append jump to sym + offset to the end of the trampoline frag fragP.
|
||||||
check_and_update_trampolines (void)
|
Adjust fragP's jump around if it's present. Adjust fragP's fr_fix/fr_var
|
||||||
{
|
and finish the frag if it's full (but don't remove it from the trampoline
|
||||||
struct trampoline_seg *ts = find_trampoline_seg (now_seg);
|
frag index). Return fixup for the newly created jump. */
|
||||||
fragS *tf = ts->trampoline_list.tc_frag_data.next_trampoline;
|
|
||||||
fragS *prev = &ts->trampoline_list;
|
|
||||||
|
|
||||||
for ( ; tf; prev = tf, tf = tf->tc_frag_data.next_trampoline)
|
|
||||||
{
|
|
||||||
if (tf->fr_var < 3)
|
|
||||||
{
|
|
||||||
frag_wane (tf);
|
|
||||||
prev->tc_frag_data.next_trampoline =
|
|
||||||
tf->tc_frag_data.next_trampoline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static fixS *xg_append_jump (fragS *fragP, symbolS *sym, offsetT offset)
|
static fixS *xg_append_jump (fragS *fragP, symbolS *sym, offsetT offset)
|
||||||
{
|
{
|
||||||
fixS *fixP;
|
fixS *fixP;
|
||||||
@ -9997,6 +9935,13 @@ static fixS *xg_append_jump (fragS *fragP, symbolS *sym, offsetT offset)
|
|||||||
if (fragP->tc_frag_data.jump_around_fix)
|
if (fragP->tc_frag_data.jump_around_fix)
|
||||||
fragP->tc_frag_data.jump_around_fix->fx_offset += 3;
|
fragP->tc_frag_data.jump_around_fix->fx_offset += 3;
|
||||||
|
|
||||||
|
/* Do we have room for more? */
|
||||||
|
if (xg_is_trampoline_frag_full (fragP))
|
||||||
|
{
|
||||||
|
frag_wane (fragP);
|
||||||
|
fragP->fr_subtype = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return fixP;
|
return fixP;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -10048,7 +9993,14 @@ add_jump_to_trampoline (fragS *tramp, fragS *origfrag)
|
|||||||
origfrag->tc_frag_data.slot_offsets[slot] = tramp->fr_fix - 3;
|
origfrag->tc_frag_data.slot_offsets[slot] = tramp->fr_fix - 3;
|
||||||
|
|
||||||
/* If trampoline is full, remove it from the list. */
|
/* If trampoline is full, remove it from the list. */
|
||||||
check_and_update_trampolines ();
|
if (xg_is_trampoline_frag_full (tramp))
|
||||||
|
{
|
||||||
|
struct trampoline_seg *ts = find_trampoline_seg (now_seg);
|
||||||
|
size_t tr = xg_find_trampoline (&ts->index, tramp->fr_address);
|
||||||
|
|
||||||
|
gas_assert (ts->index.entry[tr] == tramp);
|
||||||
|
xg_remove_trampoline_from_index (&ts->index, tr);
|
||||||
|
}
|
||||||
|
|
||||||
return 3;
|
return 3;
|
||||||
}
|
}
|
||||||
|
@ -274,7 +274,6 @@ struct xtensa_frag_type
|
|||||||
offsetT slot_offsets[MAX_SLOTS];
|
offsetT slot_offsets[MAX_SLOTS];
|
||||||
|
|
||||||
/* For trampoline fragments. */
|
/* For trampoline fragments. */
|
||||||
fragS *next_trampoline;
|
|
||||||
struct fix *jump_around_fix;
|
struct fix *jump_around_fix;
|
||||||
|
|
||||||
/* When marking frags after this one in the chain as no transform,
|
/* When marking frags after this one in the chain as no transform,
|
||||||
|
@ -7,29 +7,27 @@
|
|||||||
.*0:.*j.0x1194c
|
.*0:.*j.0x1194c
|
||||||
.*3:.*j.0x1194f
|
.*3:.*j.0x1194f
|
||||||
.*6:.*j.0x11952
|
.*6:.*j.0x11952
|
||||||
.*9:.*j.0x1d4e4
|
.*9:.*j.0x11955
|
||||||
#...
|
#...
|
||||||
.*11949:.*j.0x11955
|
.*11949:.*j.0x11958
|
||||||
.*1194c:.*j.0x24a0e
|
.*1194c:.*j.0x24a0b
|
||||||
.*1194f:.*j.0x24a0e
|
.*1194f:.*j.0x24a0b
|
||||||
.*11952:.*j.0x24a11
|
.*11952:.*j.0x24a0e
|
||||||
#...
|
.*11955:.*j.0x2d687
|
||||||
.*1d4e1:.*j.0x1d4e7
|
|
||||||
.*1d4e4:.*j.0x33462
|
|
||||||
#...
|
#...
|
||||||
|
.*24a0b:.*j.0x24a0b
|
||||||
.*24a0e:.*j.0x24a0e
|
.*24a0e:.*j.0x24a0e
|
||||||
.*24a11:.*j.0x24a11
|
|
||||||
#...
|
#...
|
||||||
.*3345f:.*ret
|
.*2d684:.*ret
|
||||||
.*33462:.*j.0x49407
|
.*2d687:.*j.0x49404
|
||||||
#...
|
#...
|
||||||
.*49407:.*j.0x49407
|
.*49404:.*j.0x49404
|
||||||
.*4940a:.*beqz.n.a2,.0x4940f
|
.*49407:.*beqz.n.a2,.0x4940c
|
||||||
.*4940c:.*j.0x693d1
|
.*49409:.*j.0x693ce
|
||||||
#...
|
#...
|
||||||
.*693d1:.*j.0x7ddd4
|
.*693ce:.*j.0x7ddd1
|
||||||
#...
|
#...
|
||||||
.*7ddd4:.*j.0x927f5
|
.*7ddd1:.*j.0x927f5
|
||||||
#...
|
#...
|
||||||
.*927f5:.*j.0x927f5
|
.*927f5:.*j.0x927f5
|
||||||
#...
|
#...
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
and a2, a2, a3
|
and a2, a2, a3
|
||||||
_ret
|
_ret
|
||||||
.endr
|
.endr
|
||||||
|
_nop
|
||||||
4:
|
4:
|
||||||
j 4b
|
j 4b
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user