/* tc-xtensa.c -- Assemble Xtensa instructions.
- Copyright 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012
- Free Software Foundation, Inc.
+ Copyright (C) 2003-2020 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
#include "xtensa-relax.h"
#include "dwarf2dbg.h"
#include "xtensa-istack.h"
-#include "struc-symbol.h"
#include "xtensa-config.h"
+#include "elf/xtensa.h"
/* Provide default values for new configuration settings. */
#ifndef XSHAL_ABI
#define XSHAL_ABI 0
#endif
+#ifndef XTENSA_MARCH_EARLIEST
+#define XTENSA_MARCH_EARLIEST 0
+#endif
+
#ifndef uint32
#define uint32 unsigned int
#endif
/* Flags to indicate whether the hardware supports the density and
absolute literals options. */
-bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
-bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
+bfd_boolean density_supported;
+bfd_boolean absolute_literals_supported;
+
+static unsigned microarch_earliest;
static vliw_insn cur_vinsn;
unsigned xtensa_num_pipe_stages;
-unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
+unsigned xtensa_fetch_width;
static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
/* Required branch target alignment. */
#define XTENSA_PROP_BT_ALIGN_REQUIRE 0x3
-#define GET_XTENSA_PROP_BT_ALIGN(flag) \
- (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
(((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
(((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
#define XTENSA_PROP_ALIGNMENT_MASK 0x0001f000
-#define GET_XTENSA_PROP_ALIGNMENT(flag) \
- (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
(((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
(((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
struct suffix_reloc_map
{
- char *suffix;
+ const char *suffix;
int length;
bfd_reloc_code_real_type reloc;
- unsigned char operator;
+ operatorT operator;
};
#define SUFFIX_MAP(str, reloc, op) { str, sizeof (str) - 1, reloc, op }
SUFFIX_MAP ("tlscall", BFD_RELOC_XTENSA_TLS_CALL, O_tlscall),
SUFFIX_MAP ("tpoff", BFD_RELOC_XTENSA_TLS_TPOFF, O_tpoff),
SUFFIX_MAP ("dtpoff", BFD_RELOC_XTENSA_TLS_DTPOFF, O_dtpoff),
- { (char *) 0, 0, BFD_RELOC_UNUSED, 0 }
};
{
FALSE, /* none */
FALSE, /* literal */
-#if !XCHAL_HAVE_DENSITY
FALSE, /* density */
-#else
- TRUE, /* density */
-#endif
TRUE, /* transform */
FALSE, /* freeregs */
FALSE, /* longcalls */
FALSE, /* literal_prefix */
FALSE, /* schedule */
-#if XSHAL_USE_ABSOLUTE_LITERALS
- TRUE /* absolute_literals */
-#else
FALSE /* absolute_literals */
-#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;
+ int literal_count;
+};
+
+/* 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;
+
+/* Limit maximal size of auto litpool by half of the j range. */
+#define MAX_AUTO_POOL_LITERALS 16384
+
+/* Limit maximal size of explicit literal pool by l32r range. */
+#define MAX_EXPLICIT_POOL_LITERALS 65536
+
+#define MAX_POOL_LITERALS \
+ (auto_litpools ? MAX_AUTO_POOL_LITERALS : MAX_EXPLICIT_POOL_LITERALS)
/* 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 (fragS *);
+static fixS *xg_append_jump (fragS *fragP, symbolS *sym, offsetT offset);
+static void xtensa_maybe_create_literal_pool_frag (bfd_boolean, bfd_boolean);
+static bfd_boolean auto_litpools = FALSE;
+static int auto_litpool_limit = 0;
+static bfd_boolean xtensa_is_init_fini (segT seg);
/* Alignment Functions. */
static void xtensa_restore_emit_state (emit_state *);
static segT cache_literal_section (bfd_boolean);
-/* Import from elf32-xtensa.c in BFD library. */
-
-extern asection *xtensa_make_property_section (asection *, const char *);
-
/* op_placement_info functions. */
static void init_op_placement_info_table (void);
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 bfd_boolean workaround_close_loop_end = FALSE;
static bfd_boolean maybe_has_close_loop_end = FALSE;
static bfd_boolean enforce_three_byte_loop_align = FALSE;
+static bfd_boolean opt_linkrelax = TRUE;
/* When workaround_short_loops is TRUE, all loops with early exits must
have at least 3 instructions. workaround_all_short_loops is a modifier
static bfd_boolean workaround_all_short_loops = FALSE;
+/* Generate individual property section for every section.
+ This option is defined in BDF library. */
+extern bfd_boolean elf32xtensa_separate_props;
static void
xtensa_setup_hw_workarounds (int earliest, int latest)
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,
+
+ option_separate_props,
+ option_no_separate_props,
};
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 },
+
+ { "separate-prop-tables", no_argument, NULL, option_separate_props },
+
{ NULL, no_argument, NULL, 0 }
};
int
-md_parse_option (int c, char *arg)
+md_parse_option (int c, const char *arg)
{
switch (c)
{
as_warn (_("--no-density option is ignored"));
return 1;
case option_link_relax:
- linkrelax = 1;
+ opt_linkrelax = TRUE;
return 1;
case option_no_link_relax:
- linkrelax = 0;
+ opt_linkrelax = FALSE;
return 1;
case option_flix:
produce_flix = FLIX_ALL;
case option_target_hardware:
{
int earliest, latest = 0;
+ char *end;
if (*arg == 0 || *arg == '-')
as_fatal (_("invalid target hardware version"));
- earliest = strtol (arg, &arg, 0);
+ earliest = strtol (arg, &end, 0);
- if (*arg == 0)
+ if (*end == 0)
latest = earliest;
- else if (*arg == '-')
+ else if (*end == '-')
{
- if (*++arg == 0)
+ if (*++end == 0)
as_fatal (_("invalid target hardware version"));
- latest = strtol (arg, &arg, 0);
+ latest = strtol (end, &end, 0);
}
- if (*arg != 0)
+ if (*end != 0)
as_fatal (_("invalid target hardware version"));
xtensa_setup_hw_workarounds (earliest, latest);
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;
+ if (auto_litpool_limit <= 0)
+ auto_litpool_limit = MAX_AUTO_POOL_LITERALS / 2;
+ return 1;
+
+ case option_no_auto_litpools:
+ auto_litpools = FALSE;
+ auto_litpool_limit = -1;
+ return 1;
+
+ case option_auto_litpool_limit:
+ {
+ int value = 0;
+ char *end;
+ 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, &end, 10);
+ if (*end != 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;
+ }
+
+ case option_separate_props:
+ elf32xtensa_separate_props = TRUE;
+ return 1;
+
+ case option_no_separate_props:
+ elf32xtensa_separate_props = 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\
+ --[no-]separate-prop-tables\n\
+ [Do not] place Xtensa property records into\n\
+ individual property sections for each section.\n\
+ Default is to generate single property section.\n", stream);
}
\f
sym_list *l;
if (!free_insn_labels)
- l = (sym_list *) xmalloc (sizeof (sym_list));
+ l = XNEW (sym_list);
else
{
l = free_insn_labels;
static void
directive_push (directiveE directive, bfd_boolean negated, const void *datum)
{
- char *file;
+ const char *file;
unsigned int line;
- state_stackS *stack = (state_stackS *) xmalloc (sizeof (state_stackS));
+ state_stackS *stack = XNEW (state_stackS);
- as_where (&file, &line);
+ file = as_where (&line);
stack->directive = directive;
stack->negated = negated;
if (!directive_state_stack)
{
- as_bad (_("unmatched end directive"));
+ as_bad (_("unmatched .end directive"));
*directive = directive_none;
return;
}
{
int len;
unsigned i;
- char *directive_string;
+ const char *directive_string;
if (strncmp (input_line_pointer, "no-", 3) != 0)
*negated = FALSE;
insn_labels = NULL;
}
as_warn (_(".begin literal is deprecated; use .literal instead"));
- state = (emit_state *) xmalloc (sizeof (emit_state));
+ state = XNEW (emit_state);
xtensa_switch_to_literal_fragment (state);
directive_push (directive_literal, negated, state);
break;
/* Allocate the literal state for this section and push
onto the directive stack. */
- ls = xmalloc (sizeof (lit_state));
+ ls = XNEW (lit_state);
gas_assert (ls);
*ls = default_lit_sections;
emit_state state;
char *p, *base_name;
char c;
- segT dest_seg;
if (inside_directive (directive_literal))
{
saved_insn_labels = insn_labels;
insn_labels = NULL;
- /* If we are using text-section literals, then this is the right value... */
- dest_seg = now_seg;
-
base_name = input_line_pointer;
xtensa_switch_to_literal_fragment (&state);
- /* ...but if we aren't using text-section-literals, then we
- need to put them in the section we just switched to. */
- 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);
"abcdefghijklmnopqrstuvwxyz_/0123456789.$");
/* Get a null-terminated copy of the name. */
- name = xmalloc (len + 1);
- gas_assert (name);
- strncpy (name, input_line_pointer, len);
- name[len] = 0;
+ name = xmemdup0 (input_line_pointer, len);
/* Skip the name in the input line. */
input_line_pointer += len;
as_bad (_("opcode-specific %s relocation used outside "
"an instruction"), reloc_howto->name);
else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
- as_bad (_("%s relocations do not fit in %d bytes"),
+ as_bad (ngettext ("%s relocations do not fit in %d byte",
+ "%s relocations do not fit in %d bytes",
+ nbytes),
reloc_howto->name, nbytes);
else if (reloc == BFD_RELOC_XTENSA_TLS_FUNC
|| reloc == BFD_RELOC_XTENSA_TLS_ARG
char *str2;
int ch;
int len;
- struct suffix_reloc_map *ptr;
+ unsigned int i;
if (*str++ != '@')
return BFD_RELOC_NONE;
len = str2 - ident;
ch = ident[0];
- for (ptr = &suffix_relocs[0]; ptr->length > 0; ptr++)
- if (ch == ptr->suffix[0]
- && len == ptr->length
- && memcmp (ident, ptr->suffix, ptr->length) == 0)
+ for (i = 0; i < ARRAY_SIZE (suffix_relocs); i++)
+ if (ch == suffix_relocs[i].suffix[0]
+ && len == suffix_relocs[i].length
+ && memcmp (ident, suffix_relocs[i].suffix, suffix_relocs[i].length) == 0)
{
/* Now check for "identifier@suffix+constant". */
if (*str == '-' || *str == '+')
}
*str_p = str;
- return ptr->reloc;
+ return suffix_relocs[i].reloc;
}
return BFD_RELOC_UNUSED;
/* Find the matching operator type. */
-static unsigned char
+static operatorT
map_suffix_reloc_to_operator (bfd_reloc_code_real_type reloc)
{
- struct suffix_reloc_map *sfx;
- unsigned char operator = (unsigned char) -1;
-
- for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
+ operatorT operator = O_illegal;
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE (suffix_relocs); i++)
{
- if (sfx->reloc == reloc)
+ if (suffix_relocs[i].reloc == reloc)
{
- operator = sfx->operator;
+ operator = suffix_relocs[i].operator;
break;
}
}
- gas_assert (operator != (unsigned char) -1);
+ gas_assert (operator != O_illegal);
return operator;
}
static bfd_reloc_code_real_type
map_operator_to_reloc (unsigned char operator, bfd_boolean is_literal)
{
- struct suffix_reloc_map *sfx;
+ unsigned int i;
bfd_reloc_code_real_type reloc = BFD_RELOC_UNUSED;
- for (sfx = &suffix_relocs[0]; sfx->suffix; sfx++)
+ for (i = 0; i < ARRAY_SIZE (suffix_relocs); i++)
{
- if (sfx->operator == operator)
+ if (suffix_relocs[i].operator == operator)
{
- reloc = sfx->reloc;
+ reloc = suffix_relocs[i].reloc;
break;
}
}
arg_end += 1;
arg_len = arg_end - input_line_pointer;
- arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
+ arg = XNEWVEC (char, (saw_colon ? 1 : 0) + arg_len + 1);
args[num_args] = arg;
if (saw_colon)
}
}
-fini:
+ fini:
if (saw_comma || saw_colon)
goto err;
input_line_pointer = old_input_line_pointer;
return num_args;
-err:
+ err:
if (saw_comma)
as_bad (_("extra comma"));
else if (saw_colon)
cnt_arg = *cnt_argp;
/* replace the argument with "31-(argument)" */
- new_arg = (char *) xmalloc (strlen (cnt_arg) + 6);
- sprintf (new_arg, "31-(%s)", cnt_arg);
+ new_arg = concat ("31-(", cnt_arg, ")", (char *) NULL);
free (cnt_arg);
*cnt_argp = new_arg;
static void
-xg_replace_opname (char **popname, char *newop)
+xg_replace_opname (char **popname, const char *newop)
{
free (*popname);
- *popname = (char *) xmalloc (strlen (newop) + 1);
- strcpy (*popname, newop);
+ *popname = xstrdup (newop);
}
/* Another special case for "WSR.INTSET".... */
if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
sr_name = "intset";
- new_opname = (char *) xmalloc (strlen (sr_name) + 6);
- sprintf (new_opname, "%s.%s", *popname, sr_name);
+ new_opname = concat (*popname, ".", sr_name, (char *) NULL);
free (*popname);
*popname = new_opname;
/* Translate the opcode. */
sr_name = xtensa_sysreg_name (isa, sr);
- new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ new_opname = XNEWVEC (char, strlen (sr_name) + 6);
sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
opname[0], sr_name);
free (*popname);
static int
-xtensa_translate_zero_immed (char *old_op,
- char *new_op,
+xtensa_translate_zero_immed (const char *old_op,
+ const char *new_op,
char **popname,
int *pnum_args,
char **arg_strings)
if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
return -1;
xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
- arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
- strcpy (arg_strings[2], arg_strings[1]);
+ arg_strings[2] = xstrdup (arg_strings[1]);
*pnum_args = 3;
}
return 0;
}
+ /* Without an operand, this is given a default immediate operand of 0. */
+ if ((strcmp (opname, "simcall") == 0 && microarch_earliest >= 280000))
+ {
+ if (*pnum_args == 0)
+ {
+ arg_strings[0] = (char *) xmalloc (2);
+ strcpy (arg_strings[0], "0");
+ *pnum_args = 1;
+ }
+ return 0;
+ }
+
if (strcmp (opname, "bbsi.l") == 0)
{
if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
}
/* 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 (xg_check_num_args (pnum_args, 0, opname, arg_strings))
return -1;
xg_replace_opname (popname, (has_underbar ? "_or" : "or"));
- arg_strings[0] = (char *) xmalloc (3);
- arg_strings[1] = (char *) xmalloc (3);
- arg_strings[2] = (char *) xmalloc (3);
- strcpy (arg_strings[0], "a1");
- strcpy (arg_strings[1], "a1");
- strcpy (arg_strings[2], "a1");
+ arg_strings[0] = xstrdup ("a1");
+ arg_strings[1] = xstrdup ("a1");
+ arg_strings[2] = xstrdup ("a1");
*pnum_args = 3;
}
return 0;
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);
orig_insn->opcode);
if (orig_insn->ntok < noperands)
{
- as_bad (_("found %d operands for '%s': Expected %d"),
+ as_bad (ngettext ("found %d operand for '%s': Expected %d",
+ "found %d operands for '%s': Expected %d",
+ orig_insn->ntok),
orig_insn->ntok,
xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
noperands);
return TRUE;
}
if (orig_insn->ntok > noperands)
- as_warn (_("found too many (%d) operands for '%s': Expected %d"),
+ as_warn (ngettext ("found %d operand for '%s': Expected %d",
+ "found %d operands for '%s': Expected %d",
+ orig_insn->ntok),
orig_insn->ntok,
xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
noperands);
{
flagword flags, link_once_flags;
- flags = bfd_get_section_flags (abfd, sec);
+ flags = bfd_section_flags (sec);
link_once_flags = (flags & SEC_LINK_ONCE);
/* Flags might not be set yet. */
{
sym_list *l;
- l = (sym_list *) xmalloc (sizeof (sym_list));
+ l = XNEW (sym_list);
l->sym = sym;
l->next = literal_syms;
literal_syms = l;
{
/* Any labels pointing to the current location need
to be adjusted to after the literal pool. */
- emit_state s;
fragS *pool_location;
if (use_literal_section)
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);
RELAX_LITERAL_POOL_END, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
- /* Now put a frag into the literal pool that points to this location. */
set_literal_pool_location (now_seg, pool_location);
- xtensa_switch_to_non_abs_literal_fragment (&s);
- frag_align (2, 0, 0);
- record_alignment (now_seg, 2);
-
- /* Close whatever frag is there. */
- frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
- xtensa_set_frag_assembly_state (frag_now);
- frag_now->tc_frag_data.literal_frag = pool_location;
- frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
- xtensa_restore_emit_state (&s);
- xtensa_set_frag_assembly_state (frag_now);
}
static fragS *
get_literal_pool_location (segT seg)
{
+ if (auto_litpools)
+ {
+ 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;
}
segment_info_type *seginfo;
fragS *fragP;
flagword flags;
- flags = bfd_get_section_flags (stdoutput, sec);
+ flags = bfd_section_flags (sec);
if (flags & SEC_DEBUGGING)
continue;
if (!(flags & SEC_ALLOC))
segment_info_type *seginfo;
fragS *fragP;
flagword flags;
- flags = bfd_get_section_flags (stdoutput, sec);
+ flags = bfd_section_flags (sec);
if (flags & SEC_DEBUGGING)
continue;
if (!(flags & SEC_ALLOC))
asection *sec,
void *unused ATTRIBUTE_UNUSED)
{
- flagword flags = bfd_get_section_flags (abfd, sec);
+ flagword flags = bfd_section_flags (sec);
segment_info_type *seginfo = seg_info (sec);
fragS *frag = seginfo->frchainP->frch_root;
asection *sec,
void *unused ATTRIBUTE_UNUSED)
{
- flagword flags = bfd_get_section_flags (abfd, sec);
+ flagword flags = bfd_section_flags (sec);
segment_info_type *seginfo = seg_info (sec);
fragS *frag = seginfo->frchainP->frch_root;
xtensa_isa isa = xtensa_default_isa;
if (frag->fr_fix == 0)
frag = next_non_empty_frag (frag);
-
+
if (frag)
{
xtensa_insnbuf_from_chars
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"),
number_to_chars_littleendian (buf, val, n);
}
+static void
+xg_init_global_config (void)
+{
+ target_big_endian = XCHAL_HAVE_BE;
+
+ density_supported = XCHAL_HAVE_DENSITY;
+ absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
+ xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
+
+ directive_state[directive_density] = XCHAL_HAVE_DENSITY;
+ directive_state[directive_absolute_literals] = XSHAL_USE_ABSOLUTE_LITERALS;
+
+ microarch_earliest = XTENSA_MARCH_EARLIEST;
+}
+
+void
+xtensa_init (int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED)
+{
+ xg_init_global_config ();
+}
/* This function is called once, at assembler startup time. It should
set up all the tables, etc. that the MD part of the assembler will
xtensa_default_isa = xtensa_isa_init (0, 0);
isa = xtensa_default_isa;
- linkrelax = 1;
+ linkrelax = opt_linkrelax;
/* Set up the literal sections. */
memset (&default_lit_sections, 0, sizeof (default_lit_sections));
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++)
+ 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)
/* Set up the assembly state. */
if (!frag_now->tc_frag_data.is_assembly_state_set)
xtensa_set_frag_assembly_state (frag_now);
+
+ if (!use_literal_section)
+ xtensa_mark_literal_pool_location ();
}
/* Split off the opcode. */
opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/0123456789.");
- opname = xmalloc (opnamelen + 1);
- memcpy (opname, str, opnamelen);
- opname[opnamelen] = '\0';
+ opname = xstrndup (str, opnamelen);
num_args = tokenize_arguments (arg_strings, str + opnamelen);
if (num_args == -1)
orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
- /* Special case: Check for "CALLXn.TLS" psuedo op. If found, grab its
+ /* Special case: Check for "CALLXn.TLS" pseudo op. If found, grab its
extra argument and set the opcode to "CALLXn". */
if (orig_insn.opcode == XTENSA_UNDEFINED
&& strncasecmp (opname, "callx", 5) == 0)
}
}
- /* Special case: Check for "j.l" psuedo op. */
+ /* Special case: Check for "j.l" pseudo op. */
if (orig_insn.opcode == XTENSA_UNDEFINED
&& strncasecmp (opname, "j.l", 3) == 0)
{
/* 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)
{
/* Set up the assembly state. */
if (!frag_now->tc_frag_data.is_assembly_state_set)
xtensa_set_frag_assembly_state (frag_now);
+
+ if (!use_literal_section
+ && seg_info (now_seg)->tc_segment_info_data.literal_pool_loc == NULL
+ && !xtensa_is_init_fini (now_seg))
+ xtensa_mark_literal_pool_location ();
}
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:
if (fixP->fx_subsy)
{
+ bfd_boolean neg = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ < S_GET_VALUE (fixP->fx_subsy);
+
switch (fixP->fx_r_type)
{
case BFD_RELOC_8:
- fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+ fixP->fx_r_type = neg
+ ? BFD_RELOC_XTENSA_NDIFF8 : BFD_RELOC_XTENSA_PDIFF8;
+ fixP->fx_signed = 0;
break;
case BFD_RELOC_16:
- fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+ fixP->fx_r_type = neg
+ ? BFD_RELOC_XTENSA_NDIFF16 : BFD_RELOC_XTENSA_PDIFF16;
+ fixP->fx_signed = 0;
break;
case BFD_RELOC_32:
- fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+ fixP->fx_r_type = neg
+ ? BFD_RELOC_XTENSA_NDIFF32 : BFD_RELOC_XTENSA_PDIFF32;
+ fixP->fx_signed = 0;
break;
default:
break;
val = *valP;
fixP->fx_done = 1;
}
+ else if (S_GET_SEGMENT (fixP->fx_addsy) == absolute_section)
+ {
+ val = S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset;
+ *valP = val;
+ fixP->fx_done = 1;
+ }
/* fall through */
case BFD_RELOC_XTENSA_PLT:
}
-char *
+const char *
md_atof (int type, char *litP, int *sizeP)
{
return ieee_md_atof (type, litP, sizeP, target_big_endian);
{
arelent *reloc;
- reloc = (arelent *) xmalloc (sizeof (arelent));
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc = XNEW (arelent);
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
opcode_funcUnit_use_stage_func ousf)
{
int i;
- resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
+ resource_table *rt = XNEW (resource_table);
rt->data = data;
rt->cycles = cycles;
rt->allocated_cycles = cycles;
rt->opcode_unit_use = ouuf;
rt->opcode_unit_stage = ousf;
- rt->units = (unsigned char **) xcalloc (cycles, sizeof (unsigned char *));
+ rt->units = XCNEWVEC (unsigned char *, cycles);
for (i = 0; i < cycles; i++)
- rt->units[i] = (unsigned char *) xcalloc (nu, sizeof (unsigned char));
+ rt->units[i] = XCNEWVEC (unsigned char, nu);
return rt;
}
old_cycles = rt->allocated_cycles;
rt->allocated_cycles = cycles;
- rt->units = xrealloc (rt->units,
- rt->allocated_cycles * sizeof (unsigned char *));
+ rt->units = XRESIZEVEC (unsigned char *, rt->units, rt->allocated_cycles);
for (i = 0; i < old_cycles; i++)
- rt->units[i] = xrealloc (rt->units[i],
- rt->num_units * sizeof (unsigned char));
+ rt->units[i] = XRESIZEVEC (unsigned char, rt->units[i], rt->num_units);
for (i = old_cycles; i < cycles; i++)
- rt->units[i] = xcalloc (rt->num_units, sizeof (unsigned char));
+ rt->units[i] = XCNEWVEC (unsigned char, rt->num_units);
}
{
IStack slotstack;
int i;
- char *file_name;
- unsigned line;
+ int slots;
if (find_vinsn_conflicts (vinsn))
{
if (vinsn->format == XTENSA_UNDEFINED)
vinsn->format = xg_find_narrowest_format (vinsn);
- if (xtensa_format_num_slots (xtensa_default_isa, vinsn->format) > 1
+ slots = xtensa_format_num_slots (xtensa_default_isa, vinsn->format);
+ if (slots > 1
&& produce_flix == FLIX_NONE)
{
as_bad (_("The option \"--no-allow-flix\" prohibits multi-slot flix."));
if (vinsn->format == XTENSA_UNDEFINED)
{
- as_where (&file_name, &line);
- as_bad_where (file_name, line,
- _("couldn't find a valid instruction format"));
+ as_bad (_("couldn't find a valid instruction format"));
fprintf (stderr, _(" ops were: "));
for (i = 0; i < vinsn->num_slots; i++)
fprintf (stderr, _(" %s;"),
return;
}
- if (vinsn->num_slots
- != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
+ if (vinsn->num_slots != slots)
{
- as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
+ as_bad (_("mismatch for format '%s': #slots = %d, #opcodes = %d"),
xtensa_format_name (xtensa_default_isa, vinsn->format),
- xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
- vinsn->num_slots);
+ slots, vinsn->num_slots);
xg_clear_vinsn (vinsn);
return;
}
if (resources_conflict (vinsn))
{
- as_where (&file_name, &line);
- as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ as_bad (_("illegal resource usage in bundle"));
fprintf (stderr, " ops were: ");
for (i = 0; i < vinsn->num_slots; i++)
fprintf (stderr, " %s;",
/* Now check resource conflicts on the modified bundle. */
if (resources_conflict (vinsn))
{
- as_where (&file_name, &line);
- as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ as_bad (_("illegal resource usage in bundle"));
fprintf (stderr, " ops were: ");
for (i = 0; i < vinsn->num_slots; i++)
fprintf (stderr, " %s;",
xg_assemble_vliw_tokens (vinsn);
xg_clear_vinsn (vinsn);
+
+ xtensa_check_frag_count ();
}
case ITYPE_LABEL:
{
static int relaxed_sym_idx = 0;
- char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
+ char *label = XNEWVEC (char, strlen (FAKE_LABEL_NAME) + 12);
sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
colon (label);
gas_assert (label_sym == NULL);
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]
+ = symbol_get_frag (tinsn->tok[1].X_add_symbol);
if (tinsn->literal_space != 0)
xg_assemble_literal_space (tinsn->literal_space, slot);
frag_now->tc_frag_data.free_reg[slot] = tinsn->extra_arg;
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 ())
{
xtensa_sanity_check ();
xtensa_add_config_info ();
+
+ xtensa_check_frag_count ();
}
+struct trampoline_chain_entry
+{
+ symbolS *sym;
+ addressT offset;
+};
-static void
-xtensa_cleanup_align_frags (void)
+/* Trampoline chain for a given (sym, offset) pair is a sorted array
+ of locations of trampoline jumps leading there. Jumps are represented
+ as pairs (sym, offset): trampoline frag symbol and offset of the jump
+ inside the frag. */
+struct trampoline_chain
+{
+ struct trampoline_chain_entry target;
+ struct trampoline_chain_entry *entry;
+ size_t n_entries;
+ size_t n_max;
+ bfd_boolean needs_sorting;
+};
+
+struct trampoline_chain_index
{
- frchainS *frchP;
- asection *s;
+ struct trampoline_chain *entry;
+ size_t n_entries;
+ size_t n_max;
+ bfd_boolean needs_sorting;
+};
- for (s = stdoutput->sections; s; s = s->next)
- for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
- {
- fragS *fragP;
- /* Walk over all of the fragments in a subsection. */
- for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
- {
- if ((fragP->fr_type == rs_align
- || fragP->fr_type == rs_align_code
- || (fragP->fr_type == rs_machine_dependent
- && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
- || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
- && fragP->fr_fix == 0)
- {
- fragS *next = fragP->fr_next;
+struct trampoline_index
+{
+ fragS **entry;
+ size_t n_entries;
+ size_t n_max;
+};
- while (next
- && next->fr_fix == 0
- && next->fr_type == rs_machine_dependent
- && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
- {
- frag_wane (next);
- next = next->fr_next;
- }
- }
- /* If we don't widen branch targets, then they
- will be easier to align. */
- if (fragP->tc_frag_data.is_branch_target
- && fragP->fr_opcode == fragP->fr_literal
- && fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_SLOTS
- && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
- frag_wane (fragP);
- if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_UNREACHABLE)
- fragP->tc_frag_data.is_unreachable = TRUE;
- }
- }
-}
+struct trampoline_seg
+{
+ struct trampoline_seg *next;
+ asection *seg;
+ /* Trampolines ordered by their frag fr_address */
+ struct trampoline_index index;
+ /* Known trampoline chains ordered by (sym, offset) pair */
+ struct trampoline_chain_index chain_index;
+};
+static struct trampoline_seg trampoline_seg_list;
+#define J_RANGE (128 * 1024)
+#define J_MARGIN 4096
+
+static int unreachable_count = 0;
-/* Re-process all of the fragments looking to convert all of the
- RELAX_DESIRE_ALIGN_IF_TARGET fragments. If there is a branch
- target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
- Otherwise, convert to a .fill 0. */
static void
-xtensa_fix_target_frags (void)
+xtensa_maybe_create_trampoline_frag (void)
{
- frchainS *frchP;
- asection *s;
+ if (!use_trampolines)
+ return;
- /* When this routine is called, all of the subsections are still intact
- so we walk over subsections instead of sections. */
- for (s = stdoutput->sections; s; s = s->next)
- for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
- {
- fragS *fragP;
+ /* 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. */
- /* Walk over all of the fragments in a subsection. */
- for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
- {
- if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
- {
- if (next_frag_is_branch_target (fragP))
- fragP->fr_subtype = RELAX_DESIRE_ALIGN;
- else
- frag_wane (fragP);
- }
- }
- }
+ if (++unreachable_count > 10)
+ {
+ xtensa_create_trampoline_frag (FALSE);
+ clear_frag_count ();
+ unreachable_count = 0;
+ }
}
-
-static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
-
static void
-xtensa_mark_narrow_branches (void)
+xtensa_check_frag_count (void)
{
- frchainS *frchP;
- asection *s;
+ if (!use_trampolines || frag_now->tc_frag_data.is_no_transform)
+ return;
- for (s = stdoutput->sections; s; s = s->next)
- for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
- {
- fragS *fragP;
- /* Walk over all of the fragments in a subsection. */
- for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
- {
- if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_SLOTS
- && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
- {
- vliw_insn vinsn;
+ /* 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. */
- vinsn_from_chars (&vinsn, fragP->fr_opcode);
- tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
+ if (get_frag_count () > 8000)
+ {
+ xtensa_create_trampoline_frag (TRUE);
+ clear_frag_count ();
+ unreachable_count = 0;
+ }
- if (vinsn.num_slots == 1
- && xtensa_opcode_is_branch (xtensa_default_isa,
- vinsn.slots[0].opcode) == 1
- && xg_get_single_size (vinsn.slots[0].opcode) == 2
- && is_narrow_branch_guaranteed_in_range (fragP,
- &vinsn.slots[0]))
- {
- fragP->fr_subtype = RELAX_SLOTS;
- fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
- fragP->tc_frag_data.is_aligning_branch = 1;
- }
- }
- }
- }
+ /* 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;
-/* A branch is typically widened only when its target is out of
- range. However, we would like to widen them to align a subsequent
- branch target when possible.
+static xtensa_insnbuf litpool_buf = NULL;
+static xtensa_insnbuf litpool_slotbuf = NULL;
- Because the branch relaxation code is so convoluted, the optimal solution
- (combining the two cases) is difficult to get right in all circumstances.
- We therefore go with an "almost as good" solution, where we only
- use for alignment narrow branches that definitely will not expand to a
- jump and a branch. These functions find and mark these cases. */
+#define TRAMPOLINE_FRAG_SIZE 3000
-/* The range in bytes of BNEZ.N and BEQZ.N. The target operand is encoded
- as PC + 4 + imm6, where imm6 is a 6-bit immediate ranging from 0 to 63.
- We start counting beginning with the frag after the 2-byte branch, so the
- maximum offset is (4 - 2) + 63 = 65. */
-#define MAX_IMMED6 65
+static struct trampoline_seg *
+find_trampoline_seg (asection *seg)
+{
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+ static struct trampoline_seg *mr;
-static offsetT unrelaxed_frag_max_size (fragS *);
+ if (mr && mr->seg == seg)
+ return mr;
-static bfd_boolean
-is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
-{
- const expressionS *exp = &tinsn->tok[1];
- symbolS *symbolP = exp->X_add_symbol;
- offsetT max_distance = exp->X_add_number;
- fragS *target_frag;
+ for ( ; ts; ts = ts->next)
+ {
+ if (ts->seg == seg)
+ {
+ mr = ts;
+ return ts;
+ }
+ }
- if (exp->X_op != O_symbol)
- return FALSE;
+ return NULL;
+}
- target_frag = symbol_get_frag (symbolP);
+static size_t xg_find_trampoline (const struct trampoline_index *idx,
+ addressT addr)
+{
+ size_t a = 0;
+ size_t b = idx->n_entries;
- max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
- if (is_branch_jmp_to_next (tinsn, fragP))
- return FALSE;
+ while (b - a > 1)
+ {
+ size_t c = (a + b) / 2;
- /* The branch doesn't branch over it's own frag,
+ if (idx->entry[c]->fr_address <= addr)
+ a = c;
+ else
+ b = c;
+ }
+ return a;
+}
+
+static void xg_add_trampoline_to_index (struct trampoline_index *idx,
+ fragS *fragP)
+{
+ if (idx->n_entries == idx->n_max)
+ {
+ idx->n_max = (idx->n_entries + 1) * 2;
+ idx->entry = xrealloc (idx->entry,
+ sizeof (*idx->entry) * idx->n_max);
+ }
+ idx->entry[idx->n_entries] = fragP;
+ ++idx->n_entries;
+}
+
+static void xg_remove_trampoline_from_index (struct trampoline_index *idx,
+ size_t i)
+{
+ gas_assert (i < idx->n_entries);
+ memmove (idx->entry + i, idx->entry + i + 1,
+ (idx->n_entries - i - 1) * sizeof (*idx->entry));
+ --idx->n_entries;
+}
+
+static void xg_add_trampoline_to_seg (struct trampoline_seg *ts,
+ fragS *fragP)
+{
+ xg_add_trampoline_to_index (&ts->index, fragP);
+}
+
+static void
+xtensa_create_trampoline_frag (bfd_boolean needs_jump_around)
+{
+ /* 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 = find_trampoline_seg (now_seg);
+ char *varP;
+ fragS *fragP;
+ int size = TRAMPOLINE_FRAG_SIZE;
+
+ if (ts == NULL)
+ {
+ ts = XCNEW(struct trampoline_seg);
+ 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);
+ }
+ fragP->tc_frag_data.needs_jump_around = needs_jump_around;
+ xg_add_trampoline_to_seg (ts, fragP);
+}
+
+static bfd_boolean xg_is_trampoline_frag_full (const fragS *fragP)
+{
+ return fragP->fr_var < 3;
+}
+
+static int xg_order_trampoline_chain_entry (const void *a, const void *b)
+{
+ const struct trampoline_chain_entry *pa = a;
+ const struct trampoline_chain_entry *pb = b;
+
+ if (pa->sym != pb->sym)
+ {
+ valueT aval = S_GET_VALUE (pa->sym);
+ valueT bval = S_GET_VALUE (pb->sym);
+
+ if (aval != bval)
+ return aval < bval ? -1 : 1;
+ }
+ if (pa->offset != pb->offset)
+ return pa->offset < pb->offset ? -1 : 1;
+ return 0;
+}
+
+static void xg_sort_trampoline_chain (struct trampoline_chain *tc)
+{
+ qsort (tc->entry, tc->n_entries, sizeof (*tc->entry),
+ xg_order_trampoline_chain_entry);
+ tc->needs_sorting = FALSE;
+}
+
+/* Find entry index in the given chain with maximal address <= source. */
+static size_t xg_find_chain_entry (struct trampoline_chain *tc,
+ addressT source)
+{
+ size_t a = 0;
+ size_t b = tc->n_entries;
+
+ if (tc->needs_sorting)
+ xg_sort_trampoline_chain (tc);
+
+ while (b - a > 1)
+ {
+ size_t c = (a + b) / 2;
+ struct trampoline_chain_entry *e = tc->entry + c;
+
+ if (S_GET_VALUE(e->sym) + e->offset <= source)
+ a = c;
+ else
+ b = c;
+ }
+ return a;
+}
+
+/* Find the best jump target for the source in the given trampoline chain.
+ The best jump target is the one that results in the shortest path to the
+ final target, it's the location of the jump closest to the final target,
+ but within the J_RANGE - J_MARGIN from the source. */
+static struct trampoline_chain_entry *
+xg_get_best_chain_entry (struct trampoline_chain *tc, addressT source)
+{
+ addressT target = S_GET_VALUE(tc->target.sym) + tc->target.offset;
+ size_t i = xg_find_chain_entry (tc, source);
+ struct trampoline_chain_entry *e = tc->entry + i;
+ int step = target < source ? -1 : 1;
+ addressT chained_target;
+ offsetT off;
+
+ if (target > source &&
+ S_GET_VALUE(e->sym) + e->offset <= source &&
+ i + 1 < tc->n_entries)
+ ++i;
+
+ while (i + step < tc->n_entries)
+ {
+ struct trampoline_chain_entry *next = tc->entry + i + step;
+
+ chained_target = S_GET_VALUE(next->sym) + next->offset;
+ off = source - chained_target;
+
+ if (labs (off) >= J_RANGE - J_MARGIN)
+ break;
+
+ i += step;
+ }
+
+ e = tc->entry + i;
+ chained_target = S_GET_VALUE(e->sym) + e->offset;
+ off = source - chained_target;
+
+ if (labs (off) < J_MARGIN ||
+ labs (off) >= J_RANGE - J_MARGIN)
+ return &tc->target;
+ return tc->entry + i;
+}
+
+static int xg_order_trampoline_chain (const void *a, const void *b)
+{
+ const struct trampoline_chain *_pa = a;
+ const struct trampoline_chain *_pb = b;
+ const struct trampoline_chain_entry *pa = &_pa->target;
+ const struct trampoline_chain_entry *pb = &_pb->target;
+ symbolS *s1 = pa->sym;
+ symbolS *s2 = pb->sym;
+
+ if (s1 != s2)
+ {
+ symbolS *tmp = symbol_symbolS (s1);
+ if (tmp)
+ s1 = tmp;
+
+ tmp = symbol_symbolS (s2);
+ if (tmp)
+ s2 = tmp;
+
+ if (s1 != s2)
+ return s1 < s2 ? -1 : 1;
+ }
+
+ if (pa->offset != pb->offset)
+ return pa->offset < pb->offset ? -1 : 1;
+ return 0;
+}
+
+static struct trampoline_chain *
+xg_get_trampoline_chain (struct trampoline_seg *ts,
+ symbolS *sym,
+ addressT offset)
+{
+ struct trampoline_chain_index *idx = &ts->chain_index;
+ struct trampoline_chain c;
+
+ if (idx->needs_sorting)
+ {
+ qsort (idx->entry, idx->n_entries, sizeof (*idx->entry),
+ xg_order_trampoline_chain);
+ idx->needs_sorting = FALSE;
+ }
+ c.target.sym = sym;
+ c.target.offset = offset;
+ return bsearch (&c, idx->entry, idx->n_entries,
+ sizeof (struct trampoline_chain),
+ xg_order_trampoline_chain);
+}
+
+/* Find trampoline chain in the given trampoline segment that is going
+ to the *sym + *offset. If found, replace *sym and *offset with the
+ best jump target in that chain. */
+static struct trampoline_chain *
+xg_find_best_eq_target (struct trampoline_seg *ts,
+ addressT source, symbolS **sym,
+ addressT *offset)
+{
+ struct trampoline_chain *tc = xg_get_trampoline_chain (ts, *sym, *offset);
+
+ if (tc)
+ {
+ struct trampoline_chain_entry *e = xg_get_best_chain_entry (tc, source);
+
+ *sym = e->sym;
+ *offset = e->offset;
+ }
+ return tc;
+}
+
+static void xg_add_location_to_chain (struct trampoline_chain *tc,
+ symbolS *sym, addressT offset)
+{
+ struct trampoline_chain_entry *e;
+
+ if (tc->n_entries == tc->n_max)
+ {
+ tc->n_max = (tc->n_max + 1) * 2;
+ tc->entry = xrealloc (tc->entry, sizeof (*tc->entry) * tc->n_max);
+ }
+ e = tc->entry + tc->n_entries;
+ e->sym = sym;
+ e->offset = offset;
+ ++tc->n_entries;
+ tc->needs_sorting = TRUE;
+}
+
+static struct trampoline_chain *
+xg_create_trampoline_chain (struct trampoline_seg *ts,
+ symbolS *sym, addressT offset)
+{
+ struct trampoline_chain_index *idx = &ts->chain_index;
+ struct trampoline_chain *tc;
+
+ if (idx->n_entries == idx->n_max)
+ {
+ idx->n_max = (idx->n_max + 1) * 2;
+ idx->entry = xrealloc (idx->entry,
+ sizeof (*idx->entry) * idx->n_max);
+ }
+
+ tc = idx->entry + idx->n_entries;
+ tc->target.sym = sym;
+ tc->target.offset = offset;
+ tc->entry = NULL;
+ tc->n_entries = 0;
+ tc->n_max = 0;
+ xg_add_location_to_chain (tc, sym, offset);
+
+ ++idx->n_entries;
+ idx->needs_sorting = TRUE;
+
+ return tc;
+}
+
+void dump_trampolines (void);
+
+void
+dump_trampolines (void)
+{
+ struct trampoline_seg *ts = trampoline_seg_list.next;
+
+ for ( ; ts; ts = ts->next)
+ {
+ size_t i;
+ asection *seg = ts->seg;
+
+ if (seg == NULL)
+ continue;
+ fprintf(stderr, "SECTION %s\n", seg->name);
+
+ for (i = 0; i < ts->index.n_entries; ++i)
+ {
+ fragS *tf = ts->index.entry[i];
+
+ fprintf(stderr, " 0x%08x: fix=%d, jump_around=%s\n",
+ (int)tf->fr_address, (int)tf->fr_fix,
+ tf->tc_frag_data.needs_jump_around ? "T" : "F");
+ }
+ }
+}
+
+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 = XCNEW (struct litpool_seg);
+ 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;
+ /* Put candidate literal pool at the beginning of every section,
+ so that even when section starts with literal load there's a
+ literal pool available. */
+ lps->frag_count = auto_litpool_limit;
+ }
+
+ 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 = XNEW (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;
+ lpf->literal_count = 0;
+
+ lps->frag_count = 0;
+}
+
+static void
+xtensa_cleanup_align_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if ((fragP->fr_type == rs_align
+ || fragP->fr_type == rs_align_code
+ || (fragP->fr_type == rs_machine_dependent
+ && (fragP->fr_subtype == RELAX_DESIRE_ALIGN
+ || fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
+ && fragP->fr_fix == 0)
+ {
+ fragS *next = fragP->fr_next;
+
+ while (next
+ && next->fr_fix == 0
+ && next->fr_type == rs_machine_dependent
+ && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+ {
+ frag_wane (next);
+ next = next->fr_next;
+ }
+ }
+ /* If we don't widen branch targets, then they
+ will be easier to align. */
+ if (fragP->tc_frag_data.is_branch_target
+ && fragP->fr_opcode == fragP->fr_literal
+ && fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ frag_wane (fragP);
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_UNREACHABLE)
+ fragP->tc_frag_data.is_unreachable = TRUE;
+ }
+ }
+}
+
+
+/* Re-process all of the fragments looking to convert all of the
+ RELAX_DESIRE_ALIGN_IF_TARGET fragments. If there is a branch
+ target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
+ Otherwise, convert to a .fill 0. */
+
+static void
+xtensa_fix_target_frags (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ /* When this routine is called, all of the subsections are still intact
+ so we walk over subsections instead of sections. */
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+ {
+ if (next_frag_is_branch_target (fragP))
+ fragP->fr_subtype = RELAX_DESIRE_ALIGN;
+ else
+ frag_wane (fragP);
+ }
+ }
+ }
+}
+
+
+static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
+
+static void
+xtensa_mark_narrow_branches (void)
+{
+ frchainS *frchP;
+ asection *s;
+
+ for (s = stdoutput->sections; s; s = s->next)
+ for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+ {
+ vliw_insn vinsn;
+
+ vinsn_from_chars (&vinsn, fragP->fr_opcode);
+ tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
+
+ if (vinsn.num_slots == 1
+ && xtensa_opcode_is_branch (xtensa_default_isa,
+ vinsn.slots[0].opcode) == 1
+ && xg_get_single_size (vinsn.slots[0].opcode) == 2
+ && is_narrow_branch_guaranteed_in_range (fragP,
+ &vinsn.slots[0]))
+ {
+ fragP->fr_subtype = RELAX_SLOTS;
+ fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
+ fragP->tc_frag_data.is_aligning_branch = 1;
+ }
+ }
+ }
+ }
+}
+
+
+/* A branch is typically widened only when its target is out of
+ range. However, we would like to widen them to align a subsequent
+ branch target when possible.
+
+ Because the branch relaxation code is so convoluted, the optimal solution
+ (combining the two cases) is difficult to get right in all circumstances.
+ We therefore go with an "almost as good" solution, where we only
+ use for alignment narrow branches that definitely will not expand to a
+ jump and a branch. These functions find and mark these cases. */
+
+/* The range in bytes of BNEZ.N and BEQZ.N. The target operand is encoded
+ as PC + 4 + imm6, where imm6 is a 6-bit immediate ranging from 0 to 63.
+ We start counting beginning with the frag after the 2-byte branch, so the
+ maximum offset is (4 - 2) + 63 = 65. */
+#define MAX_IMMED6 65
+
+static offsetT unrelaxed_frag_max_size (fragS *);
+
+static bfd_boolean
+is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
+{
+ const expressionS *exp = &tinsn->tok[1];
+ symbolS *symbolP = exp->X_add_symbol;
+ offsetT max_distance = exp->X_add_number;
+ fragS *target_frag;
+
+ if (exp->X_op != O_symbol)
+ return FALSE;
+
+ target_frag = symbol_get_frag (symbolP);
+
+ max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
+ if (is_branch_jmp_to_next (tinsn, fragP))
+ return FALSE;
+
+ /* The branch doesn't branch over it's own frag,
but over the subsequent ones. */
fragP = fragP->fr_next;
while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
/* 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
{
if (loop_frag->fr_type == rs_machine_dependent
&& (loop_frag->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
- || loop_frag->fr_subtype
+ || loop_frag->fr_subtype
== RELAX_CHECK_ALIGN_NEXT_OPCODE))
targ_frag = loop_frag;
else
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 *exp = symbol_get_value_expression (expr_sym);
{
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 *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);
walk = start->tc_frag_data.no_transform_end;
else
walk = start;
- do
+ do
{
walk->tc_frag_data.is_no_transform = 1;
walk = walk->fr_next;
static xtensa_insnbuf insnbuf = NULL;
static xtensa_insnbuf slotbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
- int offset = 0;
+ unsigned int offset = 0;
int slot;
bfd_boolean branch_seen = FALSE;
xtensa_isa isa = xtensa_default_isa;
static xtensa_insnbuf insnbuf = NULL;
int insn_count = 0;
- int offset = 0;
+ unsigned int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return insn_count;
{
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
- int offset = 0;
+ unsigned int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return FALSE;
static void
xtensa_sanity_check (void)
{
- char *file_name;
+ const char *file_name;
unsigned line;
frchainS *frchP;
asection *s;
- as_where (&file_name, &line);
+ file_name = as_where (&line);
for (s = stdoutput->sections; s; s = s->next)
for (frchP = seg_info (s)->frchainP; frchP; frchP = frchP->frch_next)
{
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;
int sz;
info_sec = subseg_new (".xtensa.info", 0);
- bfd_set_section_flags (stdoutput, info_sec, SEC_HAS_CONTENTS | SEC_READONLY);
+ bfd_set_section_flags (info_sec, SEC_HAS_CONTENTS | SEC_READONLY);
- data = xmalloc (100);
+ data = XNEWVEC (char, 100);
sprintf (data, "USE_ABSOLUTE_LITERALS=%d\nABI=%d\n",
XSHAL_USE_ABSOLUTE_LITERALS, XSHAL_ABI);
sz = strlen (data) + 1;
is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
gas_assert (is_loop);
- /* If the loop has been expanded then the LOOP instruction
- could be at an offset from this fragment. */
- if (loop_frag->tc_frag_data.slot_subtypes[0] != RELAX_IMMED)
- loop_insn_offset = get_expanded_loop_offset (loop_opcode);
+ /* If the loop has been expanded then the LOOP instruction
+ could be at an offset from this fragment. */
+ if (loop_frag->tc_frag_data.slot_subtypes[0] != RELAX_IMMED)
+ loop_insn_offset = get_expanded_loop_offset (loop_opcode);
+
+ /* In an ideal world, which is what we are shooting for here,
+ we wouldn't need to use any NOPs immediately prior to the
+ LOOP instruction. If this approach fails, relax_frag_loop_align
+ will call get_noop_aligned_address. */
+ target_address =
+ address + loop_insn_offset + xg_get_single_size (loop_opcode);
+ align_power = get_text_align_power (target_size);
+ opt_diff = get_text_align_fill_size (target_address, align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = xtensa_fetch_width
+ - ((target_address + opt_diff) % xtensa_fetch_width)
+ - target_size + opt_diff;
+ gas_assert (*max_diff >= opt_diff);
+ return opt_diff;
+
+ default:
+ break;
+ }
+ gas_assert (0);
+ return 0;
+}
+
+\f
+/* md_relax_frag Hook and Helper Functions. */
+
+static long relax_frag_loop_align (fragS *, long);
+static long relax_frag_for_align (fragS *, long);
+static long relax_frag_immed
+ (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
+
+/* Get projected address for the first fulcrum on a path from source to
+ target. */
+static addressT xg_get_fulcrum (addressT source, addressT target)
+{
+ offsetT delta = target - source;
+ int n;
+
+ n = (labs (delta) + J_RANGE - J_MARGIN - 1) / (J_RANGE - J_MARGIN);
+ return source + delta / n;
+}
+
+/* Given trampoline index, source and target of a jump find the best
+ candidate trampoline for the first fulcrum. The best trampoline is
+ the one in the reach of "j' instruction from the source, closest to
+ the projected fulcrum address, and preferrably w/o a jump around or
+ with already initialized jump around. */
+static size_t xg_find_best_trampoline (struct trampoline_index *idx,
+ addressT source, addressT target)
+{
+ addressT fulcrum = xg_get_fulcrum (source, target);
+ size_t dist = 0;
+ size_t best = -1;
+ size_t base_tr = xg_find_trampoline (idx, fulcrum);
+ int checked = 1;
+
+ /* Check trampoline frags around the base_tr to find the best. */
+ for (dist = 0; checked; ++dist)
+ {
+ int i;
+ size_t tr = base_tr - dist;
+
+ checked = 0;
+
+ /* Trampolines are checked in the following order:
+ base_tr, base_tr + 1, base_tr - 1, base_tr + 2, base_tr - 2 */
+ for (i = 0; i < 2; ++i, tr = base_tr + dist + 1)
+ if (tr < idx->n_entries)
+ {
+ fragS *trampoline_frag = idx->entry[tr];
+ offsetT off;
+
+ /* Don't check trampolines outside source - target interval. */
+ if ((trampoline_frag->fr_address < source &&
+ trampoline_frag->fr_address < target) ||
+ (trampoline_frag->fr_address > source &&
+ trampoline_frag->fr_address > target))
+ continue;
+
+ /* Don't choose trampoline that contains the source. */
+ if (source >= trampoline_frag->fr_address
+ && source <= trampoline_frag->fr_address +
+ trampoline_frag->fr_fix)
+ continue;
+
+ off = trampoline_frag->fr_address - fulcrum;
+ /* Stop if some trampoline is found and the search is more than
+ J_RANGE / 4 from the projected fulcrum. A trampoline w/o jump
+ around is nice, but it shouldn't have much overhead. */
+ if (best < idx->n_entries && labs (off) > J_RANGE / 4)
+ return best;
+
+ off = trampoline_frag->fr_address - source;
+ if (labs (off) < J_RANGE - J_MARGIN)
+ {
+ ++checked;
+ /* Stop if a trampoline w/o jump around is found or initialized
+ trampoline with jump around is found. */
+ if (!trampoline_frag->tc_frag_data.needs_jump_around ||
+ trampoline_frag->fr_fix)
+ return tr;
+ else if (best >= idx->n_entries)
+ best = tr;
+ }
+ }
+ }
+
+ if (best < idx->n_entries)
+ return best;
+ else
+ as_fatal (_("cannot find suitable trampoline"));
+}
+
+static fixS *xg_relax_fixup (struct trampoline_index *idx, fixS *fixP)
+{
+ symbolS *s = fixP->fx_addsy;
+ addressT source = fixP->fx_frag->fr_address;
+ addressT target = S_GET_VALUE (s) + fixP->fx_offset;
+ size_t tr = xg_find_best_trampoline (idx, source, target);
+ fragS *trampoline_frag = idx->entry[tr];
+ fixS *newfixP;
+
+ init_trampoline_frag (trampoline_frag);
+ newfixP = xg_append_jump (trampoline_frag,
+ fixP->fx_addsy, fixP->fx_offset);
+
+ /* Adjust the fixup for the original "j" instruction to
+ point to the newly added jump. */
+ fixP->fx_addsy = trampoline_frag->fr_symbol;
+ fixP->fx_offset = trampoline_frag->fr_fix - 3;
+ fixP->tc_fix_data.X_add_symbol = trampoline_frag->fr_symbol;
+ fixP->tc_fix_data.X_add_number = trampoline_frag->fr_fix - 3;
+
+ trampoline_frag->tc_frag_data.relax_seen = FALSE;
+
+ if (xg_is_trampoline_frag_full (trampoline_frag))
+ xg_remove_trampoline_from_index (idx, tr);
+
+ return newfixP;
+}
+
+static bfd_boolean xg_is_relaxable_fixup (fixS *fixP)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ addressT addr = fixP->fx_frag->fr_address;
+ addressT target;
+ offsetT delta;
+ symbolS *s = fixP->fx_addsy;
+ int slot;
+ xtensa_format fmt;
+ xtensa_opcode opcode;
+
+ if (fixP->fx_r_type < BFD_RELOC_XTENSA_SLOT0_OP ||
+ fixP->fx_r_type > BFD_RELOC_XTENSA_SLOT14_OP)
+ return FALSE;
+
+ target = S_GET_VALUE (s) + fixP->fx_offset;
+ delta = target - addr;
+
+ if (labs (delta) < J_RANGE - J_MARGIN)
+ return FALSE;
+
+ xtensa_insnbuf_from_chars (isa, trampoline_buf,
+ (unsigned char *) fixP->fx_frag->fr_literal +
+ fixP->fx_where, 0);
+ fmt = xtensa_format_decode (isa, trampoline_buf);
+ gas_assert (fmt != XTENSA_UNDEFINED);
+ slot = fixP->tc_fix_data.slot;
+ xtensa_format_get_slot (isa, fmt, slot, trampoline_buf, trampoline_slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, trampoline_slotbuf);
+ return opcode == xtensa_j_opcode;
+}
- /* In an ideal world, which is what we are shooting for here,
- we wouldn't need to use any NOPs immediately prior to the
- LOOP instruction. If this approach fails, relax_frag_loop_align
- will call get_noop_aligned_address. */
- target_address =
- address + loop_insn_offset + xg_get_single_size (loop_opcode);
- align_power = get_text_align_power (target_size);
- opt_diff = get_text_align_fill_size (target_address, align_power,
- target_size, FALSE, FALSE);
+static void xg_relax_fixups (struct trampoline_seg *ts)
+{
+ struct trampoline_index *idx = &ts->index;
+ segment_info_type *seginfo = seg_info (now_seg);
+ fixS *fx;
- *max_diff = xtensa_fetch_width
- - ((target_address + opt_diff) % xtensa_fetch_width)
- - target_size + opt_diff;
- gas_assert (*max_diff >= opt_diff);
- return opt_diff;
+ for (fx = seginfo->fix_root; fx; fx = fx->fx_next)
+ {
+ fixS *fixP = fx;
+ struct trampoline_chain *tc = NULL;
- default:
- break;
+ if (xg_is_relaxable_fixup (fixP))
+ {
+ tc = xg_find_best_eq_target (ts, fixP->fx_frag->fr_address,
+ &fixP->fx_addsy, &fixP->fx_offset);
+ if (!tc)
+ tc = xg_create_trampoline_chain (ts, fixP->fx_addsy,
+ fixP->fx_offset);
+ gas_assert (tc);
+ }
+
+ while (xg_is_relaxable_fixup (fixP))
+ {
+ fixP = xg_relax_fixup (idx, fixP);
+ xg_add_location_to_chain (tc, fixP->fx_frag->fr_symbol,
+ fixP->fx_where);
+ }
}
- gas_assert (0);
- return 0;
}
-\f
-/* md_relax_frag Hook and Helper Functions. */
-
-static long relax_frag_loop_align (fragS *, long);
-static long relax_frag_for_align (fragS *, long);
-static long relax_frag_immed
- (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
+/* Given a trampoline frag relax all jumps that might want to use this
+ trampoline. Only do real work once per relaxation cycle, when
+ xg_relax_trampoline is called for the first trampoline in the now_seg.
+ Don't use stretch, don't update new_stretch: place fulcrums with a
+ slack to tolerate code movement. In the worst case if a jump between
+ two trampolines wouldn't reach the next relaxation pass will fix it. */
+static void xg_relax_trampoline (fragS *fragP, long stretch ATTRIBUTE_UNUSED,
+ long *new_stretch ATTRIBUTE_UNUSED)
+{
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ if (ts->index.n_entries && ts->index.entry[0] == fragP)
+ xg_relax_fixups (ts);
+}
/* Return the number of bytes added to this fragment, given that the
input has been stretched already by "stretch". */
xtensa_isa isa = xtensa_default_isa;
int unreported = fragP->tc_frag_data.unreported_expansion;
long new_stretch = 0;
- char *file_name;
+ const char *file_name;
unsigned line;
int lit_size;
static xtensa_insnbuf vbuf = NULL;
int slot, num_slots;
xtensa_format fmt;
- as_where (&file_name, &line);
+ file_name = as_where (&line);
new_logical_line (fragP->fr_file, fragP->fr_line);
fragP->tc_frag_data.unreported_expansion = 0;
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)
+ xg_relax_trampoline (fragP, stretch, &new_stretch);
+ break;
+
default:
as_bad (_("bad relaxation state"));
}
(*widens)++;
break;
}
- address += total_frag_text_expansion (fragP);;
+ address += total_frag_text_expansion (fragP);
break;
case RELAX_IMMED:
int extra_bytes;
int bytes_short = desired_diff - num_widens;
- gas_assert (desired_diff >= 0
+ gas_assert (desired_diff >= 0
&& desired_diff < (signed) xtensa_fetch_width);
if (desired_diff == 0)
return 0;
}
+static fragS *
+xg_find_best_trampoline_for_tinsn (TInsn *tinsn, fragS *fragP)
+{
+ symbolS *sym = tinsn->tok[0].X_add_symbol;
+ addressT source = fragP->fr_address;
+ addressT target = S_GET_VALUE (sym) + tinsn->tok[0].X_add_number;
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ size_t i;
+
+ if (!ts || !ts->index.n_entries)
+ return NULL;
+
+ i = xg_find_best_trampoline (&ts->index, source, target);
+
+ return ts->index.entry[i];
+}
+
+
+/* Append jump to sym + offset to the end of the trampoline frag fragP.
+ Adjust fragP's jump around if it's present. Adjust fragP's fr_fix/fr_var
+ and finish the frag if it's full (but don't remove it from the trampoline
+ frag index). Return fixup for the newly created jump. */
+static fixS *xg_append_jump (fragS *fragP, symbolS *sym, offsetT offset)
+{
+ fixS *fixP;
+ TInsn insn;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+
+ gas_assert (fragP->fr_var >= 3);
+ tinsn_init (&insn);
+ insn.insn_type = ITYPE_INSN;
+ insn.opcode = xtensa_j_opcode;
+ insn.ntok = 1;
+ set_expr_symbol_offset (&insn.tok[0], sym, 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 + fragP->fr_fix, 3);
+ fixP = fix_new (fragP, fragP->fr_fix, 3, sym, offset, TRUE,
+ BFD_RELOC_XTENSA_SLOT0_OP);
+ fixP->tc_fix_data.slot = 0;
+
+ fragP->fr_fix += 3;
+ fragP->fr_var -= 3;
+
+ /* Adjust the jump around this trampoline (if present). */
+ if (fragP->tc_frag_data.jump_around_fix)
+ fragP->tc_frag_data.jump_around_fix->fx_offset += 3;
+
+ /* Do we have room for more? */
+ if (xg_is_trampoline_frag_full (fragP))
+ {
+ frag_wane (fragP);
+ fragP->fr_subtype = 0;
+ }
+
+ return fixP;
+}
+
+
+static int
+init_trampoline_frag (fragS *fp)
+{
+ 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 (fp->tc_frag_data.needs_jump_around)
+ {
+ fp->tc_frag_data.jump_around_fix = xg_append_jump (fp, lsym, 3);
+ growth = 3;
+ }
+ }
+ return growth;
+}
+
+static int
+xg_get_single_symbol_slot (fragS *fragP)
+{
+ int i;
+ int slot = -1;
+
+ for (i = 0; i < MAX_SLOTS; ++i)
+ if (fragP->tc_frag_data.slot_symbols[i])
+ {
+ gas_assert (slot == -1);
+ slot = i;
+ }
+
+ gas_assert (slot >= 0 && slot < MAX_SLOTS);
+
+ return slot;
+}
+
+static fixS *
+add_jump_to_trampoline (fragS *tramp, fragS *origfrag)
+{
+ int slot = xg_get_single_symbol_slot (origfrag);
+ fixS *fixP;
+
+ /* Assemble a jump to the target label in the trampoline frag. */
+ fixP = xg_append_jump (tramp,
+ origfrag->tc_frag_data.slot_symbols[slot],
+ origfrag->tc_frag_data.slot_offsets[slot]);
+
+ /* Modify the original j to point here. */
+ origfrag->tc_frag_data.slot_symbols[slot] = tramp->fr_symbol;
+ origfrag->tc_frag_data.slot_offsets[slot] = tramp->fr_fix - 3;
+
+ /* If trampoline is full, remove it from the list. */
+ if (xg_is_trampoline_frag_full (tramp))
+ {
+ struct trampoline_seg *ts = find_trampoline_seg (now_seg);
+ size_t tr = xg_find_trampoline (&ts->index, tramp->fr_address);
+
+ gas_assert (ts->index.entry[tr] == tramp);
+ xg_remove_trampoline_from_index (&ts->index, tr);
+ }
+
+ return fixP;
+}
+
+
static long
relax_frag_immed (segT segP,
fragS *fragP,
/* 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);
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;
}
}
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];
+ struct trampoline_seg *ts = find_trampoline_seg (segP);
+ struct trampoline_chain *tc = NULL;
+
+ if (ts &&
+ !xg_symbolic_immeds_fit (jinsn, segP, fragP, fragP->fr_offset,
+ total_text_diff))
+ {
+ int s = xg_get_single_symbol_slot (fragP);
+ addressT offset = fragP->tc_frag_data.slot_offsets[s];
+
+ tc = xg_find_best_eq_target (ts, fragP->fr_address,
+ &fragP->tc_frag_data.slot_symbols[s],
+ &offset);
+
+ if (!tc)
+ tc = xg_create_trampoline_chain (ts,
+ fragP->tc_frag_data.slot_symbols[s],
+ offset);
+ fragP->tc_frag_data.slot_offsets[s] = offset;
+ tinsn_immed_from_frag (jinsn, fragP, s);
+ }
+
+ if (!xg_symbolic_immeds_fit (jinsn, segP, fragP, fragP->fr_offset,
+ total_text_diff))
+ {
+ fragS *tf = xg_find_best_trampoline_for_tinsn (jinsn, fragP);
+
+ if (tf)
+ {
+ fixS *fixP;
+
+ this_text_diff += init_trampoline_frag (tf) + 3;
+ fixP = add_jump_to_trampoline (tf, fragP);
+ xg_add_location_to_chain (tc, fixP->fx_frag->fr_symbol,
+ fixP->fx_where);
+ fragP->tc_frag_data.relax_seen = FALSE;
+ }
+ 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;
}
int slot;
int num_slots;
xtensa_format fmt;
- char *file_name;
+ const char *file_name;
unsigned line;
- as_where (&file_name, &line);
+ file_name = as_where (&line);
new_logical_line (fragp->fr_file, fragp->fr_line);
switch (fragp->fr_subtype)
else
as_bad (_("invalid relaxation fragment result"));
break;
+
+ case RELAX_TRAMPOLINE:
+ break;
}
fragp->fr_var = 0;
static fixS *fix_new_exp_in_seg
(segT, subsegT, fragS *, int, int, expressionS *, int,
bfd_reloc_code_real_type);
-static void convert_frag_immed_finish_loop (segT, fragS *, TInsn *);
static void
convert_frag_immed (segT segP,
}
}
- if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
- convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
-
if (expanded && is_direct_call_opcode (orig_tinsn.opcode))
{
/* Add an expansion note on the expanded instruction. */
}
-/* Relax a loop instruction so that it can span loop >256 bytes.
-
- loop as, .L1
- .L0:
- rsr as, LEND
- wsr as, LBEG
- addi as, as, lo8 (label-.L1)
- addmi as, as, mid8 (label-.L1)
- wsr as, LEND
- isync
- rsr as, LCOUNT
- addi as, as, 1
- .L1:
- <<body>>
- label:
-*/
-
-static void
-convert_frag_immed_finish_loop (segT segP, fragS *fragP, TInsn *tinsn)
-{
- TInsn loop_insn;
- TInsn addi_insn;
- TInsn addmi_insn;
- unsigned long target;
- static xtensa_insnbuf insnbuf = NULL;
- unsigned int loop_length, loop_length_hi, loop_length_lo;
- xtensa_isa isa = xtensa_default_isa;
- addressT loop_offset;
- addressT addi_offset = 9;
- addressT addmi_offset = 12;
- fragS *next_fragP;
- int target_count;
-
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
-
- /* Get the loop offset. */
- loop_offset = get_expanded_loop_offset (tinsn->opcode);
-
- /* Validate that there really is a LOOP at the loop_offset. Because
- loops are not bundleable, we can assume that the instruction will be
- in slot 0. */
- tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
- tinsn_immed_from_frag (&loop_insn, fragP, 0);
-
- gas_assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
- addi_offset += loop_offset;
- addmi_offset += loop_offset;
-
- 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;
- gas_assert (S_GET_SEGMENT (sym) == segP
- || S_GET_SEGMENT (sym) == absolute_section);
- target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
- }
- else
- {
- as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
- target = 0;
- }
-
- loop_length = target - (fragP->fr_address + fragP->fr_fix);
- loop_length_hi = loop_length & ~0x0ff;
- loop_length_lo = loop_length & 0x0ff;
- if (loop_length_lo >= 128)
- {
- loop_length_lo -= 256;
- loop_length_hi += 256;
- }
-
- /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
- 32512. If the loop is larger than that, then we just fail. */
- if (loop_length_hi > 32512)
- as_bad_where (fragP->fr_file, fragP->fr_line,
- _("loop too long for LOOP instruction"));
-
- tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
- gas_assert (addi_insn.opcode == xtensa_addi_opcode);
-
- tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
- gas_assert (addmi_insn.opcode == xtensa_addmi_opcode);
-
- set_expr_const (&addi_insn.tok[2], loop_length_lo);
- tinsn_to_insnbuf (&addi_insn, insnbuf);
-
- fragP->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars
- (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addi_offset, 0);
-
- set_expr_const (&addmi_insn.tok[2], loop_length_hi);
- tinsn_to_insnbuf (&addmi_insn, insnbuf);
- xtensa_insnbuf_to_chars
- (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addmi_offset, 0);
-
- /* Walk through all of the frags from here to the loop end
- and mark them as no_transform to keep them from being modified
- by the linker. If we ever have a relocation for the
- addi/addmi of the difference of two symbols we can remove this. */
-
- target_count = 0;
- for (next_fragP = fragP; next_fragP != NULL;
- next_fragP = next_fragP->fr_next)
- {
- next_fragP->tc_frag_data.is_no_transform = TRUE;
- if (next_fragP->tc_frag_data.is_loop_target)
- target_count++;
- if (target_count == 2)
- break;
- }
-}
-
\f
/* A map that keeps information on a per-subsegment basis. This is
maintained during initial assembly, but is invalid once the
static subseg_map *
add_subseg_info (segT seg, subsegT subseg)
{
- subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
+ subseg_map *subseg_e = XNEW (subseg_map);
memset (subseg_e, 0, sizeof (subseg_map));
subseg_e->seg = seg;
subseg_e->subseg = subseg;
static void mark_literal_frags (seg_list *);
+static void
+xg_promote_candidate_litpool (struct litpool_seg *lps,
+ struct litpool_frag *lp)
+{
+ fragS *poolbeg;
+ fragS *poolend;
+ symbolS *lsym;
+ char label[10 + 2 * sizeof (fragS *)];
+
+ 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. */
+}
+
+static struct litpool_frag *xg_find_litpool (struct litpool_seg *lps,
+ struct litpool_frag *lpf,
+ addressT addr)
+{
+ struct litpool_frag *lp = lpf->prev;
+
+ gas_assert (lp->fragP);
+
+ 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 &&
+ lp->literal_count < MAX_POOL_LITERALS)
+ {
+ /* Found a good one. */
+ break;
+ }
+ else if (lp->prev->fragP &&
+ addr - lp->prev->addr > 128 * 1024 &&
+ lp->prev->literal_count < MAX_POOL_LITERALS)
+ {
+ /* 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. */
+ lp = lpf->prev;
+ break;
+ }
+ }
+ }
+
+ if (lp->literal_count >= MAX_POOL_LITERALS)
+ {
+ lp = lpf->prev;
+ while (lp && lp->fragP && lp->literal_count >= MAX_POOL_LITERALS)
+ {
+ lp = lp->prev;
+ }
+ gas_assert (lp);
+ }
+
+ gas_assert (lp && lp->fragP && lp->literal_count < MAX_POOL_LITERALS);
+ ++lp->literal_count;
+
+ /* Convert candidate and add the jump around. */
+ if (lp->fragP->fr_subtype == RELAX_LITERAL_POOL_CANDIDATE_BEGIN)
+ xg_promote_candidate_litpool (lps, lp);
+
+ return lp;
+}
+
+static bfd_boolean xtensa_is_init_fini (segT seg)
+{
+ if (!seg)
+ return 0;
+ return strcmp (segment_name (seg), INIT_SECTION_NAME) == 0
+ || strcmp (segment_name (seg), FINI_SECTION_NAME) == 0;
+}
+
+static void
+xtensa_assign_litpool_addresses (void)
+{
+ struct litpool_seg *lps;
+
+ 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;
+
+ if (xtensa_is_init_fini (lps->seg))
+ continue;
+
+ 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++)
+ {
+ fragS *litfrag = fragP->tc_frag_data.literal_frags[slot];
+
+ if (litfrag
+ && litfrag->tc_frag_data.is_literal
+ && !litfrag->tc_frag_data.literal_frag)
+ {
+ /* L32R referring .literal or generated as a result
+ of relaxation. Point its literal to the nearest
+ litpool preferring non-"candidate" positions to
+ avoid the jump-around. */
+
+ struct litpool_frag *lp;
+
+ lp = xg_find_litpool (lps, lpf, addr);
+ /* 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;
+ }
+ }
+ }
+}
+
static void
xtensa_move_literals (void)
{
segT dest_seg;
fixS *fix, *next_fix, **fix_splice;
sym_list *lit;
+ 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. */
+
+ xtensa_assign_litpool_addresses ();
+
+ /* Walk through the literal segments. */
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)
{
gas_assert (search_frag->fr_fix == 0
|| search_frag->fr_type == rs_align);
search_frag = search_frag->fr_next;
}
+ if (!search_frag)
+ 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);
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;
frchain_to = literal_pool->tc_frag_data.lit_frchain;
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 alignment 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;
static void
xtensa_switch_to_non_abs_literal_fragment (emit_state *result)
{
- static bfd_boolean recursive = FALSE;
fragS *pool_location = get_literal_pool_location (now_seg);
segT lit_seg;
- bfd_boolean is_init =
- (now_seg && !strcmp (segment_name (now_seg), INIT_SECTION_NAME));
- bfd_boolean is_fini =
- (now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME));
+ bfd_boolean is_init_fini = xtensa_is_init_fini (now_seg);
if (pool_location == NULL
&& !use_literal_section
- && !recursive
- && !is_init && ! is_fini)
+ && !is_init_fini)
{
- 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
- switch_to_literal_fragment. But literal sections don't have
- literal pools, so their location is always null, so we would
- recurse forever. This is kind of hacky, but it works. */
-
- recursive = TRUE;
- xtensa_mark_literal_pool_location ();
- recursive = FALSE;
+ if (!auto_litpools)
+ {
+ as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
+ }
+ xtensa_maybe_create_literal_pool_frag (TRUE, TRUE);
+ pool_location = get_literal_pool_location (now_seg);
}
lit_seg = cache_literal_section (FALSE);
xtensa_switch_section_emit_state (result, lit_seg, 0);
if (!use_literal_section
- && !is_init && !is_fini
+ && !is_init_fini
&& get_literal_pool_location (now_seg) != pool_location)
{
/* Close whatever frag is there. */
{
const char *gname = inf;
const char *group_name = elf_group_name (sec);
-
+
return (group_name == gname
|| (group_name != NULL
&& gname != NULL
cache_literal_section (bfd_boolean use_abs_literals)
{
const char *text_name, *group_name = 0;
- char *base_name, *name, *suffix;
+ const char *base_name, *suffix;
+ char *name;
segT *pcached;
segT seg, current_section;
int current_subsec;
if (*pcached)
return *pcached;
-
+
text_name = default_lit_sections.lit_prefix;
if (! text_name || ! *text_name)
{
base_name = use_abs_literals ? ".lit4" : ".literal";
if (group_name)
{
- name = xmalloc (strlen (base_name) + strlen (group_name) + 2);
- sprintf (name, "%s.%s", base_name, group_name);
+ name = concat (base_name, ".", group_name, (char *) NULL);
}
else if (strncmp (text_name, ".gnu.linkonce.", linkonce_len) == 0)
{
suffix = strchr (text_name + linkonce_len, '.');
- name = xmalloc (linkonce_len + strlen (base_name) + 1
- + (suffix ? strlen (suffix) : 0));
- strcpy (name, ".gnu.linkonce");
- strcat (name, base_name);
- if (suffix)
- strcat (name, suffix);
+ name = concat (".gnu.linkonce", base_name, suffix ? suffix : "",
+ (char *) NULL);
linkonce = TRUE;
}
else
|| strncmp (text_name, ".text", 5) == 0))
len -= 5;
- name = xmalloc (len + strlen (base_name) + 1);
+ name = XNEWVEC (char, len + strlen (base_name) + 1);
if (strncmp (text_name, ".text", 5) == 0)
{
strcpy (name, base_name);
if (! use_abs_literals)
{
/* Add the newly created literal segment to the list. */
- seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
+ seg_list *n = XNEW (seg_list);
n->seg = seg;
n->next = literal_head->next;
literal_head->next = n;
elf_group_name (seg) = group_name;
- bfd_set_section_flags (stdoutput, seg, flags);
- bfd_set_section_alignment (stdoutput, seg, 2);
+ bfd_set_section_flags (seg, flags);
+ bfd_set_section_alignment (seg, 2);
}
*pcached = seg;
num_recs++;
rec_size = num_recs * 8;
- bfd_set_section_size (stdoutput, sec, rec_size);
+ bfd_set_section_size (sec, rec_size);
if (num_recs)
{
num_recs++;
rec_size = num_recs * (8 + 4);
- bfd_set_section_size (stdoutput, sec, rec_size);
+ bfd_set_section_size (sec, rec_size);
/* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
if (num_recs)
static bfd_boolean
exclude_section_from_property_tables (segT sec)
{
- flagword flags = bfd_get_section_flags (stdoutput, sec);
+ flagword flags = bfd_section_flags (sec);
/* Sections that don't contribute to the memory footprint are excluded. */
if ((flags & SEC_DEBUGGING)
}
if (*xt_block == NULL)
{
- xtensa_block_info *new_block = (xtensa_block_info *)
- xmalloc (sizeof (xtensa_block_info));
+ xtensa_block_info *new_block = XNEW (xtensa_block_info);
new_block->sec = sec;
new_block->offset = fragP->fr_address;
new_block->size = fragP->fr_fix;
xtensa_block_info *new_block;
if ((*xt_block) != NULL)
xt_block = &(*xt_block)->next;
- new_block = (xtensa_block_info *)
- xmalloc (sizeof (xtensa_block_info));
+ new_block = XNEW (xtensa_block_info);
*new_block = tmp_block;
*xt_block = new_block;
}
int slot;
int num_opcodes = xtensa_isa_num_opcodes (isa);
- op_placement_table = (op_placement_info_table)
- xmalloc (sizeof (op_placement_info) * num_opcodes);
+ op_placement_table = XNEWVEC (op_placement_info, num_opcodes);
gas_assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
for (opcode = 0; opcode < num_opcodes; opcode++)
expressionS *exp = &tinsn->tok[i];
int rc;
unsigned line;
- char *file_name;
+ const char *file_name;
uint32 opnd_value;
switch (exp->X_op)
case O_constant:
if (xtensa_operand_is_visible (isa, opcode, i) == 0)
break;
- as_where (&file_name, &line);
+ file_name = as_where (&line);
/* 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,
{
int i;
- memset (v, 0, offsetof (vliw_insn, slots)
+ memset (v, 0, offsetof (vliw_insn, slots)
+ sizeof(TInsn) * config_max_slots);
v->format = XTENSA_UNDEFINED;
static void
xg_copy_vinsn (vliw_insn *dst, vliw_insn *src)
{
- memcpy (dst, 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));
struct rename_section_struct
{
- char *old_name;
+ const char *old_name;
char *new_name;
struct rename_section_struct *next;
};
}
/* Now add it. */
- r = (struct rename_section_struct *)
- xmalloc (sizeof (struct rename_section_struct));
+ r = XNEW (struct rename_section_struct);
r->old_name = xstrdup (old_name);
r->new_name = xstrdup (new_name);
r->next = section_rename;
char *
-xtensa_section_rename (char *name)
+xtensa_section_rename (const char *name)
{
struct rename_section_struct *r = section_rename;
return r->new_name;
}
- return name;
+ return (char *) name;
}