/* Instruction only properties about code. */
#define XTENSA_PROP_INSN_NO_DENSITY 0x00000040
#define XTENSA_PROP_INSN_NO_REORDER 0x00000080
-#define XTENSA_PROP_INSN_NO_TRANSFORM 0x00000100
+/* Historically, NO_TRANSFORM was a property of instructions,
+ but it should apply to literals under certain circumstances. */
+#define XTENSA_PROP_NO_TRANSFORM 0x00000100
/* Branch target alignment information. This transmits information
to the linker optimization about the priority of aligning a
unsigned is_data : 1;
unsigned is_unreachable : 1;
+ /* is_specific_opcode implies no_transform. */
+ unsigned is_no_transform : 1;
+
struct
{
unsigned is_loop_target : 1;
unsigned is_no_density : 1;
/* no_longcalls flag does not need to be placed in the object file. */
- /* is_specific_opcode implies no_transform. */
- unsigned is_no_transform : 1;
unsigned is_no_reorder : 1;
\f
/* Relax the assembly instruction at least "min_steps".
- Return the number of steps taken. */
+ Return the number of steps taken.
+
+ For relaxation to correctly terminate, every relaxation chain must
+ terminate in one of two ways:
+
+ 1. If the chain from one instruction to the next consists entirely of
+ single instructions, then the chain *must* handle all possible
+ immediates without failing. It must not ever fail because an
+ immediate is out of range. The MOVI.N -> MOVI -> L32R relaxation
+ chain is one example. L32R loads 32 bits, and there cannot be an
+ immediate larger than 32 bits, so it satisfies this condition.
+ Single instruction relaxation chains are as defined by
+ xg_is_single_relaxable_instruction.
+
+ 2. Otherwise, the chain must end in a multi-instruction expansion: e.g.,
+ BNEZ.N -> BNEZ -> BNEZ.W15 -> BENZ.N/J
+
+ Strictly speaking, in most cases you can violate condition 1 and be OK
+ -- in particular when the last two instructions have the same single
+ size. But nevertheless, you should guarantee the above two conditions.
+
+ We could fix this so that single-instruction expansions correctly
+ terminate when they can't handle the range, but the error messages are
+ worse, and it actually turns out that in every case but one (18-bit wide
+ branches), you need a multi-instruction expansion to get the full range
+ anyway. And because 18-bit branches are handled identically to 15-bit
+ branches, there isn't any point in changing it. */
static int
xg_assembly_relax (IStack *istack,
{
int steps_taken = 0;
- /* assert (has no symbolic operands)
- Some of its immeds don't fit.
- Try to build a relaxed version.
- This may go through a couple of stages
- of single instruction transformations before
- we get there. */
+ /* Some of its immeds don't fit. Try to build a relaxed version.
+ This may go through a couple of stages of single instruction
+ transformations before we get there. */
TInsn single_target;
TInsn current_insn;
/* If an instruction is about to grow, return the longer size. */
if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
- || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2)
+ || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2
+ || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP3)
return 3;
if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
fixes into this frchain's fix list. */
pool_location = frag_now;
frag_now->tc_frag_data.lit_frchain = frchain_now;
+ frag_now->tc_frag_data.literal_frag = frag_now;
frag_variant (rs_machine_dependent, 0, 0,
RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
}
+static void
+xtensa_mark_frags_for_org (void)
+{
+ segT *seclist;
+
+ /* Walk over each fragment of all of the current segments. If we find
+ a .org frag in any of the segments, mark all frags prior to it as
+ "no transform", which will prevent linker optimizations from messing
+ up the .org distance. This should be done after
+ xtensa_find_unmarked_state_frags, because we don't want to worry here
+ about that function trashing the data we save here. */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+ segment_info_type *seginfo;
+ fragS *fragP;
+ flagword flags;
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ seginfo = seg_info (sec);
+ if (seginfo && seginfo->frchainP)
+ {
+ fragS *last_fragP = seginfo->frchainP->frch_root;
+ for (fragP = seginfo->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ /* cvt_frag_to_fill has changed the fr_type of org frags to
+ rs_fill, so use the value as cached in rs_subtype here. */
+ if (fragP->fr_subtype == RELAX_ORG)
+ {
+ while (last_fragP != fragP->fr_next)
+ {
+ last_fragP->tc_frag_data.is_no_transform = TRUE;
+ last_fragP = last_fragP->fr_next;
+ }
+ }
+ }
+ }
+ }
+}
+
+
static void
xtensa_find_unmarked_state_frags (void)
{
void
xtensa_flush_pending_output (void)
{
+ /* This line fixes a bug where automatically generated gstabs info
+ separates a function label from its entry instruction, ending up
+ with the literal position between the function label and the entry
+ instruction and crashing code. It only happens with --gstabs and
+ --text-section-literals, and when several other obscure relaxation
+ conditions are met. */
+ if (outputting_stabs_line_debug)
+ return;
+
if (cur_vinsn.inside_bundle)
as_bad (_("missing closing brace"));
as_bad_where (fragP->fr_file, fragP->fr_line,
_("unaligned entry instruction"));
}
+
+ if (linkrelax && fragP->fr_type == rs_org)
+ fragP->fr_subtype = RELAX_ORG;
}
case RELAX_IMMED:
case RELAX_IMMED_STEP1:
case RELAX_IMMED_STEP2:
+ case RELAX_IMMED_STEP3:
/* Place the immediate. */
new_stretch += relax_frag_immed
(now_seg, fragP, stretch,
case RELAX_IMMED:
case RELAX_IMMED_STEP1:
case RELAX_IMMED_STEP2:
+ case RELAX_IMMED_STEP3:
/* Place the immediate. */
convert_frag_immed
(sec, fragp,
target = 0;
}
- know (symbolP);
- know (symbolP->sy_frag);
- know (!(S_GET_SEGMENT (symbolP) == absolute_section)
- || symbol_get_frag (symbolP) == &zero_address_frag);
-
loop_length = target - (fragP->fr_address + fragP->fr_fix);
loop_length_hi = loop_length & ~0x0ff;
loop_length_lo = loop_length & 0x0ff;
frchain_to = literal_pool->tc_frag_data.lit_frchain;
assert (frchain_to);
}
- insert_after = literal_pool;
-
- while (insert_after->fr_next->fr_subtype != RELAX_LITERAL_POOL_END)
- insert_after = insert_after->fr_next;
-
+ insert_after = literal_pool->tc_frag_data.literal_frag;
dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
*frag_splice = next_frag;
search_frag->fr_next = insert_after->fr_next;
insert_after->fr_next = search_frag;
search_frag->tc_frag_data.lit_seg = dest_seg;
+ literal_pool->tc_frag_data.literal_frag = search_frag;
/* Now move any fixups associated with this frag to the
right section. */
xtensa_move_seg_list_to_beginning (literal_head);
xtensa_find_unmarked_state_frags ();
+ xtensa_mark_frags_for_org ();
xtensa_create_property_segments (get_frag_is_literal,
NULL,
xtensa_frag_flags_init (prop_flags);
if (fragP->tc_frag_data.is_literal)
prop_flags->is_literal = TRUE;
+ if (fragP->tc_frag_data.is_specific_opcode
+ || fragP->tc_frag_data.is_no_transform)
+ prop_flags->is_no_transform = TRUE;
if (fragP->tc_frag_data.is_unreachable)
prop_flags->is_unreachable = TRUE;
else if (fragP->tc_frag_data.is_insn)
prop_flags->insn.is_loop_target = TRUE;
if (fragP->tc_frag_data.is_branch_target)
prop_flags->insn.is_branch_target = TRUE;
- if (fragP->tc_frag_data.is_specific_opcode
- || fragP->tc_frag_data.is_no_transform)
- prop_flags->insn.is_no_transform = TRUE;
if (fragP->tc_frag_data.is_no_density)
prop_flags->insn.is_no_density = TRUE;
if (fragP->tc_frag_data.use_absolute_literals)
if (prop_flags->insn.is_no_density)
num |= XTENSA_PROP_INSN_NO_DENSITY;
- if (prop_flags->insn.is_no_transform)
- num |= XTENSA_PROP_INSN_NO_TRANSFORM;
+ if (prop_flags->is_no_transform)
+ num |= XTENSA_PROP_NO_TRANSFORM;
if (prop_flags->insn.is_no_reorder)
num |= XTENSA_PROP_INSN_NO_REORDER;
if (prop_flags->insn.is_abslit)
if (prop_flags_1->insn.is_no_density !=
prop_flags_2->insn.is_no_density)
return FALSE;
- if (prop_flags_1->insn.is_no_transform !=
- prop_flags_2->insn.is_no_transform)
+ if (prop_flags_1->is_no_transform !=
+ prop_flags_2->is_no_transform)
return FALSE;
if (prop_flags_1->insn.is_no_reorder !=
prop_flags_2->insn.is_no_reorder)