GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
+ the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
#include "tc-xtensa.h"
#include "subsegs.h"
#include "xtensa-relax.h"
-#include "xtensa-istack.h"
#include "dwarf2dbg.h"
+#include "xtensa-istack.h"
#include "struc-symbol.h"
#include "xtensa-config.h"
/* 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;
static xtensa_opcode xtensa_callx12_opcode;
static xtensa_opcode xtensa_const16_opcode;
static xtensa_opcode xtensa_entry_opcode;
+static xtensa_opcode xtensa_extui_opcode;
static xtensa_opcode xtensa_movi_opcode;
static xtensa_opcode xtensa_movi_n_opcode;
static xtensa_opcode xtensa_isync_opcode;
}
-/* The "loops_ok" argument is provided to allow ignoring labels that
- define loop ends. This fixes a bug where the NOPs to align a
- loop opcode were included in a previous zero-cost loop:
-
- loop a0, loopend
- <loop1 body>
- loopend:
-
- loop a2, loopend2
- <loop2 body>
-
- would become:
-
- loop a0, loopend
- <loop1 body>
- nop.n <===== bad!
- loopend:
-
- loop a2, loopend2
- <loop2 body>
-
- This argument is used to prevent moving the NOP to before the
- loop-end label, which is what you want in this special case. */
-
static void
-xtensa_move_labels (fragS *new_frag, valueT new_offset, bfd_boolean loops_ok)
+xtensa_move_labels (fragS *new_frag, valueT new_offset)
{
sym_list *lit;
for (lit = insn_labels; lit; lit = lit->next)
{
symbolS *lit_sym = lit->sym;
- if (loops_ok || ! symbol_get_tc (lit_sym)->is_loop_target)
- {
- S_SET_VALUE (lit_sym, new_offset);
- symbol_set_frag (lit_sym, new_frag);
- }
+ S_SET_VALUE (lit_sym, new_offset);
+ symbol_set_frag (lit_sym, new_frag);
}
}
char *opname = *popname;
bfd_boolean has_underbar = FALSE;
- if (cur_vinsn.inside_bundle)
- return 0;
-
if (*opname == '_')
{
has_underbar = TRUE;
return 0;
}
- if (xtensa_nop_opcode == XTENSA_UNDEFINED
+ /* Don't do anything special with NOPs inside FLIX instructions. They
+ are handled elsewhere. Real NOP instructions are always available
+ in configurations with FLIX, so this should never be an issue but
+ check for it anyway. */
+ if (!cur_vinsn.inside_bundle && xtensa_nop_opcode == XTENSA_UNDEFINED
&& strcmp (opname, "nop") == 0)
{
if (use_transform () && !has_underbar && density_supported)
BuildOp *op;
symbolS *sym;
- memset (targ, 0, sizeof (TInsn));
- targ->linenum = insn->linenum;
+ tinsn_init (targ);
+ targ->debug_line = insn->debug_line;
+ targ->loc_directive_seen = insn->loc_directive_seen;
switch (bi->typ)
{
case INSTR_INSTR:
\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;
case INSTR_INSTR:
new_insn->insn_type = ITYPE_INSN;
new_insn->opcode = instr_spec->opcode;
- new_insn->is_specific_opcode = FALSE;
- new_insn->linenum = old_insn->linenum;
break;
case INSTR_LITERAL_DEF:
new_insn->insn_type = ITYPE_LITERAL;
new_insn->opcode = XTENSA_UNDEFINED;
- new_insn->is_specific_opcode = FALSE;
- new_insn->linenum = old_insn->linenum;
break;
case INSTR_LABEL_DEF:
- as_bad (_("INSTR_LABEL_DEF not supported yet"));
- break;
+ abort ();
}
+ new_insn->is_specific_opcode = FALSE;
+ new_insn->debug_line = old_insn->debug_line;
+ new_insn->loc_directive_seen = old_insn->loc_directive_seen;
for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
{
TInsn new_insn;
bfd_boolean do_expand;
- memset (&new_insn, 0, sizeof (TInsn));
+ tinsn_init (&new_insn);
/* Narrow it if we can. xg_simplify_insn now does all the
appropriate checking (e.g., for the density option). */
if (tinsn_has_invalid_symbolic_operands (orig_insn))
return TRUE;
+ /* Special case for extui opcode which has constraints not handled
+ by the ordinary operand encoding checks. The number of operands
+ and related syntax issues have already been checked. */
+ if (orig_insn->opcode == xtensa_extui_opcode)
+ {
+ int shiftimm = orig_insn->tok[2].X_add_number;
+ int maskimm = orig_insn->tok[3].X_add_number;
+ if (shiftimm + maskimm > 32)
+ {
+ as_bad (_("immediate operands sum to greater than 32"));
+ return TRUE;
+ }
+ }
+
/* If the instruction will definitely need to be relaxed, it is better
to expand it now for better scheduling. Decide whether to expand
now.... */
/* 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 bfd_boolean
relaxable_section (asection *sec)
{
- return (sec->flags & SEC_DEBUGGING) == 0;
+ return ((sec->flags & SEC_DEBUGGING) == 0
+ && strcmp (sec->name, ".eh_frame") != 0);
+}
+
+
+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;
+ }
+ }
+ }
+ }
+ }
}
xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
+ xtensa_extui_opcode = xtensa_opcode_lookup (isa, "extui");
xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
- xtensa_move_labels (frag_now, 0, TRUE);
+ xtensa_move_labels (frag_now, 0);
}
/* No target aligning in the absolute section. */
RELAX_DESIRE_ALIGN_IF_TARGET,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
- xtensa_move_labels (frag_now, 0, TRUE);
+ xtensa_move_labels (frag_now, 0);
}
/* We need to mark the following properties even if we aren't aligning. */
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"));
md_assemble (char *str)
{
xtensa_isa isa = xtensa_default_isa;
- char *opname, *file_name;
+ char *opname;
unsigned opnamelen;
bfd_boolean has_underbar = FALSE;
char *arg_strings[MAX_INSN_ARGS];
return;
}
- /* A FLIX bundle may be spread across multiple input lines. We want to
- report the first such line in the debug information. Record the line
- number for each TInsn (assume the file name doesn't change), so the
- first line can be found later. */
- as_where (&file_name, &orig_insn.linenum);
+ /* Record the line number for each TInsn, because a FLIX bundle may be
+ spread across multiple input lines and individual instructions may be
+ moved around in some cases. */
+ orig_insn.loc_directive_seen = dwarf2_loc_directive_seen;
+ dwarf2_where (&orig_insn.debug_line);
+ dwarf2_consume_line_info ();
xg_add_branch_and_loop_targets (&orig_insn);
as_bad_where (fragP->fr_file, fragP->fr_line,
_("unaligned entry instruction"));
}
+
+ if (linkrelax && fragP->fr_type == rs_org)
+ fragP->fr_subtype = RELAX_ORG;
}
}
+/* tc_symbol_new_hook */
+
+symbolS *expr_symbols = NULL;
+
+void
+xtensa_symbol_new_hook (symbolS *sym)
+{
+ if (S_GET_SEGMENT (sym) == expr_section)
+ {
+ symbol_get_tc (sym)->next_expr_symbol = expr_symbols;
+ expr_symbols = sym;
+ }
+}
+
+
+
void
md_apply_fix (fixS *fixP, valueT *valP, segT seg)
{
char *
md_atof (int type, char *litP, int *sizeP)
{
- int prec;
- LITTLENUM_TYPE words[4];
- char *t;
- int i;
-
- switch (type)
- {
- case 'f':
- prec = 2;
- break;
-
- case 'd':
- prec = 4;
- break;
-
- default:
- *sizeP = 0;
- return "bad call to md_atof";
- }
-
- t = atof_ieee (input_line_pointer, type, words);
- if (t)
- input_line_pointer = t;
-
- *sizeP = prec * 2;
-
- for (i = prec - 1; i >= 0; i--)
- {
- int idx = i;
- if (target_big_endian)
- idx = (prec - 1 - i);
-
- md_number_to_chars (litP, (valueT) words[idx], 2);
- litP += 2;
- }
-
- return NULL;
+ return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
bfd_boolean is_jump = FALSE;
bfd_boolean is_branch = FALSE;
xtensa_isa isa = xtensa_default_isa;
- int i;
int insn_size;
int extra_space;
char *f = NULL;
int slot;
- unsigned current_line, best_linenum;
- char *current_file;
+ struct dwarf2_line_info debug_line;
+ bfd_boolean loc_directive_seen = FALSE;
+ TInsn *tinsn;
- best_linenum = UINT_MAX;
+ memset (&debug_line, 0, sizeof (struct dwarf2_line_info));
if (generating_literals)
{
xtensa_set_frag_assembly_state (frag_now);
}
- for (i = 0; i < vinsn->num_slots; i++)
+ for (slot = 0; slot < vinsn->num_slots; slot++)
{
+ tinsn = &vinsn->slots[slot];
+
/* See if the instruction implies an aligned section. */
- if (xtensa_opcode_is_loop (isa, vinsn->slots[i].opcode) == 1)
+ if (xtensa_opcode_is_loop (isa, tinsn->opcode) == 1)
record_alignment (now_seg, 2);
- /* Also determine the best line number for debug info. */
- best_linenum = vinsn->slots[i].linenum < best_linenum
- ? vinsn->slots[i].linenum : best_linenum;
+ /* Determine the best line number for debug info. */
+ if ((tinsn->loc_directive_seen || !loc_directive_seen)
+ && (tinsn->debug_line.filenum != debug_line.filenum
+ || tinsn->debug_line.line < debug_line.line
+ || tinsn->debug_line.column < debug_line.column))
+ debug_line = tinsn->debug_line;
+ if (tinsn->loc_directive_seen)
+ loc_directive_seen = TRUE;
}
/* Special cases for instructions that force an alignment... */
frag_var (rs_machine_dependent, 0, 0,
RELAX_CHECK_ALIGN_NEXT_OPCODE, target_sym, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
-
- xtensa_move_labels (frag_now, 0, FALSE);
}
if (vinsn->slots[0].opcode == xtensa_entry_opcode
&& !vinsn->slots[0].is_specific_opcode)
{
xtensa_mark_literal_pool_location ();
- xtensa_move_labels (frag_now, 0, TRUE);
+ xtensa_move_labels (frag_now, 0);
frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
}
xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
- /* Temporarily set the logical line number to the one we want to appear
- in the debug information. */
- as_where (¤t_file, ¤t_line);
- new_logical_line (current_file, best_linenum);
- dwarf2_emit_insn (insn_size + extra_space);
- new_logical_line (current_file, current_line);
+ if (debug_type == DEBUG_DWARF2 || loc_directive_seen)
+ dwarf2_gen_line_info (frag_now_fix () - (insn_size + extra_space),
+ &debug_line);
for (slot = 0; slot < vinsn->num_slots; slot++)
{
- TInsn *tinsn = &vinsn->slots[slot];
+ tinsn = &vinsn->slots[slot];
frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
static void xtensa_fix_target_frags (void);
static void xtensa_mark_narrow_branches (void);
static void xtensa_mark_zcl_first_insns (void);
+static void xtensa_mark_difference_of_two_symbols (void);
static void xtensa_fix_a0_b_retw_frags (void);
static void xtensa_fix_b_j_loop_end_frags (void);
static void xtensa_fix_close_loop_end_frags (void);
}
+/* Some difference-of-symbols expressions make it out to the linker. Some
+ don't. If one does, then the linker can optimize between the two labels.
+ If it doesn't, then the linker shouldn't. */
+
+static void
+xtensa_mark_difference_of_two_symbols (void)
+{
+ symbolS *expr_sym;
+
+ for (expr_sym = expr_symbols; expr_sym;
+ expr_sym = symbol_get_tc (expr_sym)->next_expr_symbol)
+ {
+ expressionS *expr = symbol_get_value_expression (expr_sym);
+
+ if (expr->X_op == O_subtract)
+ {
+ symbolS *left = expr->X_add_symbol;
+ symbolS *right = expr->X_op_symbol;
+
+ /* Difference of two symbols not in the same section
+ are handled with relocations in the linker. */
+ if (S_GET_SEGMENT (left) == S_GET_SEGMENT (right))
+ {
+ fragS *start;
+ fragS *end;
+
+ if (symbol_get_frag (left)->fr_address
+ <= symbol_get_frag (right)->fr_address)
+ {
+ start = symbol_get_frag (left);
+ end = symbol_get_frag (right);
+ }
+ else
+ {
+ start = symbol_get_frag (right);
+ end = symbol_get_frag (left);
+ }
+ do
+ {
+ start->tc_frag_data.is_no_transform = 1;
+ start = start->fr_next;
+ }
+ while (start && start->fr_address < end->fr_address);
+ }
+ }
+ }
+}
+
+
/* Re-process all of the fragments looking to convert all of the
RELAX_ADD_NOP_IF_A0_B_RETW. If the next instruction is a
conditional branch or a retw/retw.n, convert this frag to one that
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_mark_difference_of_two_symbols ();
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)
TInsn *insn;
assert (!istack_full (stack));
insn = &stack->insn[rec];
- memset (insn, 0, sizeof (TInsn));
+ tinsn_init (insn);
stack->ninsn++;
return insn;
}
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
stack->ninsn--;
- memset (&stack->insn[rec], 0, sizeof (TInsn));
+ tinsn_init (&stack->insn[rec]);
}
\f