+ 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;
+
+ /* Don't choose trampoline that contains the source. */
+ if (source >= trampoline_frag->fr_address
+ && source <= trampoline_frag->fr_address +
+ trampoline_frag->fr_fix)
+ 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 bool xg_is_relaxable_fixup (fixS *fixP)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ addressT addr = fixP->fx_frag->fr_address;
+ addressT target;
+ offsetT delta;
+ symbolS *s = fixP->fx_addsy;
+ int slot;
+ xtensa_format fmt;
+ xtensa_opcode opcode;
+
+ 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) + fixP->fx_offset;
+ delta = target - addr;
+
+ if (labs (delta) < J_RANGE - J_MARGIN)
+ return false;
+
+ xtensa_insnbuf_from_chars (isa, trampoline_buf,
+ (unsigned char *) fixP->fx_frag->fr_literal +
+ fixP->fx_where, 0);
+ fmt = xtensa_format_decode (isa, trampoline_buf);
+ gas_assert (fmt != XTENSA_UNDEFINED);
+ 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);
+ return opcode == xtensa_j_opcode;
+}
+
+static void xg_relax_fixups (struct trampoline_seg *ts)
+{
+ struct trampoline_index *idx = &ts->index;
+ segment_info_type *seginfo = seg_info (now_seg);
+ fixS *fx;
+
+ for (fx = seginfo->fix_root; fx; fx = fx->fx_next)
+ {
+ fixS *fixP = fx;
+ struct trampoline_chain *tc = NULL;
+
+ if (xg_is_relaxable_fixup (fixP))
+ {
+ tc = xg_find_best_eq_target (ts, fixP->fx_frag->fr_address,
+ &fixP->fx_addsy, &fixP->fx_offset);
+ if (!tc)
+ tc = xg_create_trampoline_chain (ts, fixP->fx_addsy,
+ fixP->fx_offset);
+ gas_assert (tc);
+ }
+
+ while (xg_is_relaxable_fixup (fixP))
+ {
+ fixP = xg_relax_fixup (idx, fixP);
+ xg_add_location_to_chain (tc, fixP->fx_frag->fr_symbol,
+ fixP->fx_where);
+ }
+ }
+}
+
+/* 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)
+{
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+
+ if (ts->index.n_entries && ts->index.entry[0] == fragP)
+ xg_relax_fixups (ts);
+}