/* tc-xtensa.c -- Assemble Xtensa instructions.
- Copyright 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+ Copyright (C) 2003-2016 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston,
MA 02110-1301, USA. */
-#include <limits.h>
#include "as.h"
+#include <limits.h>
#include "sb.h"
#include "safe-ctype.h"
#include "tc-xtensa.h"
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
-/* Maximum width we would pad an unreachable frag to get alignment. */
-#define UNREACHABLE_MAX_WIDTH 8
-
static vliw_insn cur_vinsn;
unsigned xtensa_num_pipe_stages;
#endif
};
+/* A circular list of all potential and actual literal pool locations
+ in a segment. */
+struct litpool_frag
+{
+ struct litpool_frag *next;
+ struct litpool_frag *prev;
+ fragS *fragP;
+ addressT addr;
+ short priority; /* 1, 2, or 3 -- 1 is highest */
+ short original_priority;
+};
+
+/* Map a segment to its litpool_frag list. */
+struct litpool_seg
+{
+ struct litpool_seg *next;
+ asection *seg;
+ struct litpool_frag frag_list;
+ int frag_count; /* since last litpool location */
+};
+
+static struct litpool_seg litpool_seg_list;
+
/* Directive functions. */
static void finish_vinsn (vliw_insn *);
static bfd_boolean emit_single_op (TInsn *);
static int total_frag_text_expansion (fragS *);
+static bfd_boolean use_trampolines = TRUE;
+static void xtensa_check_frag_count (void);
+static void xtensa_create_trampoline_frag (bfd_boolean);
+static void xtensa_maybe_create_trampoline_frag (void);
+struct trampoline_frag;
+static int init_trampoline_frag (struct trampoline_frag *);
+static void xtensa_maybe_create_literal_pool_frag (bfd_boolean, bfd_boolean);
+static bfd_boolean auto_litpools = FALSE;
+static int auto_litpool_limit = 10000;
/* Alignment Functions. */
static void tinsn_immed_from_frag (TInsn *, fragS *, int);
static int get_num_stack_text_bytes (IStack *);
static int get_num_stack_literal_bytes (IStack *);
+static bfd_boolean tinsn_to_slotbuf (xtensa_format, int, TInsn *, xtensa_insnbuf);
/* vliw_insn functions. */
static void xg_init_vinsn (vliw_insn *);
+static void xg_copy_vinsn (vliw_insn *, vliw_insn *);
static void xg_clear_vinsn (vliw_insn *);
static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
static void xg_free_vinsn (vliw_insn *);
static xtensa_opcode xtensa_retw_n_opcode;
static xtensa_opcode xtensa_rsr_lcount_opcode;
static xtensa_opcode xtensa_waiti_opcode;
+static int config_max_slots = 0;
\f
/* Command-line Options. */
option_prefer_l32r,
option_prefer_const16,
- option_target_hardware
+ option_target_hardware,
+
+ option_trampolines,
+ option_no_trampolines,
+
+ option_auto_litpools,
+ option_no_auto_litpools,
+ option_auto_litpool_limit,
};
const char *md_shortopts = "";
{ "target-hardware", required_argument, NULL, option_target_hardware },
+ { "trampolines", no_argument, NULL, option_trampolines },
+ { "no-trampolines", no_argument, NULL, option_no_trampolines },
+
+ { "auto-litpools", no_argument, NULL, option_auto_litpools },
+ { "no-auto-litpools", no_argument, NULL, option_no_auto_litpools },
+ { "auto-litpool-limit", required_argument, NULL, option_auto_litpool_limit },
+
{ NULL, no_argument, NULL, 0 }
};
directive_state[directive_transform] = FALSE;
return 1;
+ case option_trampolines:
+ use_trampolines = TRUE;
+ return 1;
+
+ case option_no_trampolines:
+ use_trampolines = FALSE;
+ return 1;
+
+ case option_auto_litpools:
+ auto_litpools = TRUE;
+ use_literal_section = FALSE;
+ return 1;
+
+ case option_no_auto_litpools:
+ auto_litpools = FALSE;
+ auto_litpool_limit = -1;
+ return 1;
+
+ case option_auto_litpool_limit:
+ {
+ int value = 0;
+ if (auto_litpool_limit < 0)
+ as_fatal (_("no-auto-litpools is incompatible with auto-litpool-limit"));
+ if (*arg == 0 || *arg == '-')
+ as_fatal (_("invalid auto-litpool-limit argument"));
+ value = strtol (arg, &arg, 10);
+ if (*arg != 0)
+ as_fatal (_("invalid auto-litpool-limit argument"));
+ if (value < 100 || value > 10000)
+ as_fatal (_("invalid auto-litpool-limit argument (range is 100-10000)"));
+ auto_litpool_limit = value;
+ auto_litpools = TRUE;
+ use_literal_section = FALSE;
+ return 1;
+ }
+
default:
return 0;
}
flix bundles\n\
--no-allow-flix neither allow hand-written nor generate\n\
flix bundles\n\
- --rename-section old=new Rename section 'old' to 'new'\n", stream);
+ --rename-section old=new Rename section 'old' to 'new'\n\
+ --[no-]trampolines [Do not] generate trampolines (jumps to jumps)\n\
+ when jumps do not reach their targets\n\
+ --[no-]auto-litpools [Do not] automatically create literal pools\n\
+ --auto-litpool-limit=<value>\n\
+ (range 100-10000) Maximum number of blocks of\n\
+ instructions to emit between literal pool\n\
+ locations; implies --auto-litpools flag\n", stream);
}
\f
{
/* After md_end, you should be checking frag by frag, rather
than state directives. */
- assert (!past_xtensa_end);
+ gas_assert (!past_xtensa_end);
return directive_state[directive_transform];
}
/* Do not use this function after md_end; just look at align_targets
instead. There is no target-align directive, so alignment is either
enabled for all frags or not done at all. */
- assert (!past_xtensa_end);
+ gas_assert (!past_xtensa_end);
return align_targets && use_transform ();
}
/* Allocate the literal state for this section and push
onto the directive stack. */
ls = xmalloc (sizeof (lit_state));
- assert (ls);
+ gas_assert (ls);
*ls = default_lit_sections;
directive_push (directive_literal_prefix, negated, ls);
md_flush_pending_output ();
- switch (end_directive)
+ switch ((int) end_directive)
{
- case (directiveE) XTENSA_UNDEFINED:
+ case XTENSA_UNDEFINED:
discard_rest_of_line ();
return;
- case directive_density:
+ case (int) directive_density:
as_warn (_(".end [no-]density is ignored"));
demand_empty_rest_of_line ();
break;
- case directive_absolute_literals:
+ case (int) directive_absolute_literals:
if (!absolute_literals_supported && !end_negated)
{
as_warn (_("Xtensa absolute literals option not supported; ignored"));
case directive_literal_prefix:
/* Restore the default collection sections from saved state. */
s = (lit_state *) state;
- assert (s);
+ gas_assert (s);
default_lit_sections = *s;
/* Free the state storage. */
if (use_literal_section || directive_state[directive_absolute_literals])
dest_seg = now_seg;
+ /* FIXME, despite the previous comments, dest_seg is unused... */
+ (void) dest_seg;
+
/* All literals are aligned to four-byte boundaries. */
frag_align (2, 0, 0);
record_alignment (now_seg, 2);
- c = get_symbol_end ();
+ c = get_symbol_name (&base_name);
/* Just after name is now '\0'. */
p = input_line_pointer;
*p = c;
- SKIP_WHITESPACE ();
+ SKIP_WHITESPACE_AFTER_NAME ();
if (*input_line_pointer != ',' && *input_line_pointer != ':')
{
xtensa_restore_emit_state (&state);
return;
}
- *p = 0;
+ *p = 0;
colon (base_name);
-
*p = c;
+
input_line_pointer++; /* skip ',' or ':' */
xtensa_elf_cons (4);
/* Get a null-terminated copy of the name. */
name = xmalloc (len + 1);
- assert (name);
+ gas_assert (name);
strncpy (name, input_line_pointer, len);
name[len] = 0;
{
struct suffix_reloc_map *sfx;
unsigned char operator = (unsigned char) -1;
-
+
for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
{
if (sfx->reloc == reloc)
break;
}
}
- assert (operator != (unsigned char) -1);
+ gas_assert (operator != (unsigned char) -1);
return operator;
}
{
bfd_reloc_code_real_type reloc;
segT t = expression (tok);
+
if (t == absolute_section
&& xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
{
- assert (tok->X_op == O_constant);
+ gas_assert (tok->X_op == O_constant);
tok->X_op = O_symbol;
tok->X_add_symbol = &abs_symbol;
}
input_line_pointer++;
if (num_regs == 0)
goto err;
- assert (opnd_cnt > 0);
+ gas_assert (opnd_cnt > 0);
num_regs--;
opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
if (next_reg
as_warn (_("too many arguments"));
goto err;
}
- assert (opnd_cnt < MAX_INSN_ARGS);
+ gas_assert (opnd_cnt < MAX_INSN_ARGS);
expression_maybe_register (opcode, opnd_cnt, tok);
next_reg = tok->X_add_number + 1;
last_tok = tok;
last_opnd_cnt = opnd_cnt;
+ demand_empty_rest_of_line ();
do
{
offsetT val;
opname = *popname;
- assert (opname[0] != '_');
+ gas_assert (opname[0] != '_');
if (strcmp (opname, old_op) != 0)
return 0;
}
/* Don't do anything special with NOPs inside FLIX instructions. They
- are handled elsewhere. Real NOP instructions are always available
+ 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
if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
== 1)
as_bad_where ((char *) file, line,
- _("operand %d of '%s' has out of range value '%u'"),
+ _("operand %d of '%s' has out of range value '%u'"),
operand + 1,
xtensa_opcode_name (xtensa_default_isa, opcode),
value);
{
case OP_CONSTANT:
/* The expression must be the constant. */
- assert (cond->op_num < insn->ntok);
+ gas_assert (cond->op_num < insn->ntok);
exp1 = &insn->tok[cond->op_num];
if (expr_is_const (exp1))
{
break;
case OP_OPERAND:
- assert (cond->op_num < insn->ntok);
- assert (cond->op_data < insn->ntok);
+ gas_assert (cond->op_num < insn->ntok);
+ gas_assert (cond->op_data < insn->ntok);
exp1 = &insn->tok[cond->op_num];
exp2 = &insn->tok[cond->op_data];
{
TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
TransitionList *l;
- assert (insn->opcode < table->num_opcodes);
+ gas_assert (insn->opcode < table->num_opcodes);
/* Walk through all of the possible transitions. */
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
TransitionList *l;
TransitionRule *match = 0;
- assert (insn->insn_type == ITYPE_INSN);
- assert (insn->opcode < table->num_opcodes);
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionList *l;
int max_size = xg_get_single_size (opcode);
- assert (opcode < table->num_opcodes);
+ gas_assert (opcode < table->num_opcodes);
for (l = table->table[opcode]; l != NULL; l = l->next)
{
build_list = rule->to_instr;
if (is_unique_insn_expansion (rule))
{
- assert (build_list->typ == INSTR_INSTR);
+ gas_assert (build_list->typ == INSTR_INSTR);
this_size = xg_get_max_insn_widen_size (build_list->opcode);
}
else
TransitionList *l;
int max_size = 0;
- assert (opcode < table->num_opcodes);
+ gas_assert (opcode < table->num_opcodes);
for (l = table->table[opcode]; l != NULL; l = l->next)
{
build_list = rule->to_instr;
if (is_unique_insn_expansion (rule))
{
- assert (build_list->typ == INSTR_INSTR);
+ gas_assert (build_list->typ == INSTR_INSTR);
this_size = xg_get_max_insn_widen_literal_size (build_list->opcode);
}
else
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
- assert (insn->insn_type == ITYPE_INSN);
- assert (insn->opcode < table->num_opcodes);
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
int i;
int n = insn->ntok;
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
- const expressionS *expr = &insn->tok[i];
+ const expressionS *exp = &insn->tok[i];
+
if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
continue;
- switch (expr->X_op)
+ switch (exp->X_op)
{
case O_register:
case O_constant:
- if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+ if (xg_check_operand (exp->X_add_number, insn->opcode, i))
return FALSE;
break;
default:
/* The symbol should have a fixup associated with it. */
- assert (FALSE);
+ gas_assert (FALSE);
break;
}
}
int i;
int n = insn->ntok;
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
- const expressionS *expr = &insn->tok[i];
+ const expressionS *exp = &insn->tok[i];
+
if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
continue;
- switch (expr->X_op)
+ switch (exp->X_op)
{
case O_register:
case O_constant:
- if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+ if (xg_check_operand (exp->X_add_number, insn->opcode, i))
return FALSE;
break;
/* If it is a weak symbol or a symbol in a different section,
it cannot be known to fit at assembly time. */
- if (S_IS_WEAK (expr->X_add_symbol)
- || S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
+ if (S_IS_WEAK (exp->X_add_symbol)
+ || S_GET_SEGMENT (exp->X_add_symbol) != pc_seg)
{
/* For a direct call with --no-longcalls, be optimistic and
assume it will be in range. If the symbol is weak and
symbols even if longcalls is not enabled. */
if (is_direct_call_opcode (insn->opcode)
&& ! pc_frag->tc_frag_data.use_longcalls
- && (! S_IS_WEAK (expr->X_add_symbol)
- || S_IS_DEFINED (expr->X_add_symbol)))
+ && (! S_IS_WEAK (exp->X_add_symbol)
+ || S_IS_DEFINED (exp->X_add_symbol)))
return TRUE;
return FALSE;
}
- symbolP = expr->X_add_symbol;
+ symbolP = exp->X_add_symbol;
sym_frag = symbol_get_frag (symbolP);
- target = S_GET_VALUE (symbolP) + expr->X_add_number;
+ target = S_GET_VALUE (symbolP) + exp->X_add_number;
pc = pc_frag->fr_address + pc_offset;
/* If frag has yet to be reached on this pass, assume it
int op_num = op->op_num;
int op_data = op->op_data;
- assert (op->op_num < MAX_INSN_ARGS);
+ gas_assert (op->op_num < MAX_INSN_ARGS);
if (targ->ntok <= op_num)
targ->ntok = op_num + 1;
set_expr_const (&targ->tok[op_num], op_data);
break;
case OP_OPERAND:
- assert (op_data < insn->ntok);
+ gas_assert (op_data < insn->ntok);
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
break;
case OP_FREEREG:
break;
case OP_OPERAND_HI16U:
case OP_OPERAND_LOW16U:
- assert (op_data < insn->ntok);
+ gas_assert (op_data < insn->ntok);
if (expr_is_const (&insn->tok[op_data]))
{
long val;
if (targ->opcode == XTENSA_UNDEFINED
|| (targ->opcode != xtensa_const16_opcode))
return FALSE;
- assert (op_data < insn->ntok);
+ gas_assert (op_data < insn->ntok);
/* Need to build a O_lo16 or O_hi16. */
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
if (targ->tok[op_num].X_op == O_symbol)
OP_OPERAND_F32MINUS */
if (xg_has_userdef_op_fn (op->typ))
{
- assert (op_data < insn->ntok);
+ gas_assert (op_data < insn->ntok);
if (expr_is_const (&insn->tok[op_data]))
{
long val;
return FALSE; /* We cannot use a relocation for this. */
break;
}
- assert (0);
+ gas_assert (0);
break;
}
}
{
int op_num = op->op_num;
int op_data = op->op_data;
- assert (op->op_num < MAX_INSN_ARGS);
+ gas_assert (op->op_num < MAX_INSN_ARGS);
if (targ->ntok <= op_num)
targ->ntok = op_num + 1;
switch (op->typ)
{
case OP_OPERAND:
- assert (op_data < insn->ntok);
+ gas_assert (op_data < insn->ntok);
/* We can only pass resolvable literals through. */
if (!xg_valid_literal_expression (&insn->tok[op_data]))
return FALSE;
case OP_CONSTANT:
case OP_LABEL:
default:
- assert (0);
+ gas_assert (0);
break;
}
}
targ->insn_type = ITYPE_LABEL;
targ->is_specific_opcode = FALSE;
/* Literal with no ops is a label? */
- assert (op == NULL);
+ gas_assert (op == NULL);
break;
default:
- assert (0);
+ gas_assert (0);
}
return TRUE;
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
- assert (insn->insn_type == ITYPE_INSN);
- assert (insn->opcode < table->num_opcodes);
+ gas_assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
/* Check to see if it fits. */
for (i = stack_size; i < istack->ninsn; i++)
{
- TInsn *insn = &istack->insn[i];
+ TInsn *tinsn = &istack->insn[i];
- if (insn->insn_type == ITYPE_INSN
- && !tinsn_has_symbolic_operands (insn)
- && !xg_immeds_fit (insn))
+ if (tinsn->insn_type == ITYPE_INSN
+ && !tinsn_has_symbolic_operands (tinsn)
+ && !xg_immeds_fit (tinsn))
{
istack->ninsn = stack_size;
return FALSE;
xtensa_set_frag_assembly_state (frag_now);
/* Just to make sure that we did not split it up. */
- assert (old_frag->fr_next == frag_now);
+ gas_assert (old_frag->fr_next == frag_now);
}
{
case OP_CONSTANT:
/* The expression must be the constant. */
- assert (b_op->op_num < MAX_INSN_ARGS);
+ gas_assert (b_op->op_num < MAX_INSN_ARGS);
exp = &new_insn->tok[b_op->op_num];
set_expr_const (exp, b_op->op_data);
break;
case OP_OPERAND:
- assert (b_op->op_num < MAX_INSN_ARGS);
- assert (b_op->op_data < (unsigned) old_insn->ntok);
+ gas_assert (b_op->op_num < MAX_INSN_ARGS);
+ gas_assert (b_op->op_data < (unsigned) old_insn->ntok);
src_exp = &old_insn->tok[b_op->op_data];
exp = &new_insn->tok[b_op->op_num];
copy_expr (exp, src_exp);
case OP_LITERAL:
case OP_LABEL:
as_bad (_("can't handle generation of literal/labels yet"));
- assert (0);
+ gas_assert (0);
default:
as_bad (_("can't handle undefined OP TYPE"));
- assert (0);
+ gas_assert (0);
}
}
insn_spec = rule->to_instr;
/* There should only be one. */
- assert (insn_spec != NULL);
- assert (insn_spec->next == NULL);
+ gas_assert (insn_spec != NULL);
+ gas_assert (insn_spec->next == NULL);
if (insn_spec->next != NULL)
return FALSE;
set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
- assert (insn->insn_type == ITYPE_LITERAL);
- assert (insn->ntok == 1); /* must be only one token here */
+ gas_assert (insn->insn_type == ITYPE_LITERAL);
+ gas_assert (insn->ntok == 1); /* must be only one token here */
xtensa_switch_to_literal_fragment (&state);
break;
}
- assert (frag_now->tc_frag_data.literal_frag == NULL);
+ gas_assert (frag_now->tc_frag_data.literal_frag == NULL);
frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
lit_sym = frag_now->fr_symbol;
offsetT litalign = 2; /* 2^2 = 4 */
fragS *lit_saved_frag;
- assert (size % 4 == 0);
+ gas_assert (size % 4 == 0);
xtensa_switch_to_literal_fragment (&state);
int opnum,
xtensa_format fmt,
int slot,
- expressionS *expr,
+ expressionS *exp,
fragS *fragP,
offsetT offset)
{
}
else if (opcode == xtensa_const16_opcode)
{
- if (expr->X_op == O_lo16)
+ if (exp->X_op == O_lo16)
{
reloc = encode_reloc (slot);
- expr->X_op = O_symbol;
+ exp->X_op = O_symbol;
}
- else if (expr->X_op == O_hi16)
+ else if (exp->X_op == O_hi16)
{
reloc = encode_alt_reloc (slot);
- expr->X_op = O_symbol;
+ exp->X_op = O_symbol;
}
}
/* Handle erroneous "@h" and "@l" expressions here before they propagate
into the symbol table where the generic portions of the assembler
won't know what to do with them. */
- if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
+ if (exp->X_op == O_lo16 || exp->X_op == O_hi16)
{
as_bad (_("invalid expression for operand %i of '%s'"),
opnum + 1, xtensa_opcode_name (xtensa_default_isa, opcode));
}
fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
- the_fix = fix_new_exp (fragP, offset, fmt_length, expr,
+ the_fix = fix_new_exp (fragP, offset, fmt_length, exp,
howto->pc_relative, reloc);
the_fix->fx_no_overflow = 1;
- the_fix->tc_fix_data.X_add_symbol = expr->X_add_symbol;
- the_fix->tc_fix_data.X_add_number = expr->X_add_number;
+ the_fix->tc_fix_data.X_add_symbol = exp->X_add_symbol;
+ the_fix->tc_fix_data.X_add_number = exp->X_add_number;
the_fix->tc_fix_data.slot = slot;
return TRUE;
int i;
if (lit_sym == 0)
return;
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < insn->ntok; i++)
if (insn->tok[i].X_add_symbol == sym)
insn->tok[i].X_add_symbol = lit_sym;
be accompanied by major changes to make use of that data.
In any event, we can tell that we are expanding from a single-slot
- three-byte format to a wider one with the logic below. */
+ format to a wider one with the logic below. */
- if (fmt_size <= 3 && fragP->tc_frag_data.text_expansion[0] != 3)
- return 3 + fragP->tc_frag_data.text_expansion[0];
- else
- return 3;
+ int i;
+ int relaxed_size = fmt_size + fragP->tc_frag_data.text_expansion[0];
+
+ for (i = 0; i < xtensa_isa_num_formats (isa); i++)
+ {
+ if (relaxed_size == xtensa_format_length (isa, i))
+ return relaxed_size;
+ }
+
+ return 3;
}
if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
|| next_fragP->fr_subtype == RELAX_UNREACHABLE)))
next_fragP = next_fragP->fr_next;
- assert (next_fragP->fr_type == rs_machine_dependent
+ gas_assert (next_fragP->fr_type == rs_machine_dependent
&& (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
|| next_fragP->fr_subtype == RELAX_UNREACHABLE));
|| new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
new_target = new_target->fr_next;
- assert (new_target->fr_type == rs_machine_dependent
+ gas_assert (new_target->fr_type == rs_machine_dependent
&& (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
|| new_target->fr_subtype == RELAX_DESIRE_ALIGN));
}
}
+/* As specified in the relaxation table, when a loop instruction is
+ relaxed, there are 24 bytes between the loop instruction itself and
+ the first instruction in the loop. */
+
+#define RELAXED_LOOP_INSN_BYTES 24
+
static addressT
next_frag_pre_opcode_bytes (const fragS *fragp)
{
been relaxed. Note that we can assume that the LOOP
instruction is in slot 0 because loops aren't bundleable. */
if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
- return get_expanded_loop_offset (next_opcode);
+ return get_expanded_loop_offset (next_opcode) + RELAXED_LOOP_INSN_BYTES;
return 0;
}
pool_location = frag_now;
frag_now->tc_frag_data.lit_frchain = frchain_now;
frag_now->tc_frag_data.literal_frag = frag_now;
+ /* Just record this frag. */
+ xtensa_maybe_create_literal_pool_frag (FALSE, FALSE);
frag_variant (rs_machine_dependent, 0, 0,
RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
else
tinsn->opcode = xtensa_nop_opcode;
- assert (tinsn->opcode != XTENSA_UNDEFINED);
+ gas_assert (tinsn->opcode != XTENSA_UNDEFINED);
}
}
/* This is the OFFSET of the loop instruction in the expanded loop.
This MUST correspond directly to the specification of the loop
expansion. It will be validated on fragment conversion. */
- assert (opcode != XTENSA_UNDEFINED);
+ gas_assert (opcode != XTENSA_UNDEFINED);
if (opcode == xtensa_loop_opcode)
return 0;
if (opcode == xtensa_loopnez_opcode)
static fragS *
get_literal_pool_location (segT seg)
{
+ struct litpool_seg *lps = litpool_seg_list.next;
+ struct litpool_frag *lpf;
+ for ( ; lps && lps->seg->id != seg->id; lps = lps->next)
+ ;
+ if (lps)
+ {
+ for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
+ { /* Skip "candidates" for now. */
+ if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN &&
+ lpf->priority == 1)
+ return lpf->fragP;
+ }
+ /* Must convert a lower-priority pool. */
+ for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
+ {
+ if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN)
+ return lpf->fragP;
+ }
+ /* Still no match -- try for a low priority pool. */
+ for (lpf = lps->frag_list.prev; lpf->fragP; lpf = lpf->prev)
+ {
+ if (lpf->fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
+ return lpf->fragP;
+ }
+ }
return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
}
addressT frag_addr;
xtensa_format fmt;
- xtensa_insnbuf_from_chars
- (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
- fmt = xtensa_format_decode (isa, insnbuf);
- op_size = xtensa_format_length (isa, fmt);
- frag_addr = frag->fr_address % xtensa_fetch_width;
+ if (frag->fr_fix == 0)
+ frag = next_non_empty_frag (frag);
- if (frag_addr + op_size > xtensa_fetch_width)
- as_warn_where (frag->fr_file, frag->fr_line,
- _("unaligned loop: %d bytes at 0x%lx"),
- op_size, (long) frag->fr_address);
+ if (frag)
+ {
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ frag_addr = frag->fr_address % xtensa_fetch_width;
+
+ if (frag_addr + op_size > xtensa_fetch_width)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned loop: %d bytes at 0x%lx"),
+ op_size, (long) frag->fr_address);
+ }
}
frag = frag->fr_next;
}
segT current_section = now_seg;
int current_subsec = now_subseg;
xtensa_isa isa;
+ int i;
xtensa_default_isa = xtensa_isa_init (0, 0);
isa = xtensa_default_isa;
subseg_set (current_section, current_subsec);
- xg_init_vinsn (&cur_vinsn);
-
xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
+ for (i = 0; i < xtensa_isa_num_formats (isa); i++)
+ {
+ int format_slots = xtensa_format_num_slots (isa, i);
+ if (format_slots > config_max_slots)
+ config_max_slots = format_slots;
+ }
+
+ xg_init_vinsn (&cur_vinsn);
+
xtensa_num_pipe_stages = xtensa_isa_num_pipe_stages (isa);
init_op_placement_info_table ();
/* No target aligning in the absolute section. */
if (now_seg != absolute_section
- && do_align_targets ()
&& !is_unaligned_label (sym)
&& !generating_literals)
{
xtensa_set_frag_assembly_state (frag_now);
- frag_var (rs_machine_dependent,
- 0, (int) freq,
- RELAX_DESIRE_ALIGN_IF_TARGET,
- frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ if (do_align_targets ())
+ frag_var (rs_machine_dependent, 0, (int) freq,
+ RELAX_DESIRE_ALIGN_IF_TARGET, frag_now->fr_symbol,
+ frag_now->fr_offset, NULL);
+ else
+ frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
xtensa_move_labels (frag_now, 0);
}
bfd_reloc_code_real_type reloc;
char *old_input_line_pointer;
expressionS *tok = &orig_insn.extra_arg;
- segT t;
old_input_line_pointer = input_line_pointer;
input_line_pointer = arg_strings[num_args - 1];
- t = expression (tok);
+ expression (tok);
if (tok->X_op == O_symbol
&& ((reloc = xtensa_elf_suffix (&input_line_pointer, tok))
== BFD_RELOC_XTENSA_TLS_CALL))
/* We've just emitted a new instruction so clear the list of labels. */
xtensa_clear_insn_labels ();
+
+ xtensa_check_frag_count ();
}
&& ! fragP->tc_frag_data.is_literal
&& (fragP->fr_type == rs_align
|| fragP->fr_type == rs_align_code)
- && fragP->fr_address + fragP->fr_fix > 0
&& fragP->fr_offset > 0
&& now_seg != bss_section)
{
bfd_boolean
xtensa_fix_adjustable (fixS *fixP)
{
- /* An offset is not allowed in combination with the difference of two
- symbols, but that cannot be easily detected after a local symbol
- has been adjusted to a (section+offset) form. Return 0 so that such
- an fix will not be adjusted. */
- if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
- && relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
- return 0;
-
/* We need the symbol name for the VTABLE entries. */
if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
symbolS *expr_symbols = NULL;
-void
+void
xtensa_symbol_new_hook (symbolS *sym)
{
if (is_leb128_expr && S_GET_SEGMENT (sym) == expr_section)
{
case BFD_RELOC_8:
fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+ fixP->fx_signed = 0;
break;
case BFD_RELOC_16:
fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+ fixP->fx_signed = 0;
break;
case BFD_RELOC_32:
fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+ fixP->fx_signed = 0;
break;
default:
break;
}
- /* An offset is only allowed when it results from adjusting a
- local symbol into a section-relative offset. If the offset
- came from the original expression, tc_fix_adjustable will have
- prevented the fix from being converted to a section-relative
- form so that we can flag the error here. */
- if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("cannot represent subtraction with an offset"));
-
val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
- S_GET_VALUE (fixP->fx_subsy));
by the linker, and it makes the object file disassembly
readable when all branch targets are encoded in relocations. */
- assert (fixP->fx_addsy);
+ gas_assert (fixP->fx_addsy);
if (S_GET_SEGMENT (fixP->fx_addsy) == seg
&& !S_FORCE_RELOC (fixP->fx_addsy, 1))
{
/* Make sure none of our internal relocations make it this far.
They'd better have been fully resolved by this point. */
- assert ((int) fixp->fx_r_type > 0);
+ gas_assert ((int) fixp->fx_r_type > 0);
reloc->addend = fixp->fx_offset;
{
xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
- assert (rt->units[stage + cycle][unit] > 0);
+ gas_assert (rt->units[stage + cycle][unit] > 0);
rt->units[stage + cycle][unit]--;
}
}
TInsn *insn = &slotstack.insn[j];
if (insn->insn_type == ITYPE_LITERAL)
{
- assert (lit_sym == NULL);
+ gas_assert (lit_sym == NULL);
lit_sym = xg_assemble_literal (insn);
}
else
{
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
if (lit_sym)
xg_resolve_literals (insn, lit_sym);
if (j != slotstack.ninsn - 1)
xg_assemble_vliw_tokens (vinsn);
xg_clear_vinsn (vinsn);
+
+ xtensa_check_frag_count ();
}
int branches = 0;
xtensa_isa isa = xtensa_default_isa;
- assert (!past_xtensa_end);
+ gas_assert (!past_xtensa_end);
for (i = 0 ; i < vinsn->num_slots; i++)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_format format;
- vliw_insn v_copy = *vinsn;
xtensa_opcode nop_opcode = xtensa_nop_opcode;
if (vinsn->num_slots == 1)
for (format = 0; format < xtensa_isa_num_formats (isa); format++)
{
- v_copy = *vinsn;
+ vliw_insn v_copy;
+ xg_copy_vinsn (&v_copy, vinsn);
if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
{
int slot;
}
if (fit == v_copy.num_slots)
{
- *vinsn = v_copy;
+ xg_copy_vinsn (vinsn, &v_copy);
xtensa_format_encode (isa, format, vinsn->insnbuf);
vinsn->format = format;
break;
int slot, chosen_slot;
vinsn->format = xg_get_single_format (tinsn->opcode);
- assert (vinsn->format != XTENSA_UNDEFINED);
+ gas_assert (vinsn->format != XTENSA_UNDEFINED);
vinsn->num_slots = xtensa_format_num_slots (isa, vinsn->format);
chosen_slot = xg_get_single_slot (tinsn->opcode);
switch (insn->insn_type)
{
case ITYPE_LITERAL:
- assert (lit_sym == NULL);
+ gas_assert (lit_sym == NULL);
lit_sym = xg_assemble_literal (insn);
break;
case ITYPE_LABEL:
char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
colon (label);
- assert (label_sym == NULL);
+ gas_assert (label_sym == NULL);
label_sym = symbol_find_or_make (label);
- assert (label_sym);
+ gas_assert (label_sym);
free (label);
}
break;
}
break;
default:
- assert (0);
+ gas_assert (0);
break;
}
}
int slot;
int total_expansion = 0;
- for (slot = 0; slot < MAX_SLOTS; slot++)
+ for (slot = 0; slot < config_max_slots; slot++)
total_expansion += fragP->tc_frag_data.text_expansion[slot];
return total_expansion;
if (frag_now_fix () != 0
&& (! frag_now->tc_frag_data.is_insn
|| (vinsn_has_specific_opcodes (vinsn) && use_transform ())
- || !use_transform () != frag_now->tc_frag_data.is_no_transform
+ || (!use_transform ()) != frag_now->tc_frag_data.is_no_transform
|| (directive_state[directive_longcalls]
!= frag_now->tc_frag_data.use_longcalls)
|| (directive_state[directive_absolute_literals]
frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
+ if (tinsn->opcode == xtensa_l32r_opcode)
+ {
+ frag_now->tc_frag_data.literal_frags[slot] =
+ tinsn->tok[1].X_add_symbol->sy_frag;
+ }
if (tinsn->literal_space != 0)
xg_assemble_literal_space (tinsn->literal_space, slot);
frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
if (tinsn->subtype == RELAX_NARROW)
- assert (vinsn->num_slots == 1);
+ gas_assert (vinsn->num_slots == 1);
if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
is_jump = TRUE;
if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
{
if (is_jump)
{
- assert (finish_frag);
+ gas_assert (finish_frag);
frag_var (rs_machine_dependent,
- UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+ xtensa_fetch_width, xtensa_fetch_width,
RELAX_UNREACHABLE,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
+ xtensa_maybe_create_trampoline_frag ();
+ /* Always create one here. */
+ xtensa_maybe_create_literal_pool_frag (TRUE, FALSE);
}
else if (is_branch && do_align_targets ())
{
- assert (finish_frag);
+ gas_assert (finish_frag);
frag_var (rs_machine_dependent,
- UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+ xtensa_fetch_width, xtensa_fetch_width,
RELAX_MAYBE_UNREACHABLE,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
xtensa_sanity_check ();
xtensa_add_config_info ();
+
+ xtensa_check_frag_count ();
+}
+
+
+struct trampoline_frag
+{
+ struct trampoline_frag *next;
+ bfd_boolean needs_jump_around;
+ fragS *fragP;
+ fixS *fixP;
+};
+
+struct trampoline_seg
+{
+ struct trampoline_seg *next;
+ asection *seg;
+ struct trampoline_frag trampoline_list;
+};
+
+static struct trampoline_seg trampoline_seg_list;
+#define J_RANGE (128 * 1024)
+
+static int unreachable_count = 0;
+
+
+static void
+xtensa_maybe_create_trampoline_frag (void)
+{
+ if (!use_trampolines)
+ return;
+
+ /* We create an area for possible trampolines every 10 unreachable frags.
+ These are preferred over the ones not preceded by an unreachable frag,
+ because we don't have to jump around them. This function is called after
+ each RELAX_UNREACHABLE frag is created. */
+
+ if (++unreachable_count > 10)
+ {
+ xtensa_create_trampoline_frag (FALSE);
+ clear_frag_count ();
+ unreachable_count = 0;
+ }
+}
+
+static void
+xtensa_check_frag_count (void)
+{
+ if (!use_trampolines || frag_now->tc_frag_data.is_no_transform)
+ return;
+
+ /* We create an area for possible trampolines every 8000 frags or so. This
+ is an estimate based on the max range of a "j" insn (+/-128K) divided
+ by a typical frag byte count (16), minus a few for safety. This function
+ is called after each source line is processed. */
+
+ if (get_frag_count () > 8000)
+ {
+ xtensa_create_trampoline_frag (TRUE);
+ clear_frag_count ();
+ unreachable_count = 0;
+ }
+
+ /* We create an area for a possible literal pool every N (default 5000)
+ frags or so. */
+ xtensa_maybe_create_literal_pool_frag (TRUE, TRUE);
+}
+
+static xtensa_insnbuf trampoline_buf = NULL;
+static xtensa_insnbuf trampoline_slotbuf = NULL;
+
+static xtensa_insnbuf litpool_buf = NULL;
+static xtensa_insnbuf litpool_slotbuf = NULL;
+
+#define TRAMPOLINE_FRAG_SIZE 3000
+
+static void
+xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
+{
+ /* Emit a frag where we can place intermediate jump instructions,
+ in case we need to jump farther than 128K bytes.
+ Each jump instruction takes three bytes.
+ We allocate enough for 1000 trampolines in each frag.
+ If that's not enough, oh well. */
+
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+ struct trampoline_frag *tf;
+ char *varP;
+ fragS *fragP;
+ int size = TRAMPOLINE_FRAG_SIZE;
+
+ for ( ; ts; ts = ts->next)
+ {
+ if (ts->seg == now_seg)
+ break;
+ }
+
+ if (ts == NULL)
+ {
+ ts = (struct trampoline_seg *)xcalloc(sizeof (struct trampoline_seg), 1);
+ ts->next = trampoline_seg_list.next;
+ trampoline_seg_list.next = ts;
+ ts->seg = now_seg;
+ }
+
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ varP = frag_var (rs_machine_dependent, size, size, RELAX_TRAMPOLINE, NULL, 0, NULL);
+ fragP = (fragS *)(varP - SIZEOF_STRUCT_FRAG);
+ if (trampoline_buf == NULL)
+ {
+ trampoline_buf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ trampoline_slotbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ }
+ tf = (struct trampoline_frag *)xmalloc(sizeof (struct trampoline_frag));
+ tf->next = ts->trampoline_list.next;
+ ts->trampoline_list.next = tf;
+ tf->needs_jump_around = needs_jump_around;
+ tf->fragP = fragP;
+ tf->fixP = NULL;
+}
+
+
+static struct trampoline_seg *
+find_trampoline_seg (asection *seg)
+{
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+
+ for ( ; ts; ts = ts->next)
+ {
+ if (ts->seg == seg)
+ return ts;
+ }
+
+ return NULL;
+}
+
+
+void dump_trampolines (void);
+
+void
+dump_trampolines (void)
+{
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+
+ for ( ; ts; ts = ts->next)
+ {
+ asection *seg = ts->seg;
+
+ if (seg == NULL)
+ continue;
+ fprintf(stderr, "SECTION %s\n", seg->name);
+ struct trampoline_frag *tf = ts->trampoline_list.next;
+ for ( ; tf; tf = tf->next)
+ {
+ if (tf->fragP == NULL)
+ continue;
+ fprintf(stderr, " 0x%08x: fix=%d, jump_around=%s\n",
+ (int)tf->fragP->fr_address, (int)tf->fragP->fr_fix,
+ tf->needs_jump_around ? "T" : "F");
+ }
+ }
}
+static void dump_litpools (void) __attribute__ ((unused));
+
+static void
+dump_litpools (void)
+{
+ struct litpool_seg *lps = litpool_seg_list.next;
+ struct litpool_frag *lpf;
+
+ for ( ; lps ; lps = lps->next )
+ {
+ printf("litpool seg %s\n", lps->seg->name);
+ for ( lpf = lps->frag_list.next; lpf->fragP; lpf = lpf->next )
+ {
+ fragS *litfrag = lpf->fragP->fr_next;
+ int count = 0;
+ while (litfrag && litfrag->fr_subtype != RELAX_LITERAL_POOL_END)
+ {
+ if (litfrag->fr_fix == 4)
+ count++;
+ litfrag = litfrag->fr_next;
+ }
+ printf(" %ld <%d:%d> (%d) [%d]: ",
+ lpf->addr, lpf->priority, lpf->original_priority,
+ lpf->fragP->fr_line, count);
+ //dump_frag(lpf->fragP);
+ }
+ }
+}
+
+static void
+xtensa_maybe_create_literal_pool_frag (bfd_boolean create,
+ bfd_boolean only_if_needed)
+{
+ struct litpool_seg *lps = litpool_seg_list.next;
+ fragS *fragP;
+ struct litpool_frag *lpf;
+ bfd_boolean needed = FALSE;
+
+ if (use_literal_section || !auto_litpools)
+ return;
+
+ for ( ; lps ; lps = lps->next )
+ {
+ if (lps->seg == now_seg)
+ break;
+ }
+
+ if (lps == NULL)
+ {
+ lps = (struct litpool_seg *)xcalloc (sizeof (struct litpool_seg), 1);
+ lps->next = litpool_seg_list.next;
+ litpool_seg_list.next = lps;
+ lps->seg = now_seg;
+ lps->frag_list.next = &lps->frag_list;
+ lps->frag_list.prev = &lps->frag_list;
+ }
+
+ lps->frag_count++;
+
+ if (create)
+ {
+ if (only_if_needed)
+ {
+ if (past_xtensa_end || !use_transform() ||
+ frag_now->tc_frag_data.is_no_transform)
+ {
+ return;
+ }
+ if (auto_litpool_limit <= 0)
+ {
+ /* Don't create a litpool based only on frag count. */
+ return;
+ }
+ else if (lps->frag_count > auto_litpool_limit)
+ {
+ needed = TRUE;
+ }
+ else
+ {
+ return;
+ }
+ }
+ else
+ {
+ needed = TRUE;
+ }
+ }
+
+ if (needed)
+ {
+ int size = (only_if_needed) ? 3 : 0; /* Space for a "j" insn. */
+ /* Create a potential site for a literal pool. */
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ fragP = frag_now;
+ fragP->tc_frag_data.lit_frchain = frchain_now;
+ fragP->tc_frag_data.literal_frag = fragP;
+ frag_var (rs_machine_dependent, size, size,
+ (only_if_needed) ?
+ RELAX_LITERAL_POOL_CANDIDATE_BEGIN :
+ RELAX_LITERAL_POOL_BEGIN,
+ NULL, 0, NULL);
+ frag_now->tc_frag_data.lit_seg = now_seg;
+ frag_variant (rs_machine_dependent, 0, 0,
+ RELAX_LITERAL_POOL_END, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+ else
+ {
+ /* RELAX_LITERAL_POOL_BEGIN frag is being created;
+ just record it here. */
+ fragP = frag_now;
+ }
+
+ lpf = (struct litpool_frag *)xmalloc(sizeof (struct litpool_frag));
+ /* Insert at tail of circular list. */
+ lpf->addr = 0;
+ lps->frag_list.prev->next = lpf;
+ lpf->next = &lps->frag_list;
+ lpf->prev = lps->frag_list.prev;
+ lps->frag_list.prev = lpf;
+ lpf->fragP = fragP;
+ lpf->priority = (needed) ? (only_if_needed) ? 3 : 2 : 1;
+ lpf->original_priority = lpf->priority;
+
+ lps->frag_count = 0;
+}
static void
xtensa_cleanup_align_frags (void)
static bfd_boolean
is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
{
- const expressionS *expr = &tinsn->tok[1];
- symbolS *symbolP = expr->X_add_symbol;
- offsetT max_distance = expr->X_add_number;
+ const expressionS *exp = &tinsn->tok[1];
+ symbolS *symbolP = exp->X_add_symbol;
+ offsetT max_distance = exp->X_add_number;
fragS *target_frag;
- if (expr->X_op != O_symbol)
+ if (exp->X_op != O_symbol)
return FALSE;
target_frag = symbol_get_frag (symbolP);
|| fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
{
/* Find the loop frag. */
- fragS *targ_frag = next_non_empty_frag (fragP);
+ fragS *loop_frag = next_non_empty_frag (fragP);
/* Find the first insn frag. */
- targ_frag = next_non_empty_frag (targ_frag);
+ fragS *targ_frag = next_non_empty_frag (loop_frag);
+
+ /* Handle a corner case that comes up in hardware
+ diagnostics. The original assembly looks like this:
+
+ loop aX, LabelA
+ <empty_frag>--not found by next_non_empty_frag
+ loop aY, LabelB
+
+ Depending on the start address, the assembler may or
+ may not change it to look something like this:
+
+ loop aX, LabelA
+ nop--frag isn't empty anymore
+ loop aY, LabelB
+
+ So set up to check the alignment of the nop if it
+ exists */
+ while (loop_frag != targ_frag)
+ {
+ if (loop_frag->fr_type == rs_machine_dependent
+ && (loop_frag->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
+ || loop_frag->fr_subtype
+ == RELAX_CHECK_ALIGN_NEXT_OPCODE))
+ targ_frag = loop_frag;
+ else
+ loop_frag = loop_frag->fr_next;
+ }
/* Of course, sometimes (mostly for toy test cases) a
zero-cost loop instruction is the last in a section. */
sleb128 value, the linker is unable to adjust that value to account for
link-time relaxation. Mark all the code between such symbols so that
its size cannot be changed by linker relaxation. */
-
+
static void
xtensa_mark_difference_of_two_symbols (void)
{
symbolS *expr_sym;
- for (expr_sym = expr_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);
+ expressionS *exp = symbol_get_value_expression (expr_sym);
- if (expr->X_op == O_subtract)
+ if (exp->X_op == O_subtract)
{
- symbolS *left = expr->X_add_symbol;
- symbolS *right = expr->X_op_symbol;
-
+ symbolS *left = exp->X_add_symbol;
+ symbolS *right = exp->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;
+ fragS *walk;
- if (symbol_get_frag (left)->fr_address
+ if (symbol_get_frag (left)->fr_address
<= symbol_get_frag (right)->fr_address)
{
start = symbol_get_frag (left);
start = symbol_get_frag (right);
end = symbol_get_frag (left);
}
- do
+
+ if (start->tc_frag_data.no_transform_end != NULL)
+ walk = start->tc_frag_data.no_transform_end;
+ else
+ walk = start;
+ do
{
- start->tc_frag_data.is_no_transform = 1;
- start = start->fr_next;
+ walk->tc_frag_data.is_no_transform = 1;
+ walk = walk->fr_next;
}
- while (start && start->fr_address < end->fr_address);
+ while (walk && walk->fr_address < end->fr_address);
+
+ start->tc_frag_data.no_transform_end = walk;
}
}
}
}
frag_wane (fragP);
}
- assert (fragP->fr_type != rs_machine_dependent
+ gas_assert (fragP->fr_type != rs_machine_dependent
|| fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
}
}
break;
default:
/* We had darn well better know how big it is. */
- assert (0);
+ gas_assert (0);
break;
}
for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
- fragS *current_target = NULL;
xtensa_opcode current_opcode = XTENSA_UNDEFINED;
/* Walk over all of the fragments in a subsection. */
TInsn t_insn;
fragS *loop_frag = next_non_empty_frag (fragP);
tinsn_from_chars (&t_insn, loop_frag->fr_opcode, 0);
- current_target = symbol_get_frag (fragP->fr_symbol);
current_opcode = t_insn.opcode;
- assert (xtensa_opcode_is_loop (xtensa_default_isa,
+ gas_assert (xtensa_opcode_is_loop (xtensa_default_isa,
current_opcode) == 1);
}
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->fr_subtype == RELAX_SLOTS
&& fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
{
static xtensa_insnbuf insnbuf = NULL;
static bfd_boolean
is_empty_loop (const TInsn *insn, fragS *fragP)
{
- const expressionS *expr;
+ const expressionS *exp;
symbolS *symbolP;
fragS *next_fragP;
if (insn->ntok <= LOOP_IMMED_OPN)
return FALSE;
- expr = &insn->tok[LOOP_IMMED_OPN];
+ exp = &insn->tok[LOOP_IMMED_OPN];
- if (expr->X_op != O_symbol)
+ if (exp->X_op != O_symbol)
return FALSE;
- symbolP = expr->X_add_symbol;
+ symbolP = exp->X_add_symbol;
if (!symbolP)
return FALSE;
static bfd_boolean
is_local_forward_loop (const TInsn *insn, fragS *fragP)
{
- const expressionS *expr;
+ const expressionS *exp;
symbolS *symbolP;
fragS *next_fragP;
if (insn->ntok <= LOOP_IMMED_OPN)
return FALSE;
- expr = &insn->tok[LOOP_IMMED_OPN];
+ exp = &insn->tok[LOOP_IMMED_OPN];
- if (expr->X_op != O_symbol)
+ if (exp->X_op != O_symbol)
return FALSE;
- symbolP = expr->X_add_symbol;
+ symbolP = exp->X_add_symbol;
if (!symbolP)
return FALSE;
{
if (target_size <= 4)
return 2;
- assert (target_size == 8);
- return 3;
+
+ if (target_size <= 8)
+ return 3;
+
+ if (target_size <= 16)
+ return 4;
+
+ if (target_size <= 32)
+ return 5;
+
+ if (target_size <= 64)
+ return 6;
+
+ if (target_size <= 128)
+ return 7;
+
+ if (target_size <= 256)
+ return 8;
+
+ if (target_size <= 512)
+ return 9;
+
+ if (target_size <= 1024)
+ return 10;
+
+ gas_assert (0);
+ return 0;
}
bfd_boolean skip_one = FALSE;
alignment = (1 << align_pow);
- assert (target_size > 0 && alignment >= (addressT) target_size);
+ gas_assert (target_size > 0 && alignment >= (addressT) target_size);
if (!use_nops)
{
== (address + fill + target_size - 1) >> align_pow)
return fill;
}
- assert (0);
+ gas_assert (0);
return 0;
}
static int
branch_align_power (segT sec)
{
- /* If the Xtensa processor has a fetch width of 8 bytes, and the section
- is aligned to at least an 8-byte boundary, then a branch target need
- only fit within an 8-byte aligned block of memory to avoid a stall.
- Otherwise, try to fit branch targets within 4-byte aligned blocks
- (which may be insufficient, e.g., if the section has no alignment, but
- it's good enough). */
- if (xtensa_fetch_width == 8)
- {
- if (get_recorded_alignment (sec) >= 3)
- return 3;
- }
- else
- assert (xtensa_fetch_width == 4);
+ /* If the Xtensa processor has a fetch width of X, and
+ the section is aligned to at least that boundary, then a branch
+ target need only fit within that aligned block of memory to avoid
+ a stall. Otherwise, try to fit branch targets within 4-byte
+ aligned blocks (which may be insufficient, e.g., if the section
+ has no alignment, but it's good enough). */
+ int fetch_align = get_text_align_power(xtensa_fetch_width);
+ int sec_align = get_recorded_alignment (sec);
+
+ if (sec_align >= fetch_align)
+ return fetch_align;
return 2;
}
if (use_no_density)
{
- assert (fill_size % 3 == 0);
+ gas_assert (fill_size % 3 == 0);
return (fill_size / 3);
}
- assert (fill_size != 1); /* Bad argument. */
+ gas_assert (fill_size != 1); /* Bad argument. */
while (fill_size > 1)
{
fill_size -= insn_size;
count++;
}
- assert (fill_size != 1); /* Bad algorithm. */
+ gas_assert (fill_size != 1); /* Bad algorithm. */
return count;
}
if (use_no_density)
return 3;
- assert (fill_size != 1); /* Bad argument. */
+ gas_assert (fill_size != 1); /* Bad argument. */
while (fill_size > 1)
{
if (n + 1 == count)
return insn_size;
}
- assert (0);
+ gas_assert (0);
return 0;
}
xtensa_opcode opcode;
bfd_boolean is_loop;
- assert (fragP->fr_type == rs_machine_dependent);
- assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
+ gas_assert (fragP->fr_type == rs_machine_dependent);
+ gas_assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
/* Find the loop frag. */
first_insn = next_non_empty_frag (fragP);
first_insn = next_non_empty_frag (first_insn);
is_loop = next_frag_opcode_is_loop (fragP, &opcode);
- assert (is_loop);
+ gas_assert (is_loop);
loop_insn_size = xg_get_single_size (opcode);
pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
offsetT branch_align;
fragS *loop_frag;
- assert (fragP->fr_type == rs_machine_dependent);
+ gas_assert (fragP->fr_type == rs_machine_dependent);
switch (fragP->fr_subtype)
{
case RELAX_DESIRE_ALIGN:
*max_diff = (opt_diff + branch_align
- (target_size + ((address + opt_diff) % branch_align)));
- assert (*max_diff >= opt_diff);
+ gas_assert (*max_diff >= opt_diff);
return opt_diff;
case RELAX_ALIGN_NEXT_OPCODE:
target_size = get_loop_align_size (next_frag_format_size (loop_frag));
loop_insn_offset = 0;
is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
- assert (is_loop);
+ gas_assert (is_loop);
/* If the loop has been expanded then the LOOP instruction
could be at an offset from this fragment. */
*max_diff = xtensa_fetch_width
- ((target_address + opt_diff) % xtensa_fetch_width)
- target_size + opt_diff;
- assert (*max_diff >= opt_diff);
+ gas_assert (*max_diff >= opt_diff);
return opt_diff;
default:
break;
}
- assert (0);
+ gas_assert (0);
return 0;
}
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;
+};
-/* Return the number of bytes added to this fragment, given that the
- input has been stretched already by "stretch". */
+typedef struct fixup_cache fixup_cacheS;
+struct fixup_cache
+{
+ 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)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int addr = fixP->fx_frag->fr_address;
+ int target;
+ int 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);
+ delta = target - addr;
+
+ if (abs(delta) < J_RANGE / 2)
+ 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);
+ 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 = xrealloc (cache->fixups,
+ sizeof (*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;
+}
+
+/* Return the number of bytes added to this fragment, given that the
+ input has been stretched already by "stretch". */
long
xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
case RELAX_LITERAL_NR:
lit_size = 4;
fragP->fr_subtype = RELAX_LITERAL_FINAL;
- assert (unreported == lit_size);
+ gas_assert (unreported == lit_size);
memset (&fragP->fr_literal[fragP->fr_fix], 0, 4);
fragP->fr_var -= lit_size;
fragP->fr_fix += lit_size;
break;
case RELAX_LITERAL_POOL_BEGIN:
+ if (fragP->fr_var != 0)
+ {
+ /* We have a converted "candidate" literal pool;
+ assemble a jump around it. */
+ TInsn insn;
+ if (!litpool_slotbuf)
+ {
+ litpool_buf = xtensa_insnbuf_alloc (isa);
+ litpool_slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+ new_stretch += 3;
+ fragP->tc_frag_data.relax_seen = FALSE; /* Need another pass. */
+ fragP->tc_frag_data.is_insn = TRUE;
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], fragP->fr_symbol,
+ fragP->fr_fix);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, litpool_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, litpool_buf, litpool_slotbuf);
+ xtensa_insnbuf_to_chars (isa, litpool_buf,
+ (unsigned char *)fragP->fr_literal +
+ fragP->fr_fix, 3);
+ fragP->fr_fix += 3;
+ fragP->fr_var -= 3;
+ /* Add a fix-up. */
+ fix_new (fragP, 0, 3, fragP->fr_symbol, 0, TRUE,
+ BFD_RELOC_XTENSA_SLOT0_OP);
+ }
+ break;
+
case RELAX_LITERAL_POOL_END:
+ case RELAX_LITERAL_POOL_CANDIDATE_BEGIN:
case RELAX_MAYBE_UNREACHABLE:
case RELAX_MAYBE_DESIRE_ALIGN:
/* No relaxation required. */
new_stretch += relax_frag_for_align (fragP, stretch);
break;
+ case RELAX_TRAMPOLINE:
+ if (fragP->tc_frag_data.relax_seen)
+ {
+ 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;
+
+ slot = fixP->tc_fix_data.slot;
+
+ 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_frag *tf = ts->trampoline_list.next;
+ struct trampoline_frag *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->next )
+ {
+ int this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ int next_addr = (tf->next) ? tf->next->fragP->fr_address + tf->next->fragP->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
+ {
+ struct trampoline_frag *best_tf = NULL;
+ int best_delta = 0;
+
+ for ( ; tf; prev = tf, tf = tf->next )
+ {
+ int this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ int this_delta = abs (this_addr - midpoint);
+
+ if (!best_tf || this_delta < best_delta)
+ {
+ best_tf = tf;
+ best_delta = this_delta;
+ }
+ }
+ tf = best_tf;
+ }
+ if (tf->fragP == fragP)
+ {
+ if (abs (addr - trampaddr) < J_RANGE)
+ { /* The trampoline is in range of original; fix it! */
+ fixS *newfixP;
+ int offset;
+ TInsn insn;
+ symbolS *lsym;
+ fragS *fP; /* The out-of-range jump. */
+
+ new_stretch += init_trampoline_frag (tf);
+ offset = fragP->fr_fix; /* Where to assemble the j insn. */
+ lsym = fragP->fr_symbol;
+ fP = fixP->fx_frag;
+ /* Assemble a jump to the target label here. */
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], lsym, offset);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
+ xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)fragP->fr_literal + offset, 3);
+ fragP->fr_fix += 3;
+ fragP->fr_var -= 3;
+ /* Add a fix-up for the original j insn. */
+ newfixP = fix_new (fP, fixP->fx_where, fixP->fx_size, lsym, fragP->fr_fix - 3, TRUE, fixP->fx_r_type);
+ newfixP->fx_no_overflow = 1;
+ newfixP->tc_fix_data.X_add_symbol = lsym;
+ newfixP->tc_fix_data.X_add_number = offset;
+ newfixP->tc_fix_data.slot = slot;
+
+ xtensa_delete_cached_fixup (&fixup_cache, i);
+ xtensa_add_cached_fixup (&fixup_cache, newfixP);
+
+ /* Move the fix-up from the original j insn to this one. */
+ fixP->fx_frag = fragP;
+ fixP->fx_where = fragP->fr_fix - 3;
+ fixP->tc_fix_data.slot = 0;
+
+ xtensa_add_cached_fixup (&fixup_cache, fixP);
+
+ /* re-do current fixup */
+ --i;
+
+ /* Adjust the jump around this trampoline (if present). */
+ if (tf->fixP != NULL)
+ {
+ tf->fixP->fx_offset += 3;
+ }
+ new_stretch += 3;
+ 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->next = tf->next;
+ if (fragP == fixup_cache.first_frag)
+ fixup_cache.first_frag = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
default:
as_bad (_("bad relaxation state"));
}
long stretch_me;
long diff;
- assert (fragP->fr_subtype == RELAX_FILL_NOP
+ gas_assert (fragP->fr_subtype == RELAX_FILL_NOP
|| fragP->fr_subtype == RELAX_UNREACHABLE
|| (fragP->fr_subtype == RELAX_SLOTS
&& fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
(*widens)++;
break;
}
- address += total_frag_text_expansion (fragP);;
+ address += total_frag_text_expansion (fragP);
break;
case RELAX_IMMED:
{
local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
opt_diff = local_opt_diff;
- assert (opt_diff >= 0);
- assert (max_diff >= opt_diff);
+ gas_assert (opt_diff >= 0);
+ gas_assert (max_diff >= opt_diff);
if (max_diff == 0)
return 0;
{
if (this_frag->fr_subtype == RELAX_UNREACHABLE)
{
- assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
+ gas_assert (opt_diff <= (signed) xtensa_fetch_width);
return opt_diff;
}
return 0;
if (this_frag->fr_subtype == RELAX_SLOTS
&& this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
- assert (stretch_amount <= 1);
+ gas_assert (stretch_amount <= 1);
else if (this_frag->fr_subtype == RELAX_FILL_NOP)
{
if (this_frag->tc_frag_data.is_no_density)
- assert (stretch_amount == 3 || stretch_amount == 0);
+ gas_assert (stretch_amount == 3 || stretch_amount == 0);
else
- assert (stretch_amount <= 3);
+ gas_assert (stretch_amount <= 3);
}
}
return stretch_amount;
/* The idea: widen everything you can to get a target or loop aligned,
then start using NOPs.
- When we must have a NOP, here is a table of how we decide
- (so you don't have to fight through the control flow below):
-
wide_nops = the number of wide NOPs available for aligning
narrow_nops = the number of narrow NOPs available for aligning
(a subset of wide_nops)
widens = the number of narrow instructions that should be widened
- Desired wide narrow
- Diff nop nop widens
- 1 0 0 1
- 2 0 1 0
- 3a 1 0 0
- b 0 1 1 (case 3a makes this case unnecessary)
- 4a 1 0 1
- b 0 2 0
- c 0 1 2 (case 4a makes this case unnecessary)
- 5a 1 0 2
- b 1 1 0
- c 0 2 1 (case 5b makes this case unnecessary)
- 6a 2 0 0
- b 1 0 3
- c 0 1 4 (case 6b makes this case unnecessary)
- d 1 1 1 (case 6a makes this case unnecessary)
- e 0 2 2 (case 6a makes this case unnecessary)
- f 0 3 0 (case 6a makes this case unnecessary)
- 7a 1 0 4
- b 2 0 1
- c 1 1 2 (case 7b makes this case unnecessary)
- d 0 1 5 (case 7a makes this case unnecessary)
- e 0 2 3 (case 7b makes this case unnecessary)
- f 0 3 1 (case 7b makes this case unnecessary)
- g 1 2 1 (case 7b makes this case unnecessary)
*/
static long
int num_widens,
int desired_diff)
{
+ int nops_needed;
+ int nop_bytes;
+ int extra_bytes;
int bytes_short = desired_diff - num_widens;
- assert (desired_diff >= 0 && desired_diff < 8);
+ gas_assert (desired_diff >= 0
+ && desired_diff < (signed) xtensa_fetch_width);
if (desired_diff == 0)
return 0;
- assert (wide_nops > 0 || num_widens > 0);
+ gas_assert (wide_nops > 0 || num_widens > 0);
/* Always prefer widening to NOP-filling. */
if (bytes_short < 0)
/* From here we will need at least one NOP to get an alignment.
However, we may not be able to align at all, in which case,
don't widen. */
- if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ nops_needed = desired_diff / 3;
+
+ /* If there aren't enough nops, don't widen. */
+ if (nops_needed > wide_nops)
+ return 0;
+
+ /* First try it with all wide nops. */
+ nop_bytes = nops_needed * 3;
+ extra_bytes = desired_diff - nop_bytes;
+
+ if (nop_bytes + num_widens >= desired_diff)
{
- switch (desired_diff)
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return 3;
+ else if (num_widens == extra_bytes)
+ return 1;
+ return 0;
+ }
+
+ /* Add a narrow nop. */
+ nops_needed++;
+ nop_bytes += 2;
+ extra_bytes -= 2;
+ if (narrow_nops == 0 || nops_needed > wide_nops)
+ return 0;
+
+ if (nop_bytes + num_widens >= desired_diff && extra_bytes >= 0)
+ {
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return !this_frag->tc_frag_data.is_no_density ? 2 : 3;
+ else if (num_widens == extra_bytes)
+ return 1;
+ return 0;
+ }
+
+ /* Replace a wide nop with a narrow nop--we can get here if
+ extra_bytes was negative in the previous conditional. */
+ if (narrow_nops == 1)
+ return 0;
+ nop_bytes--;
+ extra_bytes++;
+ if (nop_bytes + num_widens >= desired_diff)
+ {
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return !this_frag->tc_frag_data.is_no_density ? 2 : 3;
+ else if (num_widens == extra_bytes)
+ return 1;
+ return 0;
+ }
+
+ /* If we can't satisfy any of the above cases, then we can't align
+ using padding or fill nops. */
+ return 0;
+}
+
+
+static struct trampoline_frag *
+search_trampolines (TInsn *tinsn, fragS *fragP, bfd_boolean unreachable_only)
+{
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ struct trampoline_frag *tf = (ts) ? ts->trampoline_list.next : NULL;
+ struct trampoline_frag *best_tf = NULL;
+ int best_delta = 0;
+ int 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 midpoint = lower + delta / 2;
+ int this_delta = -1;
+ int this_addr = -1;
+
+ 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->next)
{
- case 1:
- return 0;
- case 2:
- if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
- return 2; /* case 2 */
- return 0;
- case 3:
- if (wide_nops > 1)
- return 0;
- else
- return 3; /* case 3a */
- case 4:
- if (num_widens >= 1 && wide_nops == 1)
- return 3; /* case 4a */
- if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
- return 2; /* case 4b */
- return 0;
- case 5:
- if (num_widens >= 2 && wide_nops == 1)
- return 3; /* case 5a */
- /* We will need two nops. Are there enough nops
- between here and the align target? */
- if (wide_nops < 2 || narrow_nops == 0)
- return 0;
- /* Are there other nops closer that can serve instead? */
- if (wide_nops > 2 && narrow_nops > 1)
- return 0;
- /* Take the density one first, because there might not be
- another density one available. */
- if (!this_frag->tc_frag_data.is_no_density)
- return 2; /* case 5b narrow */
+ int next_addr = (tf->next) ? tf->next->fragP->fr_address + tf->next->fragP->fr_fix : 0;
+
+ this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ if (lower == addr)
+ {
+ /* Forward jump. */
+ if (this_addr - addr < J_RANGE)
+ break;
+ }
else
- return 3; /* case 5b wide */
- return 0;
- case 6:
- if (wide_nops == 2)
- return 3; /* case 6a */
- else if (num_widens >= 3 && wide_nops == 1)
- return 3; /* case 6b */
- return 0;
- case 7:
- if (wide_nops == 1 && num_widens >= 4)
- return 3; /* case 7a */
- else if (wide_nops == 2 && num_widens >= 1)
- return 3; /* case 7b */
- return 0;
- default:
- assert (0);
+ {
+ /* Backward jump. */
+ if (next_addr == 0 || addr - next_addr > J_RANGE)
+ break;
+ }
}
+ if (abs (addr - this_addr) < J_RANGE)
+ return tf;
+
+ return NULL;
}
- else
+ for ( ; tf; tf = tf->next)
{
- /* We will need a NOP no matter what, but should we widen
- this instruction to help?
+ this_addr = tf->fragP->fr_address + tf->fragP->fr_fix;
+ this_delta = abs (this_addr - midpoint);
+ if (unreachable_only && tf->needs_jump_around)
+ continue;
+ if (!best_tf || this_delta < best_delta)
+ {
+ best_tf = tf;
+ best_delta = this_delta;
+ best_addr = this_addr;
+ }
+ }
+
+ if (best_tf &&
+ best_delta < J_RANGE &&
+ abs(best_addr - lower) < J_RANGE &&
+ abs(best_addr - upper) < J_RANGE)
+ return best_tf;
+
+ return NULL; /* No suitable trampoline found. */
+}
+
+
+static struct trampoline_frag *
+get_best_trampoline (TInsn *tinsn, fragS *fragP)
+{
+ struct trampoline_frag *tf = NULL;
+
+ tf = search_trampolines (tinsn, fragP, TRUE); /* Try unreachable first. */
+
+ if (tf == NULL)
+ tf = search_trampolines (tinsn, fragP, FALSE); /* Try ones needing a jump-around, too. */
- This is a RELAX_NARROW frag. */
- switch (desired_diff)
+ return tf;
+}
+
+
+static void
+check_and_update_trampolines (void)
+{
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ struct trampoline_frag *tf = ts->trampoline_list.next;
+ struct trampoline_frag *prev = &ts->trampoline_list;
+
+ for ( ; tf; prev = tf, tf = tf->next)
+ {
+ if (tf->fragP->fr_var < 3)
{
- case 1:
- assert (0);
- return 0;
- case 2:
- case 3:
- return 0;
- case 4:
- if (wide_nops >= 1 && num_widens == 1)
- return 1; /* case 4a */
- return 0;
- case 5:
- if (wide_nops >= 1 && num_widens == 2)
- return 1; /* case 5a */
- return 0;
- case 6:
- if (wide_nops >= 2)
- return 0; /* case 6a */
- else if (wide_nops >= 1 && num_widens == 3)
- return 1; /* case 6b */
- return 0;
- case 7:
- if (wide_nops >= 1 && num_widens == 4)
- return 1; /* case 7a */
- else if (wide_nops >= 2 && num_widens == 1)
- return 1; /* case 7b */
- return 0;
- default:
- assert (0);
- return 0;
+ frag_wane (tf->fragP);
+ prev->next = tf->next;
+ tf->fragP = NULL;
}
}
- assert (0);
- return 0;
+}
+
+
+static int
+init_trampoline_frag (struct trampoline_frag *trampP)
+{
+ fragS *fp = trampP->fragP;
+ int growth = 0;
+
+ if (fp->fr_fix == 0)
+ {
+ symbolS *lsym;
+ char label[10 + 2 * sizeof(fp)];
+ sprintf (label, ".L0_TR_%p", fp);
+
+ lsym = (symbolS *)local_symbol_make (label, now_seg, 0, fp);
+ fp->fr_symbol = lsym;
+ if (trampP->needs_jump_around)
+ {
+ /* Add a jump around this block of jumps, in case
+ control flows into this block. */
+ fixS *fixP;
+ TInsn insn;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+
+ fp->tc_frag_data.is_insn = 1;
+ /* Assemble a jump insn. */
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], lsym, 3);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
+ xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)fp->fr_literal, 3);
+ fp->fr_fix += 3;
+ fp->fr_var -= 3;
+ growth = 3;
+ fixP = fix_new (fp, 0, 3, lsym, 3, TRUE, BFD_RELOC_XTENSA_SLOT0_OP);
+ trampP->fixP = fixP;
+ }
+ }
+ return growth;
+}
+
+
+static int
+add_jump_to_trampoline (struct trampoline_frag *trampP, fragS *origfrag)
+{
+ fragS *tramp = trampP->fragP;
+ fixS *fixP;
+ int offset = tramp->fr_fix; /* Where to assemble the j insn. */
+ TInsn insn;
+ symbolS *lsym;
+ symbolS *tsym;
+ int toffset;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+ int growth = 0;
+
+ lsym = tramp->fr_symbol;
+ /* Assemble a jump to the target label in the trampoline frag. */
+ tsym = origfrag->tc_frag_data.slot_symbols[0];
+ toffset = origfrag-> tc_frag_data.slot_offsets[0];
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], tsym, toffset);
+ fmt = xg_get_single_format (xtensa_j_opcode);
+ tinsn_to_slotbuf (fmt, 0, &insn, trampoline_slotbuf);
+ xtensa_format_set_slot (isa, fmt, 0, trampoline_buf, trampoline_slotbuf);
+ xtensa_insnbuf_to_chars (isa, trampoline_buf, (unsigned char *)tramp->fr_literal + offset, 3);
+ tramp->fr_fix += 3;
+ tramp->fr_var -= 3;
+ growth = 3;
+ /* add a fix-up for the trampoline jump. */
+ fixP = fix_new (tramp, tramp->fr_fix - 3, 3, tsym, toffset, TRUE, BFD_RELOC_XTENSA_SLOT0_OP);
+ /* Modify the jump at the start of this trampoline to point past the newly-added jump. */
+ fixP = trampP->fixP;
+ if (fixP)
+ fixP->fx_offset += 3;
+ /* Modify the original j to point here. */
+ origfrag->tc_frag_data.slot_symbols[0] = lsym;
+ origfrag->tc_frag_data.slot_offsets[0] = tramp->fr_fix - 3;
+ /* If trampoline is full, remove it from the list. */
+ check_and_update_trampolines ();
+
+ return growth;
}
int num_text_bytes, num_literal_bytes;
int literal_diff, total_text_diff, this_text_diff;
- assert (fragP->fr_opcode != NULL);
+ gas_assert (fragP->fr_opcode != NULL);
xg_clear_vinsn (&cur_vinsn);
vinsn_from_chars (&cur_vinsn, fragP->fr_opcode);
istack_init (&istack);
num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
min_steps, stretch);
- assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
+ gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
/* The first instruction in the relaxed sequence will go after
the current wide instruction, and thus its symbolic immediates
might not fit. */
-
+
istack_init (&istack);
- num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP,
+ num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP,
frag_offset + old_size,
min_steps, stretch + old_size);
- assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
+ gas_assert (num_steps >= min_steps && num_steps <= RELAX_IMMED_MAXSTEPS);
- fragP->tc_frag_data.slot_subtypes[slot]
+ fragP->tc_frag_data.slot_subtypes[slot]
= (int) RELAX_IMMED + num_steps;
num_literal_bytes = get_num_stack_literal_bytes (&istack);
- literal_diff
+ literal_diff
= num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
-
+
num_text_bytes = get_num_stack_text_bytes (&istack) + old_size;
}
}
this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
/* It MUST get larger. If not, we could get an infinite loop. */
- assert (num_text_bytes >= 0);
- assert (literal_diff >= 0);
- assert (total_text_diff >= 0);
+ gas_assert (num_text_bytes >= 0);
+ gas_assert (literal_diff >= 0);
+ gas_assert (total_text_diff >= 0);
fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
- assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
- assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
+ gas_assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
+ gas_assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
/* Find the associated expandable literal for this. */
if (literal_diff != 0)
fragS *lit_fragP = fragP->tc_frag_data.literal_frags[slot];
if (lit_fragP)
{
- assert (literal_diff == 4);
+ gas_assert (literal_diff == 4);
lit_fragP->tc_frag_data.unreported_expansion += literal_diff;
/* We expect that the literal section state has NOT been
modified yet. */
- assert (lit_fragP->fr_type == rs_machine_dependent
+ gas_assert (lit_fragP->fr_type == rs_machine_dependent
&& lit_fragP->fr_subtype == RELAX_LITERAL);
lit_fragP->fr_subtype = RELAX_LITERAL_NR;
if (negatable_branch && istack.ninsn > 1)
update_next_frag_state (fragP);
+ /* If last insn is a jump, and it cannot reach its target, try to find a trampoline. */
+ if (istack.ninsn > 2 &&
+ istack.insn[istack.ninsn - 1].insn_type == ITYPE_LABEL &&
+ istack.insn[istack.ninsn - 2].insn_type == ITYPE_INSN &&
+ istack.insn[istack.ninsn - 2].opcode == xtensa_j_opcode)
+ {
+ TInsn *jinsn = &istack.insn[istack.ninsn - 2];
+
+ if (!xg_symbolic_immeds_fit (jinsn, segP, fragP, fragP->fr_offset, total_text_diff))
+ {
+ struct trampoline_frag *tf = get_best_trampoline (jinsn, fragP);
+
+ if (tf)
+ {
+ this_text_diff += init_trampoline_frag (tf);
+ this_text_diff += add_jump_to_trampoline (tf, fragP);
+ }
+ else
+ {
+ /* If target symbol is undefined, assume it will reach once linked. */
+ expressionS *exp = &istack.insn[istack.ninsn - 2].tok[0];
+
+ if (exp->X_op == O_symbol && S_IS_DEFINED (exp->X_add_symbol))
+ {
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("jump target out of range; no usable trampoline found"));
+ }
+ }
+ }
+ }
+
return this_text_diff;
}
fragS *f;
fragp->fr_subtype = RELAX_LITERAL_FINAL;
- assert (fragp->tc_frag_data.unreported_expansion == 4);
+ gas_assert (fragp->tc_frag_data.unreported_expansion == 4);
memset (&fragp->fr_literal[fragp->fr_fix], 0, 4);
fragp->fr_var -= 4;
fragp->fr_fix += 4;
else
as_bad (_("invalid relaxation fragment result"));
break;
+
+ case RELAX_TRAMPOLINE:
+ break;
}
fragp->fr_var = 0;
int size, old_size, diff;
offsetT frag_offset;
- assert (slot == 0);
+ gas_assert (slot == 0);
tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
if (fragP->tc_frag_data.is_aligning_branch == 1)
{
- assert (fragP->tc_frag_data.text_expansion[0] == 1
+ gas_assert (fragP->tc_frag_data.text_expansion[0] == 1
|| fragP->tc_frag_data.text_expansion[0] == 0);
convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
fmt, slot);
return;
}
- assert (fragP->fr_opcode != NULL);
+ gas_assert (fragP->fr_opcode != NULL);
/* Frags in this relaxation state should only contain
single instruction bundles. */
frag_offset, TRUE);
diff = size - old_size;
- assert (diff >= 0);
- assert (diff <= fragP->fr_var);
+ gas_assert (diff >= 0);
+ gas_assert (diff <= fragP->fr_var);
fragP->fr_var -= diff;
fragP->fr_fix += diff;
{
char *loc = &fragP->fr_literal[fragP->fr_fix];
int size = fragP->tc_frag_data.text_expansion[0];
- assert ((unsigned) size == (fragP->fr_next->fr_address
+ gas_assert ((unsigned) size == (fragP->fr_next->fr_address
- fragP->fr_address - fragP->fr_fix));
if (size == 0)
{
int bytes;
bfd_boolean is_loop;
- assert (fr_opcode != NULL);
+ gas_assert (fr_opcode != NULL);
xg_clear_vinsn (&cur_vinsn);
else
{
bytes += fragP->tc_frag_data.text_expansion[0];
- assert (bytes == 2 || bytes == 3);
+ gas_assert (bytes == 2 || bytes == 3);
build_nop (&cur_vinsn.slots[0], bytes);
fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
}
symbolS *gen_label = NULL;
offsetT frag_offset;
bfd_boolean first = TRUE;
- bfd_boolean last_is_jump;
/* It does not fit. Find something that does and
convert immediately. */
unreach = unreach->fr_next;
}
- assert (unreach->fr_type == rs_machine_dependent
+ gas_assert (unreach->fr_type == rs_machine_dependent
&& (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
|| unreach->fr_subtype == RELAX_UNREACHABLE));
target_offset += unreach->tc_frag_data.text_expansion[0];
}
- assert (gen_label == NULL);
+ gas_assert (gen_label == NULL);
gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
fr_opcode - fragP->fr_literal
+ target_offset, fragP);
total_size = 0;
first = TRUE;
- last_is_jump = FALSE;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *tinsn = &istack.insn[i];
case ITYPE_LITERAL:
lit_frag = fragP->tc_frag_data.literal_frags[slot];
/* Already checked. */
- assert (lit_frag != NULL);
- assert (lit_sym != NULL);
- assert (tinsn->ntok == 1);
+ gas_assert (lit_frag != NULL);
+ gas_assert (lit_sym != NULL);
+ gas_assert (tinsn->ntok == 1);
/* Add a fixup. */
target_seg = S_GET_SEGMENT (lit_sym);
- assert (target_seg);
+ gas_assert (target_seg);
reloc_type = map_operator_to_reloc (tinsn->tok[0].X_op, TRUE);
fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
&tinsn->tok[0], FALSE, reloc_type);
}
diff = total_size - old_size;
- assert (diff >= 0);
+ gas_assert (diff >= 0);
if (diff != 0)
expanded = TRUE;
- assert (diff <= fragP->fr_var);
+ gas_assert (diff <= fragP->fr_var);
fragP->fr_var -= diff;
fragP->fr_fix += diff;
}
segT seg = now_seg;
subsegT subseg = now_subseg;
- assert (new_seg != 0);
+ gas_assert (new_seg != 0);
subseg_set (new_seg, new_subseg);
new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
tinsn_immed_from_frag (&loop_insn, fragP, 0);
- assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
+ gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
addi_offset += loop_offset;
addmi_offset += loop_offset;
- assert (tinsn->ntok == 2);
+ gas_assert (tinsn->ntok == 2);
if (tinsn->tok[1].X_op == O_constant)
target = tinsn->tok[1].X_add_number;
else if (tinsn->tok[1].X_op == O_symbol)
{
/* Find the fragment. */
symbolS *sym = tinsn->tok[1].X_add_symbol;
- assert (S_GET_SEGMENT (sym) == segP
+ gas_assert (S_GET_SEGMENT (sym) == segP
|| S_GET_SEGMENT (sym) == absolute_section);
target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
}
_("loop too long for LOOP instruction"));
tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
- assert (addi_insn.opcode == xtensa_addi_opcode);
+ gas_assert (addi_insn.opcode == xtensa_addi_opcode);
tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
- assert (addmi_insn.opcode == xtensa_addmi_opcode);
+ gas_assert (addmi_insn.opcode == xtensa_addmi_opcode);
set_expr_const (&addi_insn.tok[2], loop_length_lo);
tinsn_to_insnbuf (&addi_insn, insnbuf);
segT literal_section = head->seg;
/* Move the literal section to the front of the section list. */
- assert (literal_section);
+ gas_assert (literal_section);
if (literal_section != stdoutput->sections)
{
bfd_section_list_remove (stdoutput, literal_section);
{
seg_list *segment;
frchainS *frchain_from, *frchain_to;
- fragS *search_frag, *next_frag, *last_frag, *literal_pool, *insert_after;
+ fragS *search_frag, *next_frag, *literal_pool, *insert_after;
fragS **frag_splice;
emit_state state;
segT dest_seg;
fixS *fix, *next_fix, **fix_splice;
sym_list *lit;
+ struct litpool_seg *lps;
+ const char *init_name = INIT_SECTION_NAME;
+ const char *fini_name = FINI_SECTION_NAME;
+ int init_name_len = strlen(init_name);
+ int fini_name_len = strlen(fini_name);
mark_literal_frags (literal_head->next);
if (use_literal_section)
return;
+ /* Assign addresses (rough estimates) to the potential literal pool locations
+ and create new ones if the gaps are too large. */
+
+ for (lps = litpool_seg_list.next; lps; lps = lps->next)
+ {
+ frchainS *frchP = seg_info (lps->seg)->frchainP;
+ struct litpool_frag *lpf = lps->frag_list.next;
+ addressT addr = 0;
+
+ for ( ; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (lpf && fragP == lpf->fragP)
+ {
+ gas_assert(fragP->fr_type == rs_machine_dependent &&
+ (fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN ||
+ fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN));
+ /* Found a litpool location. */
+ lpf->addr = addr;
+ lpf = lpf->next;
+ }
+ if (fragP->fr_type == rs_machine_dependent &&
+ fragP->fr_subtype == RELAX_SLOTS)
+ {
+ int slot;
+ for (slot = 0; slot < MAX_SLOTS; slot++)
+ {
+ if (fragP->tc_frag_data.literal_frags[slot])
+ {
+ /* L32R; point its literal to the nearest litpool
+ preferring non-"candidate" positions to avoid
+ the jump-around. */
+ fragS *litfrag = fragP->tc_frag_data.literal_frags[slot];
+ struct litpool_frag *lp = lpf->prev;
+ if (!lp->fragP)
+ {
+ break;
+ }
+ while (lp->fragP->fr_subtype ==
+ RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
+ {
+ lp = lp->prev;
+ if (lp->fragP == NULL)
+ {
+ /* End of list; have to bite the bullet.
+ Take the nearest. */
+ lp = lpf->prev;
+ break;
+ }
+ /* Does it (conservatively) reach? */
+ if (addr - lp->addr <= 128 * 1024)
+ {
+ if (lp->fragP->fr_subtype == RELAX_LITERAL_POOL_BEGIN)
+ {
+ /* Found a good one. */
+ break;
+ }
+ else if (lp->prev->fragP &&
+ addr - lp->prev->addr > 128 * 1024)
+ {
+ /* This is still a "candidate" but the next one
+ will be too far away, so revert to the nearest
+ one, convert it and add the jump around. */
+ fragS *poolbeg;
+ fragS *poolend;
+ symbolS *lsym;
+ char label[10 + 2 * sizeof (fragS *)];
+ lp = lpf->prev;
+ poolbeg = lp->fragP;
+ lp->priority = 1;
+ poolbeg->fr_subtype = RELAX_LITERAL_POOL_BEGIN;
+ poolend = poolbeg->fr_next;
+ gas_assert (poolend->fr_type == rs_machine_dependent &&
+ poolend->fr_subtype == RELAX_LITERAL_POOL_END);
+ /* Create a local symbol pointing to the
+ end of the pool. */
+ sprintf (label, ".L0_LT_%p", poolbeg);
+ lsym = (symbolS *)local_symbol_make (label, lps->seg,
+ 0, poolend);
+ poolbeg->fr_symbol = lsym;
+ /* Rest is done in xtensa_relax_frag. */
+ }
+ }
+ }
+ if (! litfrag->tc_frag_data.literal_frag)
+ {
+ /* Take earliest use of this literal to avoid
+ forward refs. */
+ litfrag->tc_frag_data.literal_frag = lp->fragP;
+ }
+ }
+ }
+ }
+ addr += fragP->fr_fix;
+ if (fragP->fr_type == rs_fill)
+ addr += fragP->fr_offset;
+ }
+ }
+ }
+
for (segment = literal_head->next; segment; segment = segment->next)
{
+ const char *seg_name = segment_name (segment->seg);
+
/* Keep the literals for .init and .fini in separate sections. */
- if (!strcmp (segment_name (segment->seg), INIT_SECTION_NAME)
- || !strcmp (segment_name (segment->seg), FINI_SECTION_NAME))
+ if ((!memcmp (seg_name, init_name, init_name_len) &&
+ !strcmp (seg_name + init_name_len, ".literal")) ||
+ (!memcmp (seg_name, fini_name, fini_name_len) &&
+ !strcmp (seg_name + fini_name_len, ".literal")))
continue;
frchain_from = seg_info (segment->seg)->frchainP;
frchain_to = NULL;
frag_splice = &(frchain_from->frch_root);
- while (!search_frag->tc_frag_data.literal_frag)
+ while (search_frag && !search_frag->tc_frag_data.literal_frag)
{
- assert (search_frag->fr_fix == 0
+ gas_assert (search_frag->fr_fix == 0
|| search_frag->fr_type == rs_align);
search_frag = search_frag->fr_next;
}
- assert (search_frag->tc_frag_data.literal_frag->fr_subtype
+ if (!search_frag)
+ {
+ search_frag = frchain_from->frch_root;
+ as_bad_where (search_frag->fr_file, search_frag->fr_line,
+ _("literal pool location required for text-section-literals; specify with .literal_position"));
+ continue;
+ }
+
+ gas_assert (search_frag->tc_frag_data.literal_frag->fr_subtype
== RELAX_LITERAL_POOL_BEGIN);
xtensa_switch_section_emit_state (&state, segment->seg, 0);
frags in it. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
- last_frag = frag_now;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
while (search_frag != frag_now)
{
next_frag = search_frag->fr_next;
-
- /* First, move the frag out of the literal section and
- to the appropriate place. */
if (search_frag->tc_frag_data.literal_frag)
{
literal_pool = search_frag->tc_frag_data.literal_frag;
- assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
+ gas_assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
frchain_to = literal_pool->tc_frag_data.lit_frchain;
- assert (frchain_to);
+ gas_assert (frchain_to);
+ }
+
+ if (search_frag->fr_type == rs_fill && search_frag->fr_fix == 0)
+ {
+ /* Skip empty fill frags. */
+ *frag_splice = next_frag;
+ search_frag = next_frag;
+ continue;
+ }
+
+ if (search_frag->fr_type == rs_align)
+ {
+ /* Skip alignment frags, because the pool as a whole will be
+ aligned if used, and we don't want to force alignment if the
+ pool is unused. */
+ *frag_splice = next_frag;
+ search_frag = next_frag;
+ continue;
+ }
+
+ /* First, move the frag out of the literal section and
+ to the appropriate place. */
+
+ /* Insert an aligmnent frag at start of pool. */
+ if (literal_pool->fr_next->fr_type == rs_machine_dependent &&
+ literal_pool->fr_next->fr_subtype == RELAX_LITERAL_POOL_END)
+ {
+ segT pool_seg = literal_pool->fr_next->tc_frag_data.lit_seg;
+ emit_state prev_state;
+ fragS *prev_frag;
+ fragS *align_frag;
+ xtensa_switch_section_emit_state (&prev_state, pool_seg, 0);
+ prev_frag = frag_now;
+ frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ align_frag = frag_now;
+ frag_align (2, 0, 0);
+ /* Splice it into the right place. */
+ prev_frag->fr_next = align_frag->fr_next;
+ align_frag->fr_next = literal_pool->fr_next;
+ literal_pool->fr_next = align_frag;
+ /* Insert after this one. */
+ literal_pool->tc_frag_data.literal_frag = align_frag;
+ xtensa_restore_emit_state (&prev_state);
}
insert_after = literal_pool->tc_frag_data.literal_frag;
dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
+ /* Skip align frag. */
+ if (insert_after->fr_next->fr_type == rs_align)
+ {
+ insert_after = insert_after->fr_next;
+ }
*frag_splice = next_frag;
search_frag->fr_next = insert_after->fr_next;
frchain_from = seg_info (segment->seg)->frchainP;
as_warn (_("fixes not all moved from %s"), segment->seg->name);
- assert (frchain_from->fix_root == NULL);
+ gas_assert (frchain_from->fix_root == NULL);
}
frchain_from->fix_tail = NULL;
xtensa_restore_emit_state (&state);
for (lit = literal_syms; lit; lit = lit->next)
{
symbolS *lit_sym = lit->sym;
- segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
- if (dest_seg)
- S_SET_SEGMENT (lit_sym, dest_seg);
+ segT dseg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
+ if (dseg)
+ S_SET_SEGMENT (lit_sym, dseg);
}
}
segT literal_section = head->seg;
/* Move the literal section after "after". */
- assert (literal_section);
+ gas_assert (literal_section);
if (literal_section != after)
{
bfd_section_list_remove (stdoutput, literal_section);
/* Now perform the final error check. */
for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
new_count++;
- assert (new_count == old_count);
+ gas_assert (new_count == old_count);
}
&& !recursive
&& !is_init && ! is_fini)
{
- as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
+ if (!auto_litpools)
+ {
+ as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
+ }
/* When we mark a literal pool location, we want to put a frag in
the literal pool that points to it. But to do that, we want to
{
const char *gname = inf;
const char *group_name = elf_group_name (sec);
-
+
return (group_name == gname
|| (group_name != NULL
&& gname != NULL
if (*pcached)
return *pcached;
-
+
text_name = default_lit_sections.lit_prefix;
if (! text_name || ! *text_name)
{
}
else
{
- /* If the section name ends with ".text", then replace that suffix
- instead of appending an additional suffix. */
+ /* If the section name begins or ends with ".text", then replace
+ that portion instead of appending an additional suffix. */
size_t len = strlen (text_name);
- if (len >= 5 && strcmp (text_name + len - 5, ".text") == 0)
+ if (len >= 5
+ && (strcmp (text_name + len - 5, ".text") == 0
+ || strncmp (text_name, ".text", 5) == 0))
len -= 5;
name = xmalloc (len + strlen (base_name) + 1);
- strcpy (name, text_name);
- strcpy (name + len, base_name);
+ if (strncmp (text_name, ".text", 5) == 0)
+ {
+ strcpy (name, base_name);
+ strcat (name, text_name + 5);
+ }
+ else
+ {
+ strcpy (name, text_name);
+ strcpy (name + len, base_name);
+ }
}
/* Canonicalize section names to allow renaming literal sections.
static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
static void xtensa_frag_flags_init (frag_flags *);
static void get_frag_property_flags (const fragS *, frag_flags *);
-static bfd_vma frag_flags_to_number (const frag_flags *);
+static flagword frag_flags_to_number (const frag_flags *);
static void add_xt_prop_frags (segT, xtensa_block_info **, frag_flags_fn);
/* Set up property tables after relaxation. */
static bfd_boolean
get_frag_is_literal (const fragS *fragP)
{
- assert (fragP != NULL);
+ gas_assert (fragP != NULL);
return fragP->tc_frag_data.is_literal;
}
fixS *fix;
/* Write the fixup. */
- assert (cur_block);
+ gas_assert (cur_block);
fix = fix_new (frag_now, i * 8, 4,
section_symbol (cur_block->sec),
cur_block->offset,
fixS *fix;
/* Write the fixup. */
- assert (cur_block);
+ gas_assert (cur_block);
fix = fix_new (frag_now, i * 12, 4,
section_symbol (cur_block->sec),
cur_block->offset,
cur_block->size, 4);
md_number_to_chars (&frag_data[8 + i * 12],
frag_flags_to_number (&cur_block->flags),
- 4);
+ sizeof (flagword));
cur_block = cur_block->next;
}
frag_wane (frag_now);
frag_predicate property_function,
frag_predicate end_property_function)
{
- bfd_vma seg_offset;
fragS *fragP;
/* Build it if needed. */
/* We are either at NULL at the beginning or at the end. */
/* Walk through the frags. */
- seg_offset = 0;
-
if (seg_info (sec)->frchainP)
{
for (fragP = seg_info (sec)->frchainP->frch_root;
}
-static bfd_vma
+static flagword
frag_flags_to_number (const frag_flags *prop_flags)
{
- bfd_vma num = 0;
+ flagword num = 0;
if (prop_flags->is_literal)
num |= XTENSA_PROP_LITERAL;
if (prop_flags->is_insn)
xtensa_block_info **xt_block,
frag_flags_fn property_function)
{
- bfd_vma seg_offset;
fragS *fragP;
/* Build it if needed. */
/* We are either at NULL at the beginning or at the end. */
/* Walk through the frags. */
- seg_offset = 0;
-
if (seg_info (sec)->frchainP)
{
for (fragP = seg_info (sec)->frchainP->frch_root; fragP;
op_placement_table = (op_placement_info_table)
xmalloc (sizeof (op_placement_info) * num_opcodes);
- assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
+ gas_assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
for (opcode = 0; opcode < num_opcodes; opcode++)
{
void
istack_init (IStack *stack)
{
- memset (stack, 0, sizeof (IStack));
stack->ninsn = 0;
}
istack_top (IStack *stack)
{
int rec = stack->ninsn - 1;
- assert (!istack_empty (stack));
+ gas_assert (!istack_empty (stack));
return &stack->insn[rec];
}
istack_push (IStack *stack, TInsn *insn)
{
int rec = stack->ninsn;
- assert (!istack_full (stack));
+ gas_assert (!istack_full (stack));
stack->insn[rec] = *insn;
stack->ninsn++;
}
{
int rec = stack->ninsn;
TInsn *insn;
- assert (!istack_full (stack));
+ gas_assert (!istack_full (stack));
insn = &stack->insn[rec];
tinsn_init (insn);
stack->ninsn++;
istack_pop (IStack *stack)
{
int rec = stack->ninsn - 1;
- assert (!istack_empty (stack));
+ gas_assert (!istack_empty (stack));
stack->ninsn--;
tinsn_init (&stack->insn[rec]);
}
int i;
int n = insn->ntok;
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
int i;
int n = insn->ntok;
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
{
int i;
int n = insn->ntok;
- assert (insn->insn_type == ITYPE_INSN);
+ gas_assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
switch (insn->tok[i].X_op)
int noperands = xtensa_opcode_num_operands (isa, opcode);
int i;
- assert (tinsn->insn_type == ITYPE_INSN);
+ gas_assert (tinsn->insn_type == ITYPE_INSN);
if (noperands != tinsn->ntok)
as_fatal (_("operand number mismatch"));
for (i = 0; i < noperands; i++)
{
- expressionS *expr = &tinsn->tok[i];
+ expressionS *exp = &tinsn->tok[i];
int rc;
unsigned line;
char *file_name;
uint32 opnd_value;
- switch (expr->X_op)
+ switch (exp->X_op)
{
case O_register:
if (xtensa_operand_is_visible (isa, opcode, i) == 0)
break;
/* The register number has already been checked in
expression_maybe_register, so we don't need to check here. */
- opnd_value = expr->X_add_number;
+ opnd_value = exp->X_add_number;
(void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
opnd_value);
/* It is a constant and we called this function
then we have to try to fit it. */
xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
- expr->X_add_number, file_name, line);
+ exp->X_add_number, file_name, line);
break;
default:
if (fragP->tc_frag_data.slot_symbols[slot])
{
opnum = get_relaxable_immed (opcode);
- assert (opnum >= 0);
+ gas_assert (opnum >= 0);
set_expr_symbol_offset (&tinsn->tok[opnum],
fragP->tc_frag_data.slot_symbols[slot],
fragP->tc_frag_data.slot_offsets[slot]);
if (v->insnbuf == NULL)
as_fatal (_("out of memory"));
- for (i = 0; i < MAX_SLOTS; i++)
+ for (i = 0; i < config_max_slots; i++)
{
v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
if (v->slotbuf[i] == NULL)
{
int i;
- memset (v, 0, offsetof (vliw_insn, insnbuf));
+ memset (v, 0, offsetof (vliw_insn, slots)
+ + sizeof(TInsn) * config_max_slots);
v->format = XTENSA_UNDEFINED;
v->num_slots = 0;
if (xt_saved_debug_type != DEBUG_NONE)
debug_type = xt_saved_debug_type;
- for (i = 0; i < MAX_SLOTS; i++)
+ for (i = 0; i < config_max_slots; i++)
v->slots[i].opcode = XTENSA_UNDEFINED;
}
+static void
+xg_copy_vinsn (vliw_insn *dst, vliw_insn *src)
+{
+ memcpy (dst, src,
+ offsetof(vliw_insn, slots) + src->num_slots * sizeof(TInsn));
+ dst->insnbuf = src->insnbuf;
+ memcpy (dst->slotbuf, src->slotbuf, src->num_slots * sizeof(xtensa_insnbuf));
+}
+
+
static bfd_boolean
vinsn_has_specific_opcodes (vliw_insn *v)
{
{
int i;
xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
- for (i = 0; i < MAX_SLOTS; i++)
+ for (i = 0; i < config_max_slots; i++)
xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
}
for (i = 0; i < noperands; i++)
{
- expressionS* expr = &tinsn->tok[i];
- switch (expr->X_op)
+ expressionS* exp = &tinsn->tok[i];
+ switch (exp->X_op)
{
case O_symbol:
case O_lo16:
|| tinsn->is_specific_opcode
|| !xg_is_relaxable_insn (tinsn, 0))
{
- xg_add_opcode_fix (tinsn, i, fmt, slot, expr, fragP,
+ xg_add_opcode_fix (tinsn, i, fmt, slot, exp, fragP,
frag_offset - fragP->fr_literal);
}
else
{
- if (expr->X_op != O_symbol)
+ if (exp->X_op != O_symbol)
as_bad (_("invalid operand"));
- tinsn->symbol = expr->X_add_symbol;
- tinsn->offset = expr->X_add_number;
+ tinsn->symbol = exp->X_add_symbol;
+ tinsn->offset = exp->X_add_number;
}
}
else
offsetT
get_expr_const (const expressionS *s)
{
- assert (expr_is_const (s));
+ gas_assert (expr_is_const (s));
return s->X_add_number;
}
offsetT
get_expr_register (const expressionS *s)
{
- assert (expr_is_register (s));
+ gas_assert (expr_is_register (s));
return s->X_add_number;
}