aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-xtensa.c
diff options
context:
space:
mode:
Diffstat (limited to 'gas/config/tc-xtensa.c')
-rw-r--r--gas/config/tc-xtensa.c520
1 files changed, 236 insertions, 284 deletions
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index 378537c..7d8b62b 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -7348,16 +7348,23 @@ xtensa_end (void)
xtensa_check_frag_count ();
}
+struct trampoline_index
+{
+ fragS **entry;
+ size_t n_entries;
+ size_t n_max;
+};
struct trampoline_seg
{
struct trampoline_seg *next;
asection *seg;
- fragS trampoline_list;
+ struct trampoline_index index;
};
static struct trampoline_seg trampoline_seg_list;
#define J_RANGE (128 * 1024)
+#define J_MARGIN 4096
static int unreachable_count = 0;
@@ -7426,6 +7433,51 @@ find_trampoline_seg (asection *seg)
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
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_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;
+ 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);
@@ -7475,14 +7529,17 @@ dump_trampolines (void)
for ( ; ts; ts = ts->next)
{
+ size_t i;
asection *seg = ts->seg;
if (seg == NULL)
continue;
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",
(int)tf->fr_address, (int)tf->fr_fix,
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
(segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
-typedef struct cached_fixup cached_fixupS;
-struct cached_fixup
-{
- int addr;
- int target;
- int delta;
- fixS *fixP;
-};
-
-typedef struct fixup_cache fixup_cacheS;
-struct fixup_cache
+/* Get projected address for the first fulcrum on a path from source to
+ target. */
+static addressT xg_get_fulcrum (addressT source, addressT target)
{
- cached_fixupS *fixups;
- unsigned n_fixups;
- unsigned n_max;
+ offsetT delta = target - source;
+ int n;
- segT seg;
- fragS *first_frag;
-};
+ n = (labs (delta) + J_RANGE - J_MARGIN - 1) / (J_RANGE - J_MARGIN);
+ return source + delta / n;
+}
-static int fixup_order (const void *a, const void *b)
+/* 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)
{
- const cached_fixupS *pa = a;
- const cached_fixupS *pb = b;
+ 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;
- if (pa->addr == pb->addr)
+ /* Check trampoline frags around the base_tr to find the best. */
+ for (dist = 0; checked; ++dist)
{
- 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;
+ 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;
+ }
+ }
}
- return pa->addr - pb->addr;
+
+ if (best < idx->n_entries)
+ return best;
+ else
+ as_fatal (_("cannot find suitable trampoline"));
}
-static bfd_boolean xtensa_make_cached_fixup (cached_fixupS *o, fixS *fixP)
+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;
- int addr = fixP->fx_frag->fr_address;
- int target;
- int delta;
+ addressT addr = fixP->fx_frag->fr_address;
+ addressT target;
+ offsetT delta;
symbolS *s = fixP->fx_addsy;
int slot;
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 ||
fixP->fx_r_type > BFD_RELOC_XTENSA_SLOT14_OP)
return FALSE;
- target = S_GET_VALUE (s);
+
+ target = S_GET_VALUE (s) + fixP->fx_offset;
delta = target - addr;
- if (abs(delta) < J_RANGE / 2)
+ if (labs (delta) < J_RANGE - J_MARGIN)
return FALSE;
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;
xtensa_format_get_slot (isa, fmt, slot, trampoline_buf, trampoline_slotbuf);
opcode = xtensa_opcode_decode (isa, fmt, slot, trampoline_slotbuf);
- if (opcode != xtensa_j_opcode)
- 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)
-{
- if (cache->n_fixups + add > cache->n_max)
- {
- 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);
+ return opcode == xtensa_j_opcode;
}
-static unsigned xtensa_find_first_cached_fixup (const fixup_cacheS *cache,
- int addr)
+static void xg_relax_fixups (struct trampoline_seg *ts)
{
- unsigned a = 0;
- unsigned b = cache->n_fixups;
+ struct trampoline_index *idx = &ts->index;
+ segment_info_type *seginfo = seg_info (now_seg);
+ fixS *fx;
- while (b - a > 1)
+ for (fx = seginfo->fix_root; fx; fx = fx->fx_next)
{
- unsigned c = (a + b) / 2;
-
- if (cache->fixups[c].addr < addr)
- a = c;
- else
- b = c;
- }
- return a;
-}
+ fixS *fixP = fx;
-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));
+ while (xg_is_relaxable_fixup (fixP))
+ fixP = xg_relax_fixup (idx, fixP);
}
- cache->fixups[i] = o;
- ++cache->n_fixups;
- return TRUE;
}
-static void xg_relax_trampoline (fragS *fragP, long stretch, long *new_stretch)
+/* Given a trampoline frag relax all jumps that might want to use this
+ trampoline. Only do real work once per relaxation cycle, when
+ 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)
{
- static fixup_cacheS fixup_cache;
- segment_info_type *seginfo = seg_info (now_seg);
- int trampaddr = fragP->fr_address + fragP->fr_fix;
- int searchaddr = trampaddr < J_RANGE ? 0 : trampaddr - J_RANGE;
- unsigned i;
-
- if (now_seg != fixup_cache.seg ||
- fragP == fixup_cache.first_frag ||
- fixup_cache.first_frag == NULL)
- {
- xtensa_cache_relaxable_fixups (&fixup_cache, seginfo);
- fixup_cache.seg = now_seg;
- fixup_cache.first_frag = fragP;
- }
-
- /* Scan for jumps that will not reach. */
- for (i = xtensa_find_first_cached_fixup (&fixup_cache, searchaddr);
- i < fixup_cache.n_fixups; ++i)
-
- {
- 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);
- 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)
- {
- /* 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);
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
- 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;
- }
- }
- }
- }
- }
+ if (ts->index.n_entries && ts->index.entry[0] == fragP)
+ xg_relax_fixups (ts);
}
/* 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)
{
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;
- int best_delta = 0;
- int best_addr = 0;
+ offsetT best_delta = 0;
+ offsetT best_addr = 0;
symbolS *sym = tinsn->tok[0].X_add_symbol;
offsetT target = S_GET_VALUE (sym) + tinsn->tok[0].X_add_number;
offsetT addr = fragP->fr_address;
offsetT lower = (addr < target) ? addr : target;
offsetT upper = (addr > target) ? addr : target;
- int delta = upper - lower;
+ offsetT delta = upper - lower;
offsetT midpoint = lower + delta / 2;
- int this_delta = -1;
- int this_addr = -1;
+ offsetT this_delta = -1;
+ offsetT this_addr = -1;
+
+ if (!ts)
+ return NULL;
if (delta > 2 * J_RANGE)
{
/* One trampoline won't do; we need multiple.
Choose the farthest trampoline that's still in range of the original
and let a later pass finish the job. */
- for ( ; tf; tf = tf->tc_frag_data.next_trampoline)
+ for (i = 0; i < ts->index.n_entries; ++i)
{
- fragS *next = tf->tc_frag_data.next_trampoline;
- int next_addr = next ? next->fr_address + next->fr_fix : 0;
-
+ tf = ts->index.entry[i];
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. */
- 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;
}
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 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_delta = abs (this_addr - midpoint);
+ this_delta = labs (this_addr - midpoint);
if (unreachable_only && tf->tc_frag_data.needs_jump_around)
continue;
if (!best_tf || this_delta < best_delta)
@@ -9928,8 +9880,8 @@ search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
if (best_tf &&
best_delta < J_RANGE &&
- abs(best_addr - lower) < J_RANGE &&
- abs(best_addr - upper) < J_RANGE)
+ labs(best_addr - lower) < J_RANGE &&
+ labs(best_addr - upper) < J_RANGE)
return best_tf;
return NULL; /* No suitable trampoline found. */
@@ -9950,24 +9902,10 @@ get_best_trampoline (TInsn *tinsn, fragS *fragP)
}
-static void
-check_and_update_trampolines (void)
-{
- struct trampoline_seg *ts = find_trampoline_seg (now_seg);
- 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;
- }
- }
-}
-
+/* Append jump to sym + offset to the end of the trampoline frag fragP.
+ 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
+ frag index). Return fixup for the newly created jump. */
static fixS *xg_append_jump (fragS *fragP, symbolS *sym, offsetT offset)
{
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)
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;
}
@@ -10048,7 +9993,14 @@ add_jump_to_trampoline (fragS *tramp, fragS *origfrag)
origfrag->tc_frag_data.slot_offsets[slot] = tramp->fr_fix - 3;
/* 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;
}