#include <string.h>
#include <limits.h>
#include "as.h"
#include "sb.h"
#include "safe-ctype.h"
#include "tc-xtensa.h"
#include "frags.h"
#include "subsegs.h"
#include "xtensa-relax.h"
#include "xtensa-istack.h"
#include "dwarf2dbg.h"
#include "struc-symbol.h"
#include "xtensa-config.h"
#ifndef uint32
#define uint32 unsigned int
#endif
#ifndef int32
#define int32 signed int
#endif
const char comment_chars[] = "#";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "rRsSfFdDxXpP";
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
#define UNREACHABLE_MAX_WIDTH 8
static vliw_insn cur_vinsn;
unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
static bfd_boolean past_xtensa_end = FALSE;
#define FLAG_IS_A0_WRITER 0x1
#define FLAG_IS_BAD_LOOPEND 0x2
#define LITERAL_SECTION_NAME xtensa_section_rename (".literal")
#define LIT4_SECTION_NAME xtensa_section_rename (".lit4")
#define FINI_SECTION_NAME xtensa_section_rename (".fini")
#define INIT_SECTION_NAME xtensa_section_rename (".init")
#define FINI_LITERAL_SECTION_NAME xtensa_section_rename (".fini.literal")
#define INIT_LITERAL_SECTION_NAME xtensa_section_rename (".init.literal")
typedef struct lit_state_struct
{
const char *lit_seg_name;
const char *lit4_seg_name;
const char *init_lit_seg_name;
const char *fini_lit_seg_name;
segT lit_seg;
segT lit4_seg;
segT init_lit_seg;
segT fini_lit_seg;
} lit_state;
static lit_state default_lit_sections;
typedef struct seg_list_struct
{
struct seg_list_struct *next;
segT seg;
} seg_list;
static seg_list literal_head_h;
static seg_list *literal_head = &literal_head_h;
static seg_list init_literal_head_h;
static seg_list *init_literal_head = &init_literal_head_h;
static seg_list fini_literal_head_h;
static seg_list *fini_literal_head = &fini_literal_head_h;
typedef struct sym_list_struct
{
struct sym_list_struct *next;
symbolS *sym;
} sym_list;
static sym_list *insn_labels = NULL;
static sym_list *free_insn_labels = NULL;
static sym_list *saved_insn_labels = NULL;
static sym_list *literal_syms;
int prefer_const16 = 0;
int prefer_l32r = 0;
int generating_literals = 0;
#define XTENSA_PROP_LITERAL 0x00000001
#define XTENSA_PROP_INSN 0x00000002
#define XTENSA_PROP_DATA 0x00000004
#define XTENSA_PROP_UNREACHABLE 0x00000008
#define XTENSA_PROP_INSN_LOOP_TARGET 0x00000010
#define XTENSA_PROP_INSN_BRANCH_TARGET 0x00000020
#define XTENSA_PROP_INSN_NO_DENSITY 0x00000040
#define XTENSA_PROP_INSN_NO_REORDER 0x00000080
#define XTENSA_PROP_INSN_NO_TRANSFORM 0x00000100
#define XTENSA_PROP_BT_ALIGN_MASK 0x00000600
#define XTENSA_PROP_BT_ALIGN_NONE 0x0
#define XTENSA_PROP_BT_ALIGN_LOW 0x1
#define XTENSA_PROP_BT_ALIGN_HIGH 0x2
#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_ALIGN 0x00000800
#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))
#define XTENSA_PROP_INSN_ABSLIT 0x00020000
typedef struct frag_flags_struct frag_flags;
struct frag_flags_struct
{
unsigned is_literal : 1;
unsigned is_insn : 1;
unsigned is_data : 1;
unsigned is_unreachable : 1;
struct
{
unsigned is_loop_target : 1;
unsigned is_branch_target : 1;
unsigned bt_align_priority : 2;
unsigned is_no_density : 1;
unsigned is_no_transform : 1;
unsigned is_no_reorder : 1;
unsigned is_abslit : 1;
} insn;
unsigned is_align : 1;
unsigned alignment : 5;
};
struct xtensa_block_info_struct
{
segT sec;
bfd_vma offset;
size_t size;
frag_flags flags;
struct xtensa_block_info_struct *next;
};
typedef struct emit_state_struct
{
const char *name;
segT now_seg;
subsegT now_subseg;
int generating_literals;
} emit_state;
typedef unsigned long long bitfield;
#define bit_is_set(bit, bf) ((bf) & (0x01ll << (bit)))
#define set_bit(bit, bf) ((bf) |= (0x01ll << (bit)))
#define clear_bit(bit, bf) ((bf) &= ~(0x01ll << (bit)))
#define MAX_FORMATS 32
typedef struct op_placement_info_struct
{
int num_formats;
int issuef;
xtensa_format single;
xtensa_format narrowest;
xtensa_format widest;
char narrowest_size;
char widest_size;
char single_size;
bitfield formats;
bitfield slots[MAX_FORMATS];
char slots_in_format[MAX_FORMATS];
} op_placement_info, *op_placement_info_table;
op_placement_info_table op_placement_table;
#define O_pltrel O_md1
#define O_hi16 O_md2
#define O_lo16 O_md3
typedef enum
{
directive_none = 0,
directive_literal,
directive_density,
directive_transform,
directive_freeregs,
directive_longcalls,
directive_literal_prefix,
directive_schedule,
directive_absolute_literals,
directive_last_directive
} directiveE;
typedef struct
{
const char *name;
bfd_boolean can_be_negated;
} directive_infoS;
const directive_infoS directive_info[] =
{
{ "none", FALSE },
{ "literal", FALSE },
{ "density", TRUE },
{ "transform", TRUE },
{ "freeregs", FALSE },
{ "longcalls", TRUE },
{ "literal_prefix", FALSE },
{ "schedule", TRUE },
{ "absolute-literals", TRUE }
};
bfd_boolean directive_state[] =
{
FALSE,
FALSE,
#if !XCHAL_HAVE_DENSITY
FALSE,
#else
TRUE,
#endif
TRUE,
FALSE,
FALSE,
FALSE,
TRUE,
#if XSHAL_USE_ABSOLUTE_LITERALS
TRUE
#else
FALSE
#endif
};
static void xtensa_begin_directive (int);
static void xtensa_end_directive (int);
static void xtensa_dwarf2_directive_loc (int);
static void xtensa_literal_prefix (char const *, int);
static void xtensa_literal_position (int);
static void xtensa_literal_pseudo (int);
static void xtensa_frequency_pseudo (int);
static void xtensa_elf_cons (int);
static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
static void xtensa_mark_literal_pool_location (void);
static addressT get_expanded_loop_offset (xtensa_opcode);
static fragS *get_literal_pool_location (segT);
static void set_literal_pool_location (segT, fragS *);
static void xtensa_set_frag_assembly_state (fragS *);
static void finish_vinsn (vliw_insn *);
static bfd_boolean emit_single_op (TInsn *);
static int total_frag_text_expansion (fragS *);
static int get_text_align_power (unsigned);
static int get_text_align_max_fill_size (int, bfd_boolean, bfd_boolean);
static int branch_align_power (segT);
static long relax_frag_add_nop (fragS *);
static unsigned get_last_insn_flags (segT, subsegT);
static void set_last_insn_flags (segT, subsegT, unsigned, bfd_boolean);
static float get_subseg_total_freq (segT, subsegT);
static float get_subseg_target_freq (segT, subsegT);
static void set_subseg_freq (segT, subsegT, float, float);
static void xtensa_move_literals (void);
static void xtensa_reorder_segments (void);
static void xtensa_switch_to_literal_fragment (emit_state *);
static void xtensa_switch_to_non_abs_literal_fragment (emit_state *);
static void xtensa_switch_section_emit_state (emit_state *, segT, subsegT);
static void xtensa_restore_emit_state (emit_state *);
static void cache_literal_section
(seg_list *, const char *, segT *, bfd_boolean);
extern char *xtensa_get_property_section_name (asection *, const char *);
static void init_op_placement_info_table (void);
extern bfd_boolean opcode_fits_format_slot (xtensa_opcode, xtensa_format, int);
static int xg_get_single_size (xtensa_opcode);
static xtensa_format xg_get_single_format (xtensa_opcode);
static bfd_boolean tinsn_has_symbolic_operands (const TInsn *);
static bfd_boolean tinsn_has_invalid_symbolic_operands (const TInsn *);
static bfd_boolean tinsn_has_complex_operands (const TInsn *);
static bfd_boolean tinsn_to_insnbuf (TInsn *, xtensa_insnbuf);
static bfd_boolean tinsn_check_arguments (const TInsn *);
static void tinsn_from_chars (TInsn *, char *, int);
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 void xg_init_vinsn (vliw_insn *);
static void xg_clear_vinsn (vliw_insn *);
static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
static void xg_free_vinsn (vliw_insn *);
static bfd_boolean vinsn_to_insnbuf
(vliw_insn *, char *, fragS *, bfd_boolean);
static void vinsn_from_chars (vliw_insn *, char *);
bfd_boolean expr_is_const (const expressionS *);
offsetT get_expr_const (const expressionS *);
void set_expr_const (expressionS *, offsetT);
bfd_boolean expr_is_register (const expressionS *);
offsetT get_expr_register (const expressionS *);
void set_expr_symbol_offset (expressionS *, symbolS *, offsetT);
static void set_expr_symbol_offset_diff
(expressionS *, symbolS *, symbolS *, offsetT);
bfd_boolean expr_is_equal (expressionS *, expressionS *);
static void copy_expr (expressionS *, const expressionS *);
static void build_section_rename (const char *);
extern xtensa_isa xtensa_default_isa;
extern int target_big_endian;
static xtensa_opcode xtensa_addi_opcode;
static xtensa_opcode xtensa_addmi_opcode;
static xtensa_opcode xtensa_call0_opcode;
static xtensa_opcode xtensa_call4_opcode;
static xtensa_opcode xtensa_call8_opcode;
static xtensa_opcode xtensa_call12_opcode;
static xtensa_opcode xtensa_callx0_opcode;
static xtensa_opcode xtensa_callx4_opcode;
static xtensa_opcode xtensa_callx8_opcode;
static xtensa_opcode xtensa_callx12_opcode;
static xtensa_opcode xtensa_const16_opcode;
static xtensa_opcode xtensa_entry_opcode;
static xtensa_opcode xtensa_movi_opcode;
static xtensa_opcode xtensa_movi_n_opcode;
static xtensa_opcode xtensa_isync_opcode;
static xtensa_opcode xtensa_jx_opcode;
static xtensa_opcode xtensa_l32r_opcode;
static xtensa_opcode xtensa_loop_opcode;
static xtensa_opcode xtensa_loopnez_opcode;
static xtensa_opcode xtensa_loopgtz_opcode;
static xtensa_opcode xtensa_nop_opcode;
static xtensa_opcode xtensa_nop_n_opcode;
static xtensa_opcode xtensa_or_opcode;
static xtensa_opcode xtensa_ret_opcode;
static xtensa_opcode xtensa_ret_n_opcode;
static xtensa_opcode xtensa_retw_opcode;
static xtensa_opcode xtensa_retw_n_opcode;
static xtensa_opcode xtensa_rsr_lcount_opcode;
static xtensa_opcode xtensa_waiti_opcode;
bfd_boolean use_literal_section = TRUE;
static bfd_boolean align_targets = TRUE;
static bfd_boolean warn_unaligned_branch_targets = FALSE;
static bfd_boolean has_a0_b_retw = FALSE;
static bfd_boolean workaround_a0_b_retw = FALSE;
static bfd_boolean workaround_b_j_loop_end = FALSE;
static bfd_boolean workaround_short_loop = FALSE;
static bfd_boolean maybe_has_short_loop = FALSE;
static bfd_boolean workaround_close_loop_end = FALSE;
static bfd_boolean maybe_has_close_loop_end = FALSE;
static bfd_boolean workaround_all_short_loops = FALSE;
static void
xtensa_setup_hw_workarounds (int earliest, int latest)
{
if (earliest > latest)
as_fatal (_("illegal range of target hardware versions"));
if (earliest < 105000 || latest < 105000)
{
workaround_a0_b_retw |= TRUE;
workaround_b_j_loop_end |= TRUE;
workaround_short_loop |= TRUE;
workaround_close_loop_end |= TRUE;
workaround_all_short_loops |= TRUE;
}
}
enum
{
option_density = OPTION_MD_BASE,
option_no_density,
option_relax,
option_no_relax,
option_link_relax,
option_no_link_relax,
option_generics,
option_no_generics,
option_transform,
option_no_transform,
option_text_section_literals,
option_no_text_section_literals,
option_absolute_literals,
option_no_absolute_literals,
option_align_targets,
option_no_align_targets,
option_warn_unaligned_targets,
option_longcalls,
option_no_longcalls,
option_workaround_a0_b_retw,
option_no_workaround_a0_b_retw,
option_workaround_b_j_loop_end,
option_no_workaround_b_j_loop_end,
option_workaround_short_loop,
option_no_workaround_short_loop,
option_workaround_all_short_loops,
option_no_workaround_all_short_loops,
option_workaround_close_loop_end,
option_no_workaround_close_loop_end,
option_no_workarounds,
option_rename_section_name,
option_prefer_l32r,
option_prefer_const16,
option_target_hardware
};
const char *md_shortopts = "";
struct option md_longopts[] =
{
{ "density", no_argument, NULL, option_density },
{ "no-density", no_argument, NULL, option_no_density },
{ "relax", no_argument, NULL, option_relax },
{ "no-relax", no_argument, NULL, option_no_relax },
{ "generics", no_argument, NULL, option_generics },
{ "no-generics", no_argument, NULL, option_no_generics },
{ "transform", no_argument, NULL, option_transform },
{ "no-transform", no_argument, NULL, option_no_transform },
{ "text-section-literals", no_argument, NULL, option_text_section_literals },
{ "no-text-section-literals", no_argument, NULL,
option_no_text_section_literals },
{ "absolute-literals", no_argument, NULL, option_absolute_literals },
{ "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
{ "target-align", no_argument, NULL, option_align_targets },
{ "no-target-align", no_argument, NULL, option_no_align_targets },
{ "warn-unaligned-targets", no_argument, NULL,
option_warn_unaligned_targets },
{ "longcalls", no_argument, NULL, option_longcalls },
{ "no-longcalls", no_argument, NULL, option_no_longcalls },
{ "no-workaround-a0-b-retw", no_argument, NULL,
option_no_workaround_a0_b_retw },
{ "workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw },
{ "no-workaround-b-j-loop-end", no_argument, NULL,
option_no_workaround_b_j_loop_end },
{ "workaround-b-j-loop-end", no_argument, NULL,
option_workaround_b_j_loop_end },
{ "no-workaround-short-loops", no_argument, NULL,
option_no_workaround_short_loop },
{ "workaround-short-loops", no_argument, NULL,
option_workaround_short_loop },
{ "no-workaround-all-short-loops", no_argument, NULL,
option_no_workaround_all_short_loops },
{ "workaround-all-short-loop", no_argument, NULL,
option_workaround_all_short_loops },
{ "prefer-l32r", no_argument, NULL, option_prefer_l32r },
{ "prefer-const16", no_argument, NULL, option_prefer_const16 },
{ "no-workarounds", no_argument, NULL, option_no_workarounds },
{ "no-workaround-close-loop-end", no_argument, NULL,
option_no_workaround_close_loop_end },
{ "workaround-close-loop-end", no_argument, NULL,
option_workaround_close_loop_end },
{ "rename-section", required_argument, NULL, option_rename_section_name },
{ "link-relax", no_argument, NULL, option_link_relax },
{ "no-link-relax", no_argument, NULL, option_no_link_relax },
{ "target-hardware", required_argument, NULL, option_target_hardware },
{ NULL, no_argument, NULL, 0 }
};
size_t md_longopts_size = sizeof md_longopts;
int
md_parse_option (int c, char *arg)
{
switch (c)
{
case option_density:
as_warn (_("--density option is ignored"));
return 1;
case option_no_density:
as_warn (_("--no-density option is ignored"));
return 1;
case option_link_relax:
linkrelax = 1;
return 1;
case option_no_link_relax:
linkrelax = 0;
return 1;
case option_generics:
as_warn (_("--generics is deprecated; use --transform instead"));
return md_parse_option (option_transform, arg);
case option_no_generics:
as_warn (_("--no-generics is deprecated; use --no-transform instead"));
return md_parse_option (option_no_transform, arg);
case option_relax:
as_warn (_("--relax is deprecated; use --transform instead"));
return md_parse_option (option_transform, arg);
case option_no_relax:
as_warn (_("--no-relax is deprecated; use --no-transform instead"));
return md_parse_option (option_no_transform, arg);
case option_longcalls:
directive_state[directive_longcalls] = TRUE;
return 1;
case option_no_longcalls:
directive_state[directive_longcalls] = FALSE;
return 1;
case option_text_section_literals:
use_literal_section = FALSE;
return 1;
case option_no_text_section_literals:
use_literal_section = TRUE;
return 1;
case option_absolute_literals:
if (!absolute_literals_supported)
{
as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
return 0;
}
directive_state[directive_absolute_literals] = TRUE;
return 1;
case option_no_absolute_literals:
directive_state[directive_absolute_literals] = FALSE;
return 1;
case option_workaround_a0_b_retw:
workaround_a0_b_retw = TRUE;
return 1;
case option_no_workaround_a0_b_retw:
workaround_a0_b_retw = FALSE;
return 1;
case option_workaround_b_j_loop_end:
workaround_b_j_loop_end = TRUE;
return 1;
case option_no_workaround_b_j_loop_end:
workaround_b_j_loop_end = FALSE;
return 1;
case option_workaround_short_loop:
workaround_short_loop = TRUE;
return 1;
case option_no_workaround_short_loop:
workaround_short_loop = FALSE;
return 1;
case option_workaround_all_short_loops:
workaround_all_short_loops = TRUE;
return 1;
case option_no_workaround_all_short_loops:
workaround_all_short_loops = FALSE;
return 1;
case option_workaround_close_loop_end:
workaround_close_loop_end = TRUE;
return 1;
case option_no_workaround_close_loop_end:
workaround_close_loop_end = FALSE;
return 1;
case option_no_workarounds:
workaround_a0_b_retw = FALSE;
workaround_b_j_loop_end = FALSE;
workaround_short_loop = FALSE;
workaround_all_short_loops = FALSE;
workaround_close_loop_end = FALSE;
return 1;
case option_align_targets:
align_targets = TRUE;
return 1;
case option_no_align_targets:
align_targets = FALSE;
return 1;
case option_warn_unaligned_targets:
warn_unaligned_branch_targets = TRUE;
return 1;
case option_rename_section_name:
build_section_rename (arg);
return 1;
case 'Q':
return 1;
case option_prefer_l32r:
if (prefer_const16)
as_fatal (_("prefer-l32r conflicts with prefer-const16"));
prefer_l32r = 1;
return 1;
case option_prefer_const16:
if (prefer_l32r)
as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
prefer_const16 = 1;
return 1;
case option_target_hardware:
{
int earliest, latest = 0;
if (*arg == 0 || *arg == '-')
as_fatal (_("invalid target hardware version"));
earliest = strtol (arg, &arg, 0);
if (*arg == 0)
latest = earliest;
else if (*arg == '-')
{
if (*++arg == 0)
as_fatal (_("invalid target hardware version"));
latest = strtol (arg, &arg, 0);
}
if (*arg != 0)
as_fatal (_("invalid target hardware version"));
xtensa_setup_hw_workarounds (earliest, latest);
return 1;
}
case option_transform:
return 1;
case option_no_transform:
directive_state[directive_transform] = FALSE;
return 1;
default:
return 0;
}
}
void
md_show_usage (FILE *stream)
{
fputs ("\n\
Xtensa options:\n\
--[no-]text-section-literals\n\
[Do not] put literals in the text section\n\
--[no-]absolute-literals\n\
[Do not] default to use non-PC-relative literals\n\
--[no-]target-align [Do not] try to align branch targets\n\
--[no-]longcalls [Do not] emit 32-bit call sequences\n\
--[no-]transform [Do not] transform instructions\n\
--rename-section old=new Rename section 'old' to 'new'\n", stream);
}
static void
xtensa_add_insn_label (symbolS *sym)
{
sym_list *l;
if (!free_insn_labels)
l = (sym_list *) xmalloc (sizeof (sym_list));
else
{
l = free_insn_labels;
free_insn_labels = l->next;
}
l->sym = sym;
l->next = insn_labels;
insn_labels = l;
}
static void
xtensa_clear_insn_labels (void)
{
sym_list **pl;
for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
;
*pl = insn_labels;
insn_labels = NULL;
}
static void
xtensa_move_labels (fragS *new_frag, valueT new_offset, bfd_boolean loops_ok)
{
sym_list *lit;
for (lit = insn_labels; lit; lit = lit->next)
{
symbolS *lit_sym = lit->sym;
if (loops_ok || ! symbol_get_tc (lit_sym)->is_loop_target)
{
S_SET_VALUE (lit_sym, new_offset);
symbol_set_frag (lit_sym, new_frag);
}
}
}
typedef struct state_stackS_struct
{
directiveE directive;
bfd_boolean negated;
bfd_boolean old_state;
const char *file;
unsigned int line;
const void *datum;
struct state_stackS_struct *prev;
} state_stackS;
state_stackS *directive_state_stack;
const pseudo_typeS md_pseudo_table[] =
{
{ "align", s_align_bytes, 0 },
{ "literal_position", xtensa_literal_position, 0 },
{ "frame", s_ignore, 0 },
{ "long", xtensa_elf_cons, 4 },
{ "word", xtensa_elf_cons, 4 },
{ "short", xtensa_elf_cons, 2 },
{ "begin", xtensa_begin_directive, 0 },
{ "end", xtensa_end_directive, 0 },
{ "loc", xtensa_dwarf2_directive_loc, 0 },
{ "literal", xtensa_literal_pseudo, 0 },
{ "frequency", xtensa_frequency_pseudo, 0 },
{ NULL, 0, 0 },
};
static bfd_boolean
use_transform (void)
{
assert (!past_xtensa_end);
return directive_state[directive_transform];
}
static bfd_boolean
do_align_targets (void)
{
assert (!past_xtensa_end);
return align_targets && use_transform ();
}
static void
directive_push (directiveE directive, bfd_boolean negated, const void *datum)
{
char *file;
unsigned int line;
state_stackS *stack = (state_stackS *) xmalloc (sizeof (state_stackS));
as_where (&file, &line);
stack->directive = directive;
stack->negated = negated;
stack->old_state = directive_state[directive];
stack->file = file;
stack->line = line;
stack->datum = datum;
stack->prev = directive_state_stack;
directive_state_stack = stack;
directive_state[directive] = !negated;
}
static void
directive_pop (directiveE *directive,
bfd_boolean *negated,
const char **file,
unsigned int *line,
const void **datum)
{
state_stackS *top = directive_state_stack;
if (!directive_state_stack)
{
as_bad (_("unmatched end directive"));
*directive = directive_none;
return;
}
directive_state[directive_state_stack->directive] = top->old_state;
*directive = top->directive;
*negated = top->negated;
*file = top->file;
*line = top->line;
*datum = top->datum;
directive_state_stack = top->prev;
free (top);
}
static void
directive_balance (void)
{
while (directive_state_stack)
{
directiveE directive;
bfd_boolean negated;
const char *file;
unsigned int line;
const void *datum;
directive_pop (&directive, &negated, &file, &line, &datum);
as_warn_where ((char *) file, line,
_(".begin directive with no matching .end directive"));
}
}
static bfd_boolean
inside_directive (directiveE dir)
{
state_stackS *top = directive_state_stack;
while (top && top->directive != dir)
top = top->prev;
return (top != NULL);
}
static void
get_directive (directiveE *directive, bfd_boolean *negated)
{
int len;
unsigned i;
char *directive_string;
if (strncmp (input_line_pointer, "no-", 3) != 0)
*negated = FALSE;
else
{
*negated = TRUE;
input_line_pointer += 3;
}
len = strspn (input_line_pointer,
"abcdefghijklmnopqrstuvwxyz_-/0123456789.");
if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
{
as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
directive_string = "transform";
}
else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
{
as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
directive_string = "transform";
}
else
directive_string = input_line_pointer;
for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
{
if (strncmp (directive_string, directive_info[i].name, len) == 0)
{
input_line_pointer += len;
*directive = (directiveE) i;
if (*negated && !directive_info[i].can_be_negated)
as_bad (_("directive %s cannot be negated"),
directive_info[i].name);
return;
}
}
as_bad (_("unknown directive"));
*directive = (directiveE) XTENSA_UNDEFINED;
}
static void
xtensa_begin_directive (int ignore ATTRIBUTE_UNUSED)
{
directiveE directive;
bfd_boolean negated;
emit_state *state;
int len;
lit_state *ls;
get_directive (&directive, &negated);
if (directive == (directiveE) XTENSA_UNDEFINED)
{
discard_rest_of_line ();
return;
}
if (cur_vinsn.inside_bundle)
as_bad (_("directives are not valid inside bundles"));
switch (directive)
{
case directive_literal:
if (!inside_directive (directive_literal))
{
saved_insn_labels = insn_labels;
insn_labels = NULL;
}
as_warn (_(".begin literal is deprecated; use .literal instead"));
state = (emit_state *) xmalloc (sizeof (emit_state));
xtensa_switch_to_literal_fragment (state);
directive_push (directive_literal, negated, state);
break;
case directive_literal_prefix:
md_flush_pending_output ();
if (generating_literals)
{
as_bad (_("cannot set literal_prefix inside literal fragment"));
return;
}
ls = xmalloc (sizeof (lit_state));
assert (ls);
*ls = default_lit_sections;
directive_push (directive_literal_prefix, negated, ls);
SKIP_WHITESPACE ();
len = strspn (input_line_pointer,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz_/0123456789.$");
xtensa_literal_prefix (input_line_pointer, len);
input_line_pointer += len;
break;
case directive_freeregs:
input_line_pointer += strcspn (input_line_pointer, "\n");
directive_push (directive_freeregs, negated, 0);
break;
case directive_schedule:
md_flush_pending_output ();
frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
directive_push (directive_schedule, negated, 0);
xtensa_set_frag_assembly_state (frag_now);
break;
case directive_density:
as_warn (_(".begin [no-]density is ignored"));
break;
case directive_absolute_literals:
md_flush_pending_output ();
if (!absolute_literals_supported && !negated)
{
as_warn (_("Xtensa absolute literals option not supported; ignored"));
break;
}
xtensa_set_frag_assembly_state (frag_now);
directive_push (directive, negated, 0);
break;
default:
md_flush_pending_output ();
xtensa_set_frag_assembly_state (frag_now);
directive_push (directive, negated, 0);
break;
}
demand_empty_rest_of_line ();
}
static void
xtensa_end_directive (int ignore ATTRIBUTE_UNUSED)
{
directiveE begin_directive, end_directive;
bfd_boolean begin_negated, end_negated;
const char *file;
unsigned int line;
emit_state *state;
emit_state **state_ptr;
lit_state *s;
if (cur_vinsn.inside_bundle)
as_bad (_("directives are not valid inside bundles"));
get_directive (&end_directive, &end_negated);
md_flush_pending_output ();
switch (end_directive)
{
case (directiveE) XTENSA_UNDEFINED:
discard_rest_of_line ();
return;
case directive_density:
as_warn (_(".end [no-]density is ignored"));
demand_empty_rest_of_line ();
break;
case directive_absolute_literals:
if (!absolute_literals_supported && !end_negated)
{
as_warn (_("Xtensa absolute literals option not supported; ignored"));
demand_empty_rest_of_line ();
return;
}
break;
default:
break;
}
state_ptr = &state;
directive_pop (&begin_directive, &begin_negated, &file, &line,
(const void **) state_ptr);
if (begin_directive != directive_none)
{
if (begin_directive != end_directive || begin_negated != end_negated)
{
as_bad (_("does not match begin %s%s at %s:%d"),
begin_negated ? "no-" : "",
directive_info[begin_directive].name, file, line);
}
else
{
switch (end_directive)
{
case directive_literal:
frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_restore_emit_state (state);
xtensa_set_frag_assembly_state (frag_now);
free (state);
if (!inside_directive (directive_literal))
{
xtensa_clear_insn_labels ();
insn_labels = saved_insn_labels;
}
break;
case directive_literal_prefix:
s = (lit_state *) state;
assert (s);
if (use_literal_section)
default_lit_sections = *s;
free (s);
break;
case directive_schedule:
case directive_freeregs:
break;
default:
xtensa_set_frag_assembly_state (frag_now);
break;
}
}
}
demand_empty_rest_of_line ();
}
static bfd_boolean xtensa_loc_directive_seen = FALSE;
static void
xtensa_dwarf2_directive_loc (int x)
{
xtensa_loc_directive_seen = TRUE;
dwarf2_directive_loc (x);
}
static void
xtensa_dwarf2_emit_insn (int size, struct dwarf2_line_info *loc)
{
if (debug_type != DEBUG_DWARF2 && ! xtensa_loc_directive_seen)
return;
xtensa_loc_directive_seen = FALSE;
dwarf2_gen_line_info (frag_now_fix () - size, loc);
}
static void
xtensa_literal_position (int ignore ATTRIBUTE_UNUSED)
{
md_flush_pending_output ();
if (inside_directive (directive_literal))
as_warn (_(".literal_position inside literal directive; ignoring"));
xtensa_mark_literal_pool_location ();
demand_empty_rest_of_line ();
xtensa_clear_insn_labels ();
}
static void
xtensa_literal_pseudo (int ignored ATTRIBUTE_UNUSED)
{
emit_state state;
char *p, *base_name;
char c;
segT dest_seg;
if (inside_directive (directive_literal))
{
as_bad (_(".literal not allowed inside .begin literal region"));
ignore_rest_of_line ();
return;
}
md_flush_pending_output ();
saved_insn_labels = insn_labels;
insn_labels = NULL;
dest_seg = now_seg;
base_name = input_line_pointer;
xtensa_switch_to_literal_fragment (&state);
if (use_literal_section || directive_state[directive_absolute_literals])
dest_seg = now_seg;
frag_align (2, 0, 0);
record_alignment (now_seg, 2);
c = get_symbol_end ();
p = input_line_pointer;
*p = c;
SKIP_WHITESPACE ();
if (*input_line_pointer != ',' && *input_line_pointer != ':')
{
as_bad (_("expected comma or colon after symbol name; "
"rest of line ignored"));
ignore_rest_of_line ();
xtensa_restore_emit_state (&state);
return;
}
*p = 0;
colon (base_name);
*p = c;
input_line_pointer++;
xtensa_elf_cons (4);
xtensa_restore_emit_state (&state);
xtensa_clear_insn_labels ();
insn_labels = saved_insn_labels;
}
static void
xtensa_literal_prefix (char const *start, int len)
{
char *name, *linkonce_suffix;
char *newname, *newname4;
size_t linkonce_len;
name = xmalloc (len + 1);
assert (name);
strncpy (name, start, len);
name[len] = 0;
newname = xmalloc (len + strlen (".literal") + 1);
newname4 = xmalloc (len + strlen (".lit4") + 1);
linkonce_len = sizeof (".gnu.linkonce.") - 1;
if (strncmp (name, ".gnu.linkonce.", linkonce_len) == 0
&& (linkonce_suffix = strchr (name + linkonce_len, '.')) != 0)
{
strcpy (newname, ".gnu.linkonce.literal");
strcpy (newname4, ".gnu.linkonce.lit4");
strcat (newname, linkonce_suffix);
strcat (newname4, linkonce_suffix);
}
else
{
int suffix_pos = len;
if (len >= 5 && strcmp (name + len - 5, ".text") == 0)
suffix_pos -= 5;
strcpy (newname, name);
strcpy (newname4, name);
strcpy (newname + suffix_pos, ".literal");
strcpy (newname4 + suffix_pos, ".lit4");
}
default_lit_sections.lit_seg = NULL;
default_lit_sections.lit4_seg = NULL;
default_lit_sections.lit_seg_name = tc_canonicalize_symbol_name (newname);
default_lit_sections.lit4_seg_name = tc_canonicalize_symbol_name (newname4);
free (name);
}
static void
xtensa_frequency_pseudo (int ignored ATTRIBUTE_UNUSED)
{
float fall_through_f, target_f;
fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
if (fall_through_f < 0)
{
as_bad (_("fall through frequency must be greater than 0"));
ignore_rest_of_line ();
return;
}
target_f = (float) strtod (input_line_pointer, &input_line_pointer);
if (target_f < 0)
{
as_bad (_("branch target frequency must be greater than 0"));
ignore_rest_of_line ();
return;
}
set_subseg_freq (now_seg, now_subseg, target_f + fall_through_f, target_f);
demand_empty_rest_of_line ();
}
static void
xtensa_elf_cons (int nbytes)
{
expressionS exp;
bfd_reloc_code_real_type reloc;
md_flush_pending_output ();
if (cur_vinsn.inside_bundle)
as_bad (_("directives are not valid inside bundles"));
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
do
{
expression (&exp);
if (exp.X_op == O_symbol
&& *input_line_pointer == '@'
&& ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
!= BFD_RELOC_NONE))
{
reloc_howto_type *reloc_howto =
bfd_reloc_type_lookup (stdoutput, reloc);
if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
as_bad (_("unsupported relocation"));
else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
&& reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
|| (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
&& reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
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"),
reloc_howto->name, nbytes);
else
{
char *p = frag_more ((int) nbytes);
xtensa_set_frag_assembly_state (frag_now);
fix_new_exp (frag_now, p - frag_now->fr_literal,
nbytes, &exp, 0, reloc);
}
}
else
emit_expr (&exp, (unsigned int) nbytes);
}
while (*input_line_pointer++ == ',');
input_line_pointer--;
demand_empty_rest_of_line ();
}
static bfd_reloc_code_real_type
xtensa_elf_suffix (char **str_p, expressionS *exp_p)
{
struct map_bfd
{
char *string;
int length;
bfd_reloc_code_real_type reloc;
};
char ident[20];
char *str = *str_p;
char *str2;
int ch;
int len;
struct map_bfd *ptr;
#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
static struct map_bfd mapping[] =
{
MAP ("l", BFD_RELOC_LO16),
MAP ("h", BFD_RELOC_HI16),
MAP ("plt", BFD_RELOC_XTENSA_PLT),
{ (char *) 0, 0, BFD_RELOC_UNUSED }
};
if (*str++ != '@')
return BFD_RELOC_NONE;
for (ch = *str, str2 = ident;
(str2 < ident + sizeof (ident) - 1
&& (ISALNUM (ch) || ch == '@'));
ch = *++str)
{
*str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
}
*str2 = '\0';
len = str2 - ident;
ch = ident[0];
for (ptr = &mapping[0]; ptr->length > 0; ptr++)
if (ch == ptr->string[0]
&& len == ptr->length
&& memcmp (ident, ptr->string, ptr->length) == 0)
{
if (*str == '-' || *str == '+')
{
char *orig_line = input_line_pointer;
expressionS new_exp;
input_line_pointer = str;
expression (&new_exp);
if (new_exp.X_op == O_constant)
{
exp_p->X_add_number += new_exp.X_add_number;
str = input_line_pointer;
}
if (&input_line_pointer != str_p)
input_line_pointer = orig_line;
}
*str_p = str;
return ptr->reloc;
}
return BFD_RELOC_UNUSED;
}
static const char *
expression_end (const char *name)
{
while (1)
{
switch (*name)
{
case '}':
case ';':
case '\0':
case ',':
case ':':
return name;
case ' ':
case '\t':
++name;
continue;
default:
return 0;
}
}
}
#define ERROR_REG_NUM ((unsigned) -1)
static unsigned
tc_get_register (const char *prefix)
{
unsigned reg;
const char *next_expr;
const char *old_line_pointer;
SKIP_WHITESPACE ();
old_line_pointer = input_line_pointer;
if (*input_line_pointer == '$')
++input_line_pointer;
if (input_line_pointer[0] == 's' && input_line_pointer[1] == 'p'
&& expression_end (input_line_pointer + 2))
{
input_line_pointer += 2;
return 1;
}
while (*input_line_pointer++ == *prefix++)
;
--input_line_pointer;
--prefix;
if (*prefix)
{
as_bad (_("bad register name: %s"), old_line_pointer);
return ERROR_REG_NUM;
}
if (!ISDIGIT ((unsigned char) *input_line_pointer))
{
as_bad (_("bad register number: %s"), input_line_pointer);
return ERROR_REG_NUM;
}
reg = 0;
while (ISDIGIT ((int) *input_line_pointer))
reg = reg * 10 + *input_line_pointer++ - '0';
if (!(next_expr = expression_end (input_line_pointer)))
{
as_bad (_("bad register name: %s"), old_line_pointer);
return ERROR_REG_NUM;
}
input_line_pointer = (char *) next_expr;
return reg;
}
static void
expression_maybe_register (xtensa_opcode opc, int opnd, expressionS *tok)
{
xtensa_isa isa = xtensa_default_isa;
if (xtensa_operand_is_register (isa, opc, opnd) == 0)
{
bfd_reloc_code_real_type reloc;
segT t = expression (tok);
if (t == absolute_section
&& xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
{
assert (tok->X_op == O_constant);
tok->X_op = O_symbol;
tok->X_add_symbol = &abs_symbol;
}
if ((tok->X_op == O_constant || tok->X_op == O_symbol)
&& (reloc = xtensa_elf_suffix (&input_line_pointer, tok))
&& (reloc != BFD_RELOC_NONE))
{
switch (reloc)
{
default:
case BFD_RELOC_UNUSED:
as_bad (_("unsupported relocation"));
break;
case BFD_RELOC_XTENSA_PLT:
tok->X_op = O_pltrel;
break;
case BFD_RELOC_LO16:
if (tok->X_op == O_constant)
tok->X_add_number &= 0xffff;
else
tok->X_op = O_lo16;
break;
case BFD_RELOC_HI16:
if (tok->X_op == O_constant)
tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
else
tok->X_op = O_hi16;
break;
}
}
}
else
{
xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
if (reg != ERROR_REG_NUM)
{
uint32 buf = reg;
if (xtensa_operand_encode (isa, opc, opnd, &buf))
as_bad (_("register number out of range"));
}
tok->X_op = O_register;
tok->X_add_symbol = 0;
tok->X_add_number = reg;
}
}
static int
tokenize_arguments (char **args, char *str)
{
char *old_input_line_pointer;
bfd_boolean saw_comma = FALSE;
bfd_boolean saw_arg = FALSE;
bfd_boolean saw_colon = FALSE;
int num_args = 0;
char *arg_end, *arg;
int arg_len;
old_input_line_pointer = input_line_pointer;
input_line_pointer = str;
while (*input_line_pointer)
{
SKIP_WHITESPACE ();
switch (*input_line_pointer)
{
case '\0':
case '}':
goto fini;
case ':':
input_line_pointer++;
if (saw_comma || saw_colon || !saw_arg)
goto err;
saw_colon = TRUE;
break;
case ',':
input_line_pointer++;
if (saw_comma || saw_colon || !saw_arg)
goto err;
saw_comma = TRUE;
break;
default:
if (!saw_comma && !saw_colon && saw_arg)
goto err;
arg_end = input_line_pointer + 1;
while (!expression_end (arg_end))
arg_end += 1;
arg_len = arg_end - input_line_pointer;
arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
args[num_args] = arg;
if (saw_colon)
*arg++ = ':';
strncpy (arg, input_line_pointer, arg_len);
arg[arg_len] = '\0';
input_line_pointer = arg_end;
num_args += 1;
saw_comma = FALSE;
saw_colon = FALSE;
saw_arg = TRUE;
break;
}
}
fini:
if (saw_comma || saw_colon)
goto err;
input_line_pointer = old_input_line_pointer;
return num_args;
err:
if (saw_comma)
as_bad (_("extra comma"));
else if (saw_colon)
as_bad (_("extra colon"));
else if (!saw_arg)
as_bad (_("missing argument"));
else
as_bad (_("missing comma or colon"));
input_line_pointer = old_input_line_pointer;
return -1;
}
static bfd_boolean
parse_arguments (TInsn *insn, int num_args, char **arg_strings)
{
expressionS *tok, *last_tok;
xtensa_opcode opcode = insn->opcode;
bfd_boolean had_error = TRUE;
xtensa_isa isa = xtensa_default_isa;
int n, num_regs = 0;
int opcode_operand_count;
int opnd_cnt, last_opnd_cnt;
unsigned int next_reg = 0;
char *old_input_line_pointer;
if (insn->insn_type == ITYPE_LITERAL)
opcode_operand_count = 1;
else
opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
tok = insn->tok;
memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
old_input_line_pointer = input_line_pointer;
last_tok = 0;
last_opnd_cnt = -1;
opnd_cnt = 0;
while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
{
opnd_cnt += 1;
tok++;
}
for (n = 0; n < num_args; n++)
{
input_line_pointer = arg_strings[n];
if (*input_line_pointer == ':')
{
xtensa_regfile opnd_rf;
input_line_pointer++;
if (num_regs == 0)
goto err;
assert (opnd_cnt > 0);
num_regs--;
opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
if (next_reg
!= tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
as_warn (_("incorrect register number, ignoring"));
next_reg++;
}
else
{
if (opnd_cnt >= opcode_operand_count)
{
as_warn (_("too many arguments"));
goto err;
}
assert (opnd_cnt < MAX_INSN_ARGS);
expression_maybe_register (opcode, opnd_cnt, tok);
next_reg = tok->X_add_number + 1;
if (tok->X_op == O_illegal || tok->X_op == O_absent)
goto err;
if (xtensa_operand_is_register (isa, opcode, opnd_cnt) == 1)
{
num_regs = xtensa_operand_num_regs (isa, opcode, opnd_cnt) - 1;
}
else
num_regs = 0;
last_tok = tok;
last_opnd_cnt = opnd_cnt;
do
{
opnd_cnt += 1;
tok++;
}
while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
}
}
if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
goto err;
insn->ntok = tok - insn->tok;
had_error = FALSE;
err:
input_line_pointer = old_input_line_pointer;
return had_error;
}
static int
get_invisible_operands (TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
static xtensa_insnbuf slotbuf = NULL;
xtensa_format fmt;
xtensa_opcode opc = insn->opcode;
int slot, opnd, fmt_found;
unsigned val;
if (!slotbuf)
slotbuf = xtensa_insnbuf_alloc (isa);
fmt_found = 0;
slot = 0;
for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
{
for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
{
if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
{
fmt_found = 1;
break;
}
}
if (fmt_found) break;
}
if (!fmt_found)
{
as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
return -1;
}
for (opnd = 0; opnd < insn->ntok; opnd++)
{
if (xtensa_operand_is_visible (isa, opc, opnd) == 1
&& (insn->tok[opnd].X_op == O_register
|| insn->tok[opnd].X_op == O_constant))
{
val = insn->tok[opnd].X_add_number;
xtensa_operand_encode (isa, opc, opnd, &val);
xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
}
}
for (opnd = 0; opnd < insn->ntok; opnd++)
{
if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
{
xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
xtensa_operand_decode (isa, opc, opnd, &val);
insn->tok[opnd].X_add_number = val;
if (xtensa_operand_is_register (isa, opc, opnd) == 1)
insn->tok[opnd].X_op = O_register;
else
insn->tok[opnd].X_op = O_constant;
}
}
return 0;
}
static void
xg_reverse_shift_count (char **cnt_argp)
{
char *cnt_arg, *new_arg;
cnt_arg = *cnt_argp;
new_arg = (char *) xmalloc (strlen (cnt_arg) + 6);
sprintf (new_arg, "31-(%s)", cnt_arg);
free (cnt_arg);
*cnt_argp = new_arg;
}
static int
xg_arg_is_constant (char *arg, offsetT *valp)
{
expressionS exp;
char *save_ptr = input_line_pointer;
input_line_pointer = arg;
expression (&exp);
input_line_pointer = save_ptr;
if (exp.X_op == O_constant)
{
*valp = exp.X_add_number;
return 1;
}
return 0;
}
static void
xg_replace_opname (char **popname, char *newop)
{
free (*popname);
*popname = (char *) xmalloc (strlen (newop) + 1);
strcpy (*popname, newop);
}
static int
xg_check_num_args (int *pnum_args,
int expected_num,
char *opname,
char **arg_strings)
{
int num_args = *pnum_args;
if (num_args < expected_num)
{
as_bad (_("not enough operands (%d) for '%s'; expected %d"),
num_args, opname, expected_num);
return -1;
}
if (num_args > expected_num)
{
as_warn (_("too many operands (%d) for '%s'; expected %d"),
num_args, opname, expected_num);
while (num_args-- > expected_num)
{
free (arg_strings[num_args]);
arg_strings[num_args] = 0;
}
*pnum_args = expected_num;
return -1;
}
return 0;
}
static int
xg_translate_sysreg_op (char **popname, int *pnum_args, char **arg_strings)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_sysreg sr;
char *opname, *new_opname;
const char *sr_name;
int is_user, is_write;
bfd_boolean has_underbar = FALSE;
opname = *popname;
if (*opname == '_')
{
has_underbar = TRUE;
opname += 1;
}
is_user = (opname[1] == 'u');
is_write = (opname[0] == 'w');
if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
return -1;
sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
if (sr == XTENSA_UNDEFINED && is_write && !is_user
&& !strcasecmp (arg_strings[1], "intset"))
sr = xtensa_sysreg_lookup_name (isa, "interrupt");
if (sr == XTENSA_UNDEFINED
|| (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
{
offsetT val;
if (!xg_arg_is_constant (arg_strings[1], &val))
{
as_bad (_("invalid register '%s' for '%s' instruction"),
arg_strings[1], opname);
return -1;
}
sr = xtensa_sysreg_lookup (isa, val, is_user);
if (sr == XTENSA_UNDEFINED)
{
as_bad (_("invalid register number (%ld) for '%s' instruction"),
(long) val, opname);
return -1;
}
}
free (arg_strings[1]);
arg_strings[1] = 0;
*pnum_args = 1;
sr_name = xtensa_sysreg_name (isa, sr);
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.%s", (has_underbar ? "_" : ""),
*popname, sr_name);
free (*popname);
*popname = new_opname;
return 0;
}
static int
xtensa_translate_old_userreg_ops (char **popname)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_sysreg sr;
char *opname, *new_opname;
const char *sr_name;
bfd_boolean has_underbar = FALSE;
opname = *popname;
if (opname[0] == '_')
{
has_underbar = TRUE;
opname += 1;
}
sr = xtensa_sysreg_lookup_name (isa, opname + 1);
if (sr != XTENSA_UNDEFINED)
{
static char namebuf[10];
sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
if (strcmp (namebuf, opname + 1) == 0)
return 0;
}
else
{
offsetT val;
char *end;
if (opname[1] != 'u' || opname[2] != 'r')
return 0;
val = strtoul (opname + 3, &end, 10);
if (*end != '\0')
return 0;
sr = xtensa_sysreg_lookup (isa, val, 1);
if (sr == XTENSA_UNDEFINED)
{
as_bad (_("invalid register number (%ld) for '%s'"),
(long) val, opname);
return -1;
}
}
sr_name = xtensa_sysreg_name (isa, sr);
new_opname = (char *) xmalloc (strlen (sr_name) + 6);
sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
opname[0], sr_name);
free (*popname);
*popname = new_opname;
return 0;
}
static int
xtensa_translate_zero_immed (char *old_op,
char *new_op,
char **popname,
int *pnum_args,
char **arg_strings)
{
char *opname;
offsetT val;
opname = *popname;
assert (opname[0] != '_');
if (strcmp (opname, old_op) != 0)
return 0;
if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
return -1;
if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
{
xg_replace_opname (popname, new_op);
free (arg_strings[1]);
arg_strings[1] = arg_strings[2];
arg_strings[2] = 0;
*pnum_args = 2;
}
return 0;
}
static int
xg_translate_idioms (char **popname, int *pnum_args, char **arg_strings)
{
char *opname = *popname;
bfd_boolean has_underbar = FALSE;
if (cur_vinsn.inside_bundle)
return 0;
if (*opname == '_')
{
has_underbar = TRUE;
opname += 1;
}
if (strcmp (opname, "mov") == 0)
{
if (use_transform () && !has_underbar && density_supported)
xg_replace_opname (popname, "mov.n");
else
{
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]);
*pnum_args = 3;
}
return 0;
}
if (strcmp (opname, "bbsi.l") == 0)
{
if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
return -1;
xg_replace_opname (popname, (has_underbar ? "_bbsi" : "bbsi"));
if (target_big_endian)
xg_reverse_shift_count (&arg_strings[1]);
return 0;
}
if (strcmp (opname, "bbci.l") == 0)
{
if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
return -1;
xg_replace_opname (popname, (has_underbar ? "_bbci" : "bbci"));
if (target_big_endian)
xg_reverse_shift_count (&arg_strings[1]);
return 0;
}
if (xtensa_nop_opcode == XTENSA_UNDEFINED
&& strcmp (opname, "nop") == 0)
{
if (use_transform () && !has_underbar && density_supported)
xg_replace_opname (popname, "nop.n");
else
{
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");
*pnum_args = 3;
}
return 0;
}
if ((((opname[0] == 'r' || opname[0] == 'w')
&& (opname[1] == 'u' || opname[1] == 's'))
|| (opname[0] == 'x' && opname[1] == 's'))
&& opname[2] == 'r'
&& opname[3] == '\0')
return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
if ((opname[0] == 'r' || opname[0] == 'w')
&& xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
return xtensa_translate_old_userreg_ops (popname);
if (!has_underbar && use_transform ())
{
if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
pnum_args, arg_strings))
return -1;
if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
pnum_args, arg_strings))
return -1;
if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
pnum_args, arg_strings))
return -1;
if (xtensa_translate_zero_immed ("blti", "bltz", popname,
pnum_args, arg_strings))
return -1;
}
return 0;
}
static int
get_relaxable_immed (xtensa_opcode opcode)
{
int last_immed = -1;
int noperands, opi;
if (opcode == XTENSA_UNDEFINED)
return -1;
noperands = xtensa_opcode_num_operands (xtensa_default_isa, opcode);
for (opi = noperands - 1; opi >= 0; opi--)
{
if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
continue;
if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
return opi;
if (last_immed == -1
&& xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
last_immed = opi;
}
return last_immed;
}
static xtensa_opcode
get_opcode_from_buf (const char *buf, int slot)
{
static xtensa_insnbuf insnbuf = NULL;
static xtensa_insnbuf slotbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
xtensa_format fmt;
if (!insnbuf)
{
insnbuf = xtensa_insnbuf_alloc (isa);
slotbuf = xtensa_insnbuf_alloc (isa);
}
xtensa_insnbuf_from_chars (isa, insnbuf, (const unsigned char *) buf, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
return XTENSA_UNDEFINED;
if (slot >= xtensa_format_num_slots (isa, fmt))
return XTENSA_UNDEFINED;
xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
}
#ifdef TENSILICA_DEBUG
static void
xtensa_print_insn_table (void)
{
int num_opcodes, num_operands;
xtensa_opcode opcode;
xtensa_isa isa = xtensa_default_isa;
num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
for (opcode = 0; opcode < num_opcodes; opcode++)
{
int opn;
fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
num_operands = xtensa_opcode_num_operands (isa, opcode);
for (opn = 0; opn < num_operands; opn++)
{
if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
continue;
if (xtensa_operand_is_register (isa, opcode, opn) == 1)
{
xtensa_regfile opnd_rf =
xtensa_operand_regfile (isa, opcode, opn);
fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
}
else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
fputs ("[lLr] ", stderr);
else
fputs ("i ", stderr);
}
fprintf (stderr, "\n");
}
}
static void
print_vliw_insn (xtensa_insnbuf vbuf)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_format f = xtensa_format_decode (isa, vbuf);
xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
int op;
fprintf (stderr, "format = %d\n", f);
for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
{
xtensa_opcode opcode;
const char *opname;
int operands;
xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
opcode = xtensa_opcode_decode (isa, f, op, sbuf);
opname = xtensa_opcode_name (isa, opcode);
fprintf (stderr, "op in slot %i is %s;\n", op, opname);
fprintf (stderr, " operands = ");
for (operands = 0;
operands < xtensa_opcode_num_operands (isa, opcode);
operands++)
{
unsigned int val;
if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
continue;
xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
xtensa_operand_decode (isa, opcode, operands, &val);
fprintf (stderr, "%d ", val);
}
fprintf (stderr, "\n");
}
xtensa_insnbuf_free (isa, sbuf);
}
#endif
static bfd_boolean
is_direct_call_opcode (xtensa_opcode opcode)
{
xtensa_isa isa = xtensa_default_isa;
int n, num_operands;
if (xtensa_opcode_is_call (isa, opcode) == 0)
return FALSE;
num_operands = xtensa_opcode_num_operands (isa, opcode);
for (n = 0; n < num_operands; n++)
{
if (xtensa_operand_is_register (isa, opcode, n) == 0
&& xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
return TRUE;
}
return FALSE;
}
static int
decode_reloc (bfd_reloc_code_real_type reloc, int *slot, bfd_boolean *is_alt)
{
if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
&& reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
{
*slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
*is_alt = FALSE;
}
else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
&& reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
{
*slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
*is_alt = TRUE;
}
else
return -1;
return 0;
}
static bfd_reloc_code_real_type
encode_reloc (int slot)
{
if (slot < 0 || slot > 14)
return BFD_RELOC_NONE;
return BFD_RELOC_XTENSA_SLOT0_OP + slot;
}
static bfd_reloc_code_real_type
encode_alt_reloc (int slot)
{
if (slot < 0 || slot > 14)
return BFD_RELOC_NONE;
return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
}
static void
xtensa_insnbuf_set_operand (xtensa_insnbuf slotbuf,
xtensa_format fmt,
int slot,
xtensa_opcode opcode,
int operand,
uint32 value,
const char *file,
unsigned int line)
{
uint32 valbuf = value;
if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
{
if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
== 1)
as_bad_where ((char *) file, line,
_("operand %u is out of range for '%s'"), value,
xtensa_opcode_name (xtensa_default_isa, opcode));
else
as_bad_where ((char *) file, line,
_("operand %u is invalid for '%s'"), value,
xtensa_opcode_name (xtensa_default_isa, opcode));
return;
}
xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
slotbuf, valbuf);
}
static uint32
xtensa_insnbuf_get_operand (xtensa_insnbuf slotbuf,
xtensa_format fmt,
int slot,
xtensa_opcode opcode,
int opnum)
{
uint32 val = 0;
(void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
fmt, slot, slotbuf, &val);
(void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
return val;
}
static bfd_boolean
xg_instruction_matches_option_term (TInsn *insn ATTRIBUTE_UNUSED,
const ReqOrOption *option)
{
if (strcmp (option->option_name, "realnop") == 0
|| strncmp (option->option_name, "IsaUse", 6) == 0)
{
return TRUE;
}
else
{
as_fatal (_("internal error: unknown option name '%s'"),
option->option_name);
}
}
static bfd_boolean
xg_instruction_matches_or_options (TInsn *insn,
const ReqOrOptionList *or_option)
{
const ReqOrOption *option;
for (option = or_option; option != NULL; option = option->next)
{
if (xg_instruction_matches_option_term (insn, option))
return TRUE;
}
return FALSE;
}
static bfd_boolean
xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
{
const ReqOption *req_options;
for (req_options = options;
req_options != NULL;
req_options = req_options->next)
{
if (!xg_instruction_matches_or_options (insn,
req_options->or_option_terms))
return FALSE;
}
return TRUE;
}
static bfd_boolean
xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
{
PreconditionList *condition_l;
if (rule->opcode != insn->opcode)
return FALSE;
for (condition_l = rule->conditions;
condition_l != NULL;
condition_l = condition_l->next)
{
expressionS *exp1;
expressionS *exp2;
Precondition *cond = condition_l->precond;
switch (cond->typ)
{
case OP_CONSTANT:
assert (cond->op_num < insn->ntok);
exp1 = &insn->tok[cond->op_num];
if (expr_is_const (exp1))
{
switch (cond->cmp)
{
case OP_EQUAL:
if (get_expr_const (exp1) != cond->op_data)
return FALSE;
break;
case OP_NOTEQUAL:
if (get_expr_const (exp1) == cond->op_data)
return FALSE;
break;
default:
return FALSE;
}
}
else if (expr_is_register (exp1))
{
switch (cond->cmp)
{
case OP_EQUAL:
if (get_expr_register (exp1) != cond->op_data)
return FALSE;
break;
case OP_NOTEQUAL:
if (get_expr_register (exp1) == cond->op_data)
return FALSE;
break;
default:
return FALSE;
}
}
else
return FALSE;
break;
case OP_OPERAND:
assert (cond->op_num < insn->ntok);
assert (cond->op_data < insn->ntok);
exp1 = &insn->tok[cond->op_num];
exp2 = &insn->tok[cond->op_data];
switch (cond->cmp)
{
case OP_EQUAL:
if (!expr_is_equal (exp1, exp2))
return FALSE;
break;
case OP_NOTEQUAL:
if (expr_is_equal (exp1, exp2))
return FALSE;
break;
}
break;
case OP_LITERAL:
case OP_LABEL:
default:
return FALSE;
}
}
if (!xg_instruction_matches_options (insn, rule->options))
return FALSE;
return TRUE;
}
static int
transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
{
bfd_boolean a_greater = FALSE;
bfd_boolean b_greater = FALSE;
ReqOptionList *l_a = a->options;
ReqOptionList *l_b = b->options;
while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
{
ReqOrOptionList *l_or_a = l_a->or_option_terms;
ReqOrOptionList *l_or_b = l_b->or_option_terms;
while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
{
if (l_or_a->is_true != l_or_b->is_true)
return 0;
if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
{
if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
&& strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
{
if (prefer_const16)
a_greater = TRUE;
else
b_greater = TRUE;
}
else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
&& strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
{
if (prefer_const16)
b_greater = TRUE;
else
a_greater = TRUE;
}
else
return 0;
}
l_or_a = l_or_a->next;
l_or_b = l_or_b->next;
}
if (l_or_a || l_or_b)
return 0;
l_a = l_a->next;
l_b = l_b->next;
}
if (l_a || l_b)
return 0;
if (a_greater && b_greater)
return 0;
if (b_greater)
return 1;
if (a_greater)
return -1;
return 0;
}
static TransitionRule *
xg_instruction_match (TInsn *insn)
{
TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
if (xg_instruction_matches_rule (insn, rule))
return rule;
}
return NULL;
}
static bfd_boolean
is_unique_insn_expansion (TransitionRule *r)
{
if (!r->to_instr || r->to_instr->next != NULL)
return FALSE;
if (r->to_instr->typ != INSTR_INSTR)
return FALSE;
return TRUE;
}
static int
xg_get_build_instr_size (BuildInstr *insn)
{
assert (insn->typ == INSTR_INSTR);
return xg_get_single_size (insn->opcode);
}
static bfd_boolean
xg_is_narrow_insn (TInsn *insn)
{
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int num_match = 0;
assert (insn->insn_type == ITYPE_INSN);
assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
if (xg_instruction_matches_rule (insn, rule)
&& is_unique_insn_expansion (rule))
{
assert (insn->insn_type == ITYPE_INSN);
if (xg_get_single_size (insn->opcode)
< xg_get_build_instr_size (rule->to_instr))
{
num_match++;
if (num_match > 1)
return FALSE;
}
}
}
return (num_match == 1);
}
static bfd_boolean
xg_is_single_relaxable_insn (TInsn *insn)
{
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int num_match = 0;
assert (insn->insn_type == ITYPE_INSN);
assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
if (xg_instruction_matches_rule (insn, rule)
&& is_unique_insn_expansion (rule))
{
assert (insn->insn_type == ITYPE_INSN);
if (xg_get_single_size (insn->opcode)
<= xg_get_build_instr_size (rule->to_instr))
{
num_match++;
if (num_match > 1)
return FALSE;
}
}
}
return (num_match == 1);
}
static int
xg_get_max_insn_widen_size (xtensa_opcode opcode)
{
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int max_size = xg_get_single_size (opcode);
assert (opcode < table->num_opcodes);
for (l = table->table[opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
BuildInstr *build_list;
int this_size = 0;
if (!rule)
continue;
build_list = rule->to_instr;
if (is_unique_insn_expansion (rule))
{
assert (build_list->typ == INSTR_INSTR);
this_size = xg_get_max_insn_widen_size (build_list->opcode);
}
else
for (; build_list != NULL; build_list = build_list->next)
{
switch (build_list->typ)
{
case INSTR_INSTR:
this_size += xg_get_single_size (build_list->opcode);
break;
case INSTR_LITERAL_DEF:
case INSTR_LABEL_DEF:
default:
break;
}
}
if (this_size > max_size)
max_size = this_size;
}
return max_size;
}
static int
xg_get_max_insn_widen_literal_size (xtensa_opcode opcode)
{
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int max_size = 0;
assert (opcode < table->num_opcodes);
for (l = table->table[opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
BuildInstr *build_list;
int this_size = 0;
if (!rule)
continue;
build_list = rule->to_instr;
if (is_unique_insn_expansion (rule))
{
assert (build_list->typ == INSTR_INSTR);
this_size = xg_get_max_insn_widen_literal_size (build_list->opcode);
}
else
for (; build_list != NULL; build_list = build_list->next)
{
switch (build_list->typ)
{
case INSTR_LITERAL_DEF:
this_size += 4;
break;
case INSTR_INSTR:
case INSTR_LABEL_DEF:
default:
break;
}
}
if (this_size > max_size)
max_size = this_size;
}
return max_size;
}
static bfd_boolean
xg_is_relaxable_insn (TInsn *insn, int lateral_steps)
{
int steps_taken = 0;
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
if (xg_instruction_matches_rule (insn, rule))
{
if (steps_taken == lateral_steps)
return TRUE;
steps_taken++;
}
}
return FALSE;
}
static symbolS *
get_special_literal_symbol (void)
{
static symbolS *sym = NULL;
if (sym == NULL)
sym = symbol_find_or_make ("SPECIAL_LITERAL0\001");
return sym;
}
static symbolS *
get_special_label_symbol (void)
{
static symbolS *sym = NULL;
if (sym == NULL)
sym = symbol_find_or_make ("SPECIAL_LABEL0\001");
return sym;
}
static bfd_boolean
xg_valid_literal_expression (const expressionS *exp)
{
switch (exp->X_op)
{
case O_constant:
case O_symbol:
case O_big:
case O_uminus:
case O_subtract:
case O_pltrel:
return TRUE;
default:
return FALSE;
}
}
static bfd_boolean
xg_check_operand (int32 value, xtensa_opcode opcode, int operand)
{
uint32 valbuf = value;
if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
return TRUE;
return FALSE;
}
static bfd_boolean
xg_immeds_fit (const TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
int i;
int n = insn->ntok;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
const expressionS *expr = &insn->tok[i];
if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
continue;
switch (expr->X_op)
{
case O_register:
case O_constant:
if (xg_check_operand (expr->X_add_number, insn->opcode, i))
return FALSE;
break;
default:
assert (FALSE);
break;
}
}
return TRUE;
}
static bfd_boolean
xg_symbolic_immeds_fit (const TInsn *insn,
segT pc_seg,
fragS *pc_frag,
offsetT pc_offset,
long stretch)
{
xtensa_isa isa = xtensa_default_isa;
symbolS *symbolP;
fragS *sym_frag;
offsetT target, pc;
uint32 new_offset;
int i;
int n = insn->ntok;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
const expressionS *expr = &insn->tok[i];
if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
continue;
switch (expr->X_op)
{
case O_register:
case O_constant:
if (xg_check_operand (expr->X_add_number, insn->opcode, i))
return FALSE;
break;
case O_lo16:
case O_hi16:
if (xg_check_operand (0xffff, insn->opcode, i))
return FALSE;
break;
case O_symbol:
if (pc_frag == 0
|| xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0)
return FALSE;
if (S_IS_WEAK (expr->X_add_symbol))
return FALSE;
if (is_direct_call_opcode (insn->opcode)
&& ! pc_frag->tc_frag_data.use_longcalls)
{
if (S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
return TRUE;
}
if (S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
return FALSE;
symbolP = expr->X_add_symbol;
sym_frag = symbol_get_frag (symbolP);
target = S_GET_VALUE (symbolP) + expr->X_add_number;
pc = pc_frag->fr_address + pc_offset;
if (stretch != 0
&& sym_frag->relax_marker != pc_frag->relax_marker
&& S_GET_SEGMENT (symbolP) == pc_seg)
{
target += stretch;
}
new_offset = target;
xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
if (xg_check_operand (new_offset, insn->opcode, i))
return FALSE;
break;
default:
return FALSE;
}
}
return TRUE;
}
static bfd_boolean
xg_build_to_insn (TInsn *targ, TInsn *insn, BuildInstr *bi)
{
BuildOp *op;
symbolS *sym;
memset (targ, 0, sizeof (TInsn));
targ->loc = insn->loc;
switch (bi->typ)
{
case INSTR_INSTR:
op = bi->ops;
targ->opcode = bi->opcode;
targ->insn_type = ITYPE_INSN;
targ->is_specific_opcode = FALSE;
for (; op != NULL; op = op->next)
{
int op_num = op->op_num;
int op_data = op->op_data;
assert (op->op_num < MAX_INSN_ARGS);
if (targ->ntok <= op_num)
targ->ntok = op_num + 1;
switch (op->typ)
{
case OP_CONSTANT:
set_expr_const (&targ->tok[op_num], op_data);
break;
case OP_OPERAND:
assert (op_data < insn->ntok);
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
break;
case OP_LITERAL:
sym = get_special_literal_symbol ();
set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
break;
case OP_LABEL:
sym = get_special_label_symbol ();
set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
break;
case OP_OPERAND_HI16U:
case OP_OPERAND_LOW16U:
assert (op_data < insn->ntok);
if (expr_is_const (&insn->tok[op_data]))
{
long val;
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
val = xg_apply_userdef_op_fn (op->typ,
targ->tok[op_num].
X_add_number);
targ->tok[op_num].X_add_number = val;
}
else
{
if (targ->opcode == XTENSA_UNDEFINED
|| (targ->opcode != xtensa_const16_opcode))
return FALSE;
assert (op_data < insn->ntok);
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
if (targ->tok[op_num].X_op == O_symbol)
{
if (op->typ == OP_OPERAND_HI16U)
targ->tok[op_num].X_op = O_hi16;
else if (op->typ == OP_OPERAND_LOW16U)
targ->tok[op_num].X_op = O_lo16;
else
return FALSE;
}
}
break;
default:
if (xg_has_userdef_op_fn (op->typ))
{
assert (op_data < insn->ntok);
if (expr_is_const (&insn->tok[op_data]))
{
long val;
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
val = xg_apply_userdef_op_fn (op->typ,
targ->tok[op_num].
X_add_number);
targ->tok[op_num].X_add_number = val;
}
else
return FALSE;
break;
}
assert (0);
break;
}
}
break;
case INSTR_LITERAL_DEF:
op = bi->ops;
targ->opcode = XTENSA_UNDEFINED;
targ->insn_type = ITYPE_LITERAL;
targ->is_specific_opcode = FALSE;
for (; op != NULL; op = op->next)
{
int op_num = op->op_num;
int op_data = op->op_data;
assert (op->op_num < MAX_INSN_ARGS);
if (targ->ntok <= op_num)
targ->ntok = op_num + 1;
switch (op->typ)
{
case OP_OPERAND:
assert (op_data < insn->ntok);
if (!xg_valid_literal_expression (&insn->tok[op_data]))
return FALSE;
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
break;
case OP_LITERAL:
case OP_CONSTANT:
case OP_LABEL:
default:
assert (0);
break;
}
}
break;
case INSTR_LABEL_DEF:
op = bi->ops;
targ->opcode = XTENSA_UNDEFINED;
targ->insn_type = ITYPE_LABEL;
targ->is_specific_opcode = FALSE;
assert (op == NULL);
break;
default:
assert (0);
}
return TRUE;
}
static bfd_boolean
xg_build_to_stack (IStack *istack, TInsn *insn, BuildInstr *bi)
{
for (; bi != NULL; bi = bi->next)
{
TInsn *next_insn = istack_push_space (istack);
if (!xg_build_to_insn (next_insn, insn, bi))
return FALSE;
}
return TRUE;
}
static bfd_boolean
xg_expand_to_stack (IStack *istack, TInsn *insn, int lateral_steps)
{
int stack_size = istack->ninsn;
int steps_taken = 0;
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
if (xg_instruction_matches_rule (insn, rule))
{
if (lateral_steps == steps_taken)
{
int i;
if (!xg_build_to_stack (istack, insn, rule->to_instr))
return FALSE;
for (i = stack_size; i < istack->ninsn; i++)
{
TInsn *insn = &istack->insn[i];
if (insn->insn_type == ITYPE_INSN
&& !tinsn_has_symbolic_operands (insn)
&& !xg_immeds_fit (insn))
{
istack->ninsn = stack_size;
return FALSE;
}
}
return TRUE;
}
steps_taken++;
}
}
return FALSE;
}
static bfd_boolean
xg_expand_narrow (TInsn *targ, TInsn *insn)
{
TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
if (xg_instruction_matches_rule (insn, rule)
&& is_unique_insn_expansion (rule))
{
if (xg_get_single_size (insn->opcode)
<= xg_get_build_instr_size (rule->to_instr))
{
xg_build_to_insn (targ, insn, rule->to_instr);
return FALSE;
}
}
}
return TRUE;
}
static int
xg_assembly_relax (IStack *istack,
TInsn *insn,
segT pc_seg,
fragS *pc_frag,
offsetT pc_offset,
int min_steps,
long stretch)
{
int steps_taken = 0;
TInsn single_target;
TInsn current_insn;
int lateral_steps = 0;
int istack_size = istack->ninsn;
if (xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
&& steps_taken >= min_steps)
{
istack_push (istack, insn);
return steps_taken;
}
current_insn = *insn;
while (xg_is_single_relaxable_insn (¤t_insn))
{
int error_val = xg_expand_narrow (&single_target, ¤t_insn);
assert (!error_val);
if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
stretch))
{
steps_taken++;
if (steps_taken >= min_steps)
{
istack_push (istack, &single_target);
return steps_taken;
}
}
current_insn = single_target;
}
while (xg_is_relaxable_insn (¤t_insn, lateral_steps))
{
if (xg_symbolic_immeds_fit (¤t_insn, pc_seg, pc_frag, pc_offset,
stretch))
{
if (steps_taken >= min_steps)
{
istack_push (istack, ¤t_insn);
return steps_taken;
}
}
steps_taken++;
if (xg_expand_to_stack (istack, ¤t_insn, lateral_steps))
{
if (steps_taken >= min_steps)
return steps_taken;
}
lateral_steps++;
istack->ninsn = istack_size;
}
istack_push (istack, insn);
return steps_taken;
}
static void
xg_force_frag_space (int size)
{
frag_grow (size);
}
static void
xg_finish_frag (char *last_insn,
enum xtensa_relax_statesE frag_state,
enum xtensa_relax_statesE slot0_state,
int max_growth,
bfd_boolean is_insn)
{
fragS *old_frag;
xg_force_frag_space (max_growth);
old_frag = frag_now;
frag_now->fr_opcode = last_insn;
if (is_insn)
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, max_growth, max_growth,
frag_state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
old_frag->tc_frag_data.slot_subtypes[0] = slot0_state;
xtensa_set_frag_assembly_state (frag_now);
assert (old_frag->fr_next == frag_now);
}
static bfd_boolean
is_next_frag_target (const fragS *fragP, const fragS *target)
{
if (fragP == NULL)
return FALSE;
for (; fragP; fragP = fragP->fr_next)
{
if (fragP == target)
return TRUE;
if (fragP->fr_fix != 0)
return FALSE;
if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
return FALSE;
if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
&& ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
return FALSE;
if (fragP->fr_type == rs_space)
return FALSE;
}
return FALSE;
}
static bfd_boolean
is_branch_jmp_to_next (TInsn *insn, fragS *fragP)
{
xtensa_isa isa = xtensa_default_isa;
int i;
int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
int target_op = -1;
symbolS *sym;
fragS *target_frag;
if (xtensa_opcode_is_branch (isa, insn->opcode) == 0
&& xtensa_opcode_is_jump (isa, insn->opcode) == 0)
return FALSE;
for (i = 0; i < num_ops; i++)
{
if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
{
target_op = i;
break;
}
}
if (target_op == -1)
return FALSE;
if (insn->ntok <= target_op)
return FALSE;
if (insn->tok[target_op].X_op != O_symbol)
return FALSE;
sym = insn->tok[target_op].X_add_symbol;
if (sym == NULL)
return FALSE;
if (insn->tok[target_op].X_add_number != 0)
return FALSE;
target_frag = symbol_get_frag (sym);
if (target_frag == NULL)
return FALSE;
if (is_next_frag_target (fragP->fr_next, target_frag)
&& S_GET_VALUE (sym) == target_frag->fr_address)
return TRUE;
return FALSE;
}
static void
xg_add_branch_and_loop_targets (TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
{
int i = 1;
if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
&& insn->tok[i].X_op == O_symbol)
symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
return;
}
if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
|| xtensa_opcode_is_loop (isa, insn->opcode) == 1)
{
int i;
for (i = 0; i < insn->ntok && i < num_ops; i++)
{
if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
&& insn->tok[i].X_op == O_symbol)
{
symbolS *sym = insn->tok[i].X_add_symbol;
symbol_get_tc (sym)->is_branch_target = TRUE;
if (S_IS_DEFINED (sym))
symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
}
}
}
}
static bfd_boolean
xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
{
int num_ops = 0;
BuildOp *b_op;
switch (instr_spec->typ)
{
case INSTR_INSTR:
new_insn->insn_type = ITYPE_INSN;
new_insn->opcode = instr_spec->opcode;
new_insn->is_specific_opcode = FALSE;
new_insn->loc = old_insn->loc;
break;
case INSTR_LITERAL_DEF:
new_insn->insn_type = ITYPE_LITERAL;
new_insn->opcode = XTENSA_UNDEFINED;
new_insn->is_specific_opcode = FALSE;
new_insn->loc = old_insn->loc;
break;
case INSTR_LABEL_DEF:
as_bad (_("INSTR_LABEL_DEF not supported yet"));
break;
}
for (b_op = instr_spec->ops; b_op != NULL; b_op = b_op->next)
{
expressionS *exp;
const expressionS *src_exp;
num_ops++;
switch (b_op->typ)
{
case OP_CONSTANT:
assert (b_op->op_num < MAX_INSN_ARGS);
exp = &new_insn->tok[b_op->op_num];
set_expr_const (exp, b_op->op_data);
break;
case OP_OPERAND:
assert (b_op->op_num < MAX_INSN_ARGS);
assert (b_op->op_data < (unsigned) old_insn->ntok);
src_exp = &old_insn->tok[b_op->op_data];
exp = &new_insn->tok[b_op->op_num];
copy_expr (exp, src_exp);
break;
case OP_LITERAL:
case OP_LABEL:
as_bad (_("can't handle generation of literal/labels yet"));
assert (0);
default:
as_bad (_("can't handle undefined OP TYPE"));
assert (0);
}
}
new_insn->ntok = num_ops;
return FALSE;
}
static bfd_boolean
xg_simplify_insn (TInsn *old_insn, TInsn *new_insn)
{
TransitionRule *rule;
BuildInstr *insn_spec;
if (old_insn->is_specific_opcode || !density_supported)
return FALSE;
rule = xg_instruction_match (old_insn);
if (rule == NULL)
return FALSE;
insn_spec = rule->to_instr;
assert (insn_spec != NULL);
assert (insn_spec->next == NULL);
if (insn_spec->next != NULL)
return FALSE;
xg_build_token_insn (insn_spec, old_insn, new_insn);
return TRUE;
}
static bfd_boolean
xg_expand_assembly_insn (IStack *istack, TInsn *orig_insn)
{
int noperands;
TInsn new_insn;
bfd_boolean do_expand;
memset (&new_insn, 0, sizeof (TInsn));
if (xg_simplify_insn (orig_insn, &new_insn))
orig_insn = &new_insn;
noperands = xtensa_opcode_num_operands (xtensa_default_isa,
orig_insn->opcode);
if (orig_insn->ntok < noperands)
{
as_bad (_("found %d operands for '%s': Expected %d"),
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"),
orig_insn->ntok,
xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
noperands);
orig_insn->ntok = noperands;
if (tinsn_has_invalid_symbolic_operands (orig_insn))
return TRUE;
do_expand = (!orig_insn->is_specific_opcode && use_transform ());
if (is_direct_call_opcode (orig_insn->opcode))
do_expand = FALSE;
if (tinsn_has_symbolic_operands (orig_insn))
{
if (!tinsn_has_complex_operands (orig_insn))
do_expand = FALSE;
}
else if (xg_immeds_fit (orig_insn))
do_expand = FALSE;
if (do_expand)
xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
else
istack_push (istack, orig_insn);
return FALSE;
}
static bfd_boolean
get_is_linkonce_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec)
{
flagword flags, link_once_flags;
flags = bfd_get_section_flags (abfd, sec);
link_once_flags = (flags & SEC_LINK_ONCE);
if (!link_once_flags)
{
static size_t len = sizeof ".gnu.linkonce.t.";
if (strncmp (segment_name (sec), ".gnu.linkonce.t.", len - 1) == 0)
link_once_flags = SEC_LINK_ONCE;
}
return (link_once_flags != 0);
}
static void
xtensa_add_literal_sym (symbolS *sym)
{
sym_list *l;
l = (sym_list *) xmalloc (sizeof (sym_list));
l->sym = sym;
l->next = literal_syms;
literal_syms = l;
}
static symbolS *
xtensa_create_literal_symbol (segT sec, fragS *frag)
{
static int lit_num = 0;
static char name[256];
symbolS *symbolP;
sprintf (name, ".L_lit_sym%d", lit_num);
if (get_is_linkonce_section (stdoutput, sec))
{
symbolP = symbol_new (name, sec, 0, frag);
S_CLEAR_EXTERNAL (symbolP);
}
else
symbolP = symbol_new (name, sec, 0, frag);
xtensa_add_literal_sym (symbolP);
frag->tc_frag_data.is_literal = TRUE;
lit_num++;
return symbolP;
}
static symbolS *
xg_assemble_literal ( TInsn *insn)
{
emit_state state;
symbolS *lit_sym = NULL;
offsetT litsize = 4;
offsetT litalign = 2;
expressionS saved_loc;
expressionS * emit_val;
set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
assert (insn->insn_type == ITYPE_LITERAL);
assert (insn->ntok == 1);
xtensa_switch_to_literal_fragment (&state);
emit_val = &insn->tok[0];
if (emit_val->X_op == O_big)
{
int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
if (size > litsize)
{
as_bad_where (frag_now->fr_file, frag_now->fr_line,
_("invalid immediate"));
xtensa_restore_emit_state (&state);
return NULL;
}
}
frag_align (litalign, 0, 0);
record_alignment (now_seg, litalign);
if (emit_val->X_op == O_pltrel)
{
char *p = frag_more (litsize);
xtensa_set_frag_assembly_state (frag_now);
if (emit_val->X_add_symbol)
emit_val->X_op = O_symbol;
else
emit_val->X_op = O_constant;
fix_new_exp (frag_now, p - frag_now->fr_literal,
litsize, emit_val, 0, BFD_RELOC_XTENSA_PLT);
}
else
emit_expr (emit_val, litsize);
assert (frag_now->tc_frag_data.literal_frag == NULL);
frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
lit_sym = frag_now->fr_symbol;
frag_now->tc_frag_data.is_literal = TRUE;
xtensa_restore_emit_state (&state);
return lit_sym;
}
static void
xg_assemble_literal_space ( int size, int slot)
{
emit_state state;
offsetT litalign = 2;
fragS *lit_saved_frag;
assert (size % 4 == 0);
xtensa_switch_to_literal_fragment (&state);
frag_align (litalign, 0, 0);
record_alignment (now_seg, litalign);
xg_force_frag_space (size);
lit_saved_frag = frag_now;
frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
frag_now->tc_frag_data.is_literal = TRUE;
frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
xtensa_restore_emit_state (&state);
frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
}
static bfd_boolean
xg_add_opcode_fix (TInsn *tinsn,
int opnum,
xtensa_format fmt,
int slot,
expressionS *expr,
fragS *fragP,
offsetT offset)
{
xtensa_opcode opcode = tinsn->opcode;
bfd_reloc_code_real_type reloc;
reloc_howto_type *howto;
int fmt_length;
fixS *the_fix;
reloc = BFD_RELOC_NONE;
if (opcode == xtensa_l32r_opcode)
{
if (fragP->tc_frag_data.use_absolute_literals)
reloc = encode_alt_reloc (slot);
}
else if (opcode == xtensa_const16_opcode)
{
if (expr->X_op == O_lo16)
{
reloc = encode_reloc (slot);
expr->X_op = O_symbol;
}
else if (expr->X_op == O_hi16)
{
reloc = encode_alt_reloc (slot);
expr->X_op = O_symbol;
}
}
if (opnum != get_relaxable_immed (opcode))
{
as_bad (_("invalid relocation for operand %i of '%s'"),
opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
return FALSE;
}
if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
{
as_bad (_("invalid expression for operand %i of '%s'"),
opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
return FALSE;
}
if (reloc == BFD_RELOC_NONE)
reloc = encode_reloc (slot);
if (reloc == BFD_RELOC_NONE)
{
as_bad (_("invalid relocation in instruction slot %i"), slot);
return FALSE;
}
howto = bfd_reloc_type_lookup (stdoutput, reloc);
if (!howto)
{
as_bad (_("undefined symbol for opcode \"%s\""),
xtensa_opcode_name (xtensa_default_isa, opcode));
return FALSE;
}
fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
the_fix = fix_new_exp (fragP, offset, fmt_length, expr,
howto->pc_relative, reloc);
the_fix->fx_no_overflow = 1;
if (expr->X_add_symbol
&& (S_IS_EXTERNAL (expr->X_add_symbol)
|| S_IS_WEAK (expr->X_add_symbol)))
the_fix->fx_plt = TRUE;
the_fix->tc_fix_data.X_add_symbol = expr->X_add_symbol;
the_fix->tc_fix_data.X_add_number = expr->X_add_number;
the_fix->tc_fix_data.slot = slot;
return TRUE;
}
static bfd_boolean
xg_emit_insn_to_buf (TInsn *tinsn,
xtensa_format fmt,
char *buf,
fragS *fragP,
offsetT offset,
bfd_boolean build_fix)
{
static xtensa_insnbuf insnbuf = NULL;
bfd_boolean has_symbolic_immed = FALSE;
bfd_boolean ok = TRUE;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
if (has_symbolic_immed && build_fix)
{
int opnum = get_relaxable_immed (tinsn->opcode);
expressionS *exp = &tinsn->tok[opnum];
if (!xg_add_opcode_fix (tinsn, opnum, fmt, 0, exp, fragP, offset))
ok = FALSE;
}
fragP->tc_frag_data.is_insn = TRUE;
xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
(unsigned char *) buf, 0);
return ok;
}
static void
xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
{
symbolS *sym = get_special_literal_symbol ();
int i;
if (lit_sym == 0)
return;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < insn->ntok; i++)
if (insn->tok[i].X_add_symbol == sym)
insn->tok[i].X_add_symbol = lit_sym;
}
static void
xg_resolve_labels (TInsn *insn, symbolS *label_sym)
{
symbolS *sym = get_special_label_symbol ();
int i;
for (i = 0; i < insn->ntok; i++)
if (insn->tok[i].X_add_symbol == sym)
insn->tok[i].X_add_symbol = label_sym;
}
static bfd_boolean
is_register_writer (const TInsn *insn, const char *regset, int regnum)
{
int i;
int num_ops;
xtensa_isa isa = xtensa_default_isa;
num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
for (i = 0; i < num_ops; i++)
{
char inout;
inout = xtensa_operand_inout (isa, insn->opcode, i);
if ((inout == 'o' || inout == 'm')
&& xtensa_operand_is_register (isa, insn->opcode, i) == 1)
{
xtensa_regfile opnd_rf =
xtensa_operand_regfile (isa, insn->opcode, i);
if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
{
if ((insn->tok[i].X_op == O_register)
&& (insn->tok[i].X_add_number == regnum))
return TRUE;
}
}
}
return FALSE;
}
static bfd_boolean
is_bad_loopend_opcode (const TInsn *tinsn)
{
xtensa_opcode opcode = tinsn->opcode;
if (opcode == XTENSA_UNDEFINED)
return FALSE;
if (opcode == xtensa_call0_opcode
|| opcode == xtensa_callx0_opcode
|| opcode == xtensa_call4_opcode
|| opcode == xtensa_callx4_opcode
|| opcode == xtensa_call8_opcode
|| opcode == xtensa_callx8_opcode
|| opcode == xtensa_call12_opcode
|| opcode == xtensa_callx12_opcode
|| opcode == xtensa_isync_opcode
|| opcode == xtensa_ret_opcode
|| opcode == xtensa_ret_n_opcode
|| opcode == xtensa_retw_opcode
|| opcode == xtensa_retw_n_opcode
|| opcode == xtensa_waiti_opcode
|| opcode == xtensa_rsr_lcount_opcode)
return TRUE;
return FALSE;
}
static bfd_boolean
is_unaligned_label (symbolS *sym)
{
const char *name = S_GET_NAME (sym);
static size_t fake_size = 0;
if (name
&& name[0] == '.'
&& name[1] == 'L' && (name[2] == 'n' || name[2] == 'M'))
return TRUE;
if (fake_size == 0)
fake_size = strlen (FAKE_LABEL_NAME);
if (name
&& strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
&& (name[fake_size] == 'F'
|| name[fake_size] == 'L'
|| (name[fake_size] == 'e'
&& strncmp ("endfunc", name+fake_size, 7) == 0)))
return TRUE;
return FALSE;
}
static fragS *
next_non_empty_frag (const fragS *fragP)
{
fragS *next_fragP = fragP->fr_next;
while (next_fragP && next_fragP->fr_fix == 0)
next_fragP = next_fragP->fr_next;
if (next_fragP == NULL || next_fragP->fr_fix == 0)
return NULL;
return next_fragP;
}
static bfd_boolean
next_frag_opcode_is_loop (const fragS *fragP, xtensa_opcode *opcode)
{
xtensa_opcode out_opcode;
const fragS *next_fragP = next_non_empty_frag (fragP);
if (next_fragP == NULL)
return FALSE;
out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
{
*opcode = out_opcode;
return TRUE;
}
return FALSE;
}
static int
frag_format_size (const fragS *fragP)
{
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
xtensa_format fmt;
int fmt_size;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
if (fragP == NULL)
return XTENSA_UNDEFINED;
xtensa_insnbuf_from_chars (isa, insnbuf,
(unsigned char *) fragP->fr_literal, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
return XTENSA_UNDEFINED;
fmt_size = xtensa_format_length (isa, fmt);
if (fragP->fr_opcode != fragP->fr_literal)
return fmt_size;
if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
return fmt_size;
if (fragP->fr_type != rs_machine_dependent)
return fmt_size;
if (fragP->fr_subtype != RELAX_SLOTS)
return fmt_size;
if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
|| fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2)
return 3;
if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
return 2 + fragP->tc_frag_data.text_expansion[0];
return fmt_size;
}
static int
next_frag_format_size (const fragS *fragP)
{
const fragS *next_fragP = next_non_empty_frag (fragP);
return frag_format_size (next_fragP);
}
static void
update_next_frag_state (fragS *fragP)
{
fragS *next_fragP = fragP->fr_next;
fragS *new_target = NULL;
if (align_targets)
{
while (!(next_fragP->fr_type == rs_machine_dependent
&& (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
|| next_fragP->fr_subtype == RELAX_UNREACHABLE)))
next_fragP = next_fragP->fr_next;
assert (next_fragP->fr_type == rs_machine_dependent
&& (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
|| next_fragP->fr_subtype == RELAX_UNREACHABLE));
new_target = next_fragP->fr_next;
while (!(new_target->fr_type == rs_machine_dependent
&& (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
|| new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
new_target = new_target->fr_next;
assert (new_target->fr_type == rs_machine_dependent
&& (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
|| new_target->fr_subtype == RELAX_DESIRE_ALIGN));
}
while (next_fragP && next_fragP->fr_fix == 0)
{
if (next_fragP->fr_type == rs_machine_dependent
&& next_fragP->fr_subtype == RELAX_LOOP_END)
{
next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
return;
}
next_fragP = next_fragP->fr_next;
}
}
static bfd_boolean
next_frag_is_branch_target (const fragS *fragP)
{
for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
{
if (fragP->tc_frag_data.is_branch_target)
return TRUE;
if (fragP->fr_fix != 0)
break;
}
return FALSE;
}
static bfd_boolean
next_frag_is_loop_target (const fragS *fragP)
{
for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
{
if (fragP->tc_frag_data.is_loop_target)
return TRUE;
if (fragP->fr_fix != 0)
break;
}
return FALSE;
}
static addressT
next_frag_pre_opcode_bytes (const fragS *fragp)
{
const fragS *next_fragp = fragp->fr_next;
xtensa_opcode next_opcode;
if (!next_frag_opcode_is_loop (fragp, &next_opcode))
return 0;
while (next_fragp->fr_fix == 0)
next_fragp = next_fragp->fr_next;
if (next_fragp->fr_type != rs_machine_dependent)
return 0;
if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
return get_expanded_loop_offset (next_opcode);
return 0;
}
static void
xtensa_mark_literal_pool_location (void)
{
emit_state s;
fragS *pool_location;
if (use_literal_section && !directive_state[directive_absolute_literals])
return;
frag_align (2, 0, 0);
record_alignment (now_seg, 2);
pool_location = frag_now;
frag_now->tc_frag_data.lit_frchain = frchain_now;
frag_variant (rs_machine_dependent, 0, 0,
RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
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);
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);
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 void
build_nop (TInsn *tinsn, int size)
{
tinsn_init (tinsn);
switch (size)
{
case 2:
tinsn->opcode = xtensa_nop_n_opcode;
tinsn->ntok = 0;
if (tinsn->opcode == XTENSA_UNDEFINED)
as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
break;
case 3:
if (xtensa_nop_opcode == XTENSA_UNDEFINED)
{
tinsn->opcode = xtensa_or_opcode;
set_expr_const (&tinsn->tok[0], 1);
set_expr_const (&tinsn->tok[1], 1);
set_expr_const (&tinsn->tok[2], 1);
tinsn->ntok = 3;
}
else
tinsn->opcode = xtensa_nop_opcode;
assert (tinsn->opcode != XTENSA_UNDEFINED);
}
}
static void
assemble_nop (int size, char *buf)
{
static xtensa_insnbuf insnbuf = NULL;
TInsn tinsn;
build_nop (&tinsn, size);
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_to_insnbuf (&tinsn, insnbuf);
xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
(unsigned char *) buf, 0);
}
static addressT
get_expanded_loop_offset (xtensa_opcode opcode)
{
assert (opcode != XTENSA_UNDEFINED);
if (opcode == xtensa_loop_opcode)
return 0;
if (opcode == xtensa_loopnez_opcode)
return 3;
if (opcode == xtensa_loopgtz_opcode)
return 6;
as_fatal (_("get_expanded_loop_offset: invalid opcode"));
return 0;
}
static fragS *
get_literal_pool_location (segT seg)
{
return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
}
static void
set_literal_pool_location (segT seg, fragS *literal_pool_loc)
{
seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
}
static void
xtensa_set_frag_assembly_state (fragS *fragP)
{
if (!density_supported)
fragP->tc_frag_data.is_no_density = TRUE;
if (!directive_state[directive_transform])
fragP->tc_frag_data.is_no_transform = TRUE;
if (directive_state[directive_longcalls])
fragP->tc_frag_data.use_longcalls = TRUE;
fragP->tc_frag_data.use_absolute_literals =
directive_state[directive_absolute_literals];
fragP->tc_frag_data.is_assembly_state_set = TRUE;
}
static bfd_boolean
relaxable_section (asection *sec)
{
return (sec->flags & SEC_DEBUGGING) == 0;
}
static void
xtensa_find_unmarked_state_frags (void)
{
segT *seclist;
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segT sec = *seclist;
segment_info_type *seginfo;
fragS *fragP;
flagword flags;
flags = bfd_get_section_flags (stdoutput, sec);
if (flags & SEC_DEBUGGING)
continue;
if (!(flags & SEC_ALLOC))
continue;
seginfo = seg_info (sec);
if (seginfo && seginfo->frchainP)
{
fragS *last_fragP = 0;
for (fragP = seginfo->frchainP->frch_root; fragP;
fragP = fragP->fr_next)
{
if (fragP->fr_fix != 0
&& !fragP->tc_frag_data.is_assembly_state_set)
{
if (last_fragP == 0)
{
as_warn_where (fragP->fr_file, fragP->fr_line,
_("assembly state not set for first frag in section %s"),
sec->name);
}
else
{
fragP->tc_frag_data.is_assembly_state_set = TRUE;
fragP->tc_frag_data.is_no_density =
last_fragP->tc_frag_data.is_no_density;
fragP->tc_frag_data.is_no_transform =
last_fragP->tc_frag_data.is_no_transform;
fragP->tc_frag_data.use_longcalls =
last_fragP->tc_frag_data.use_longcalls;
fragP->tc_frag_data.use_absolute_literals =
last_fragP->tc_frag_data.use_absolute_literals;
}
}
if (fragP->tc_frag_data.is_assembly_state_set)
last_fragP = fragP;
}
}
}
}
static void
xtensa_find_unaligned_branch_targets (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec,
void *unused ATTRIBUTE_UNUSED)
{
flagword flags = bfd_get_section_flags (abfd, sec);
segment_info_type *seginfo = seg_info (sec);
fragS *frag = seginfo->frchainP->frch_root;
if (flags & SEC_CODE)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
while (frag != NULL)
{
if (frag->tc_frag_data.is_branch_target)
{
int op_size;
addressT branch_align, frag_addr;
xtensa_format fmt;
xtensa_insnbuf_from_chars
(isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
fmt = xtensa_format_decode (isa, insnbuf);
op_size = xtensa_format_length (isa, fmt);
branch_align = 1 << branch_align_power (sec);
frag_addr = frag->fr_address % branch_align;
if (frag_addr + op_size > branch_align)
as_warn_where (frag->fr_file, frag->fr_line,
_("unaligned branch target: %d bytes at 0x%lx"),
op_size, (long) frag->fr_address);
}
frag = frag->fr_next;
}
xtensa_insnbuf_free (isa, insnbuf);
}
}
static void
xtensa_find_unaligned_loops (bfd *abfd ATTRIBUTE_UNUSED,
asection *sec,
void *unused ATTRIBUTE_UNUSED)
{
flagword flags = bfd_get_section_flags (abfd, sec);
segment_info_type *seginfo = seg_info (sec);
fragS *frag = seginfo->frchainP->frch_root;
xtensa_isa isa = xtensa_default_isa;
if (flags & SEC_CODE)
{
xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
while (frag != NULL)
{
if (frag->tc_frag_data.is_first_loop_insn)
{
int op_size;
addressT frag_addr;
xtensa_format fmt;
xtensa_insnbuf_from_chars
(isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
fmt = xtensa_format_decode (isa, insnbuf);
op_size = xtensa_format_length (isa, fmt);
frag_addr = frag->fr_address % xtensa_fetch_width;
if (frag_addr + op_size > xtensa_fetch_width)
as_warn_where (frag->fr_file, frag->fr_line,
_("unaligned loop: %d bytes at 0x%lx"),
op_size, (long) frag->fr_address);
}
frag = frag->fr_next;
}
xtensa_insnbuf_free (isa, insnbuf);
}
}
static int
xg_apply_fix_value (fixS *fixP, valueT val)
{
xtensa_isa isa = xtensa_default_isa;
static xtensa_insnbuf insnbuf = NULL;
static xtensa_insnbuf slotbuf = NULL;
xtensa_format fmt;
int slot;
bfd_boolean alt_reloc;
xtensa_opcode opcode;
char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
(void) decode_reloc (fixP->fx_r_type, &slot, &alt_reloc);
if (alt_reloc)
as_fatal (_("unexpected fix"));
if (!insnbuf)
{
insnbuf = xtensa_insnbuf_alloc (isa);
slotbuf = xtensa_insnbuf_alloc (isa);
}
xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
as_fatal (_("undecodable fix"));
xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
if (opcode == XTENSA_UNDEFINED)
as_fatal (_("undecodable fix"));
if (opcode == xtensa_const16_opcode)
return 0;
xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
get_relaxable_immed (opcode), val,
fixP->fx_file, fixP->fx_line);
xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
xtensa_insnbuf_to_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
return 1;
}
const char *
xtensa_target_format (void)
{
return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
}
void
xtensa_file_arch_init (bfd *abfd)
{
bfd_set_private_flags (abfd, 0x100 | 0x200);
}
void
md_number_to_chars (char *buf, valueT val, int n)
{
if (target_big_endian)
number_to_chars_bigendian (buf, val, n);
else
number_to_chars_littleendian (buf, val, n);
}
void
md_begin (void)
{
segT current_section = now_seg;
int current_subsec = now_subseg;
xtensa_isa isa;
xtensa_default_isa = xtensa_isa_init (0, 0);
isa = xtensa_default_isa;
linkrelax = 1;
memset (&default_lit_sections, 0, sizeof (default_lit_sections));
default_lit_sections.init_lit_seg_name = INIT_LITERAL_SECTION_NAME;
default_lit_sections.fini_lit_seg_name = FINI_LITERAL_SECTION_NAME;
default_lit_sections.lit_seg_name = LITERAL_SECTION_NAME;
default_lit_sections.lit4_seg_name = LIT4_SECTION_NAME;
subseg_set (current_section, current_subsec);
xg_init_vinsn (&cur_vinsn);
xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
xtensa_call4_opcode = xtensa_opcode_lookup (isa, "call4");
xtensa_call8_opcode = xtensa_opcode_lookup (isa, "call8");
xtensa_call12_opcode = xtensa_opcode_lookup (isa, "call12");
xtensa_callx0_opcode = xtensa_opcode_lookup (isa, "callx0");
xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
init_op_placement_info_table ();
if (!frag_now->tc_frag_data.is_assembly_state_set)
xtensa_set_frag_assembly_state (frag_now);
}
void
xtensa_init_fix_data (fixS *x)
{
x->tc_fix_data.slot = 0;
x->tc_fix_data.X_add_symbol = NULL;
x->tc_fix_data.X_add_number = 0;
}
void
xtensa_frob_label (symbolS *sym)
{
float freq = get_subseg_target_freq (now_seg, now_subseg);
symbol_set_frag (sym, frag_now);
S_SET_VALUE (sym, (valueT) frag_now_fix ());
if (generating_literals)
xtensa_add_literal_sym (sym);
else
xtensa_add_insn_label (sym);
if (symbol_get_tc (sym)->is_loop_target)
{
if ((get_last_insn_flags (now_seg, now_subseg)
& FLAG_IS_BAD_LOOPEND) != 0)
as_bad (_("invalid last instruction for a zero-overhead loop"));
xtensa_set_frag_assembly_state (frag_now);
frag_var (rs_machine_dependent, 4, 4, RELAX_LOOP_END,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
xtensa_move_labels (frag_now, 0, TRUE);
}
if (now_seg != absolute_section
&& do_align_targets ()
&& !is_unaligned_label (sym)
&& !generating_literals)
{
xtensa_set_frag_assembly_state (frag_now);
frag_var (rs_machine_dependent,
0, (int) freq,
RELAX_DESIRE_ALIGN_IF_TARGET,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
xtensa_move_labels (frag_now, 0, TRUE);
}
if (symbol_get_tc (sym)->is_branch_target)
symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
if (symbol_get_tc (sym)->is_loop_target)
symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
}
int
xtensa_unrecognized_line (int ch)
{
switch (ch)
{
case '{' :
if (cur_vinsn.inside_bundle == 0)
{
generate_lineno_debug ();
if (debug_type == DEBUG_STABS)
{
xt_saved_debug_type = debug_type;
debug_type = DEBUG_NONE;
}
cur_vinsn.inside_bundle = 1;
}
else
{
as_bad (_("extra opening brace"));
return 0;
}
break;
case '}' :
if (cur_vinsn.inside_bundle)
finish_vinsn (&cur_vinsn);
else
{
as_bad (_("extra closing brace"));
return 0;
}
break;
default:
as_bad (_("syntax error"));
return 0;
}
return 1;
}
void
xtensa_flush_pending_output (void)
{
if (cur_vinsn.inside_bundle)
as_bad (_("missing closing brace"));
if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
{
frag_wane (frag_now);
frag_new (0);
xtensa_set_frag_assembly_state (frag_now);
}
frag_now->tc_frag_data.is_insn = FALSE;
xtensa_clear_insn_labels ();
}
static void
error_reset_cur_vinsn (void)
{
if (cur_vinsn.inside_bundle)
{
if (*input_line_pointer == '}'
|| *(input_line_pointer - 1) == '}'
|| *(input_line_pointer - 2) == '}')
xg_clear_vinsn (&cur_vinsn);
}
}
void
md_assemble (char *str)
{
xtensa_isa isa = xtensa_default_isa;
char *opname;
unsigned opnamelen;
bfd_boolean has_underbar = FALSE;
char *arg_strings[MAX_INSN_ARGS];
int num_args;
TInsn orig_insn;
tinsn_init (&orig_insn);
opnamelen = strspn (str, "abcdefghijklmnopqrstuvwxyz_/0123456789.");
opname = xmalloc (opnamelen + 1);
memcpy (opname, str, opnamelen);
opname[opnamelen] = '\0';
num_args = tokenize_arguments (arg_strings, str + opnamelen);
if (num_args == -1)
{
as_bad (_("syntax error"));
return;
}
if (xg_translate_idioms (&opname, &num_args, arg_strings))
return;
if (*opname == '_')
{
has_underbar = TRUE;
opname += 1;
}
orig_insn.insn_type = ITYPE_INSN;
orig_insn.ntok = 0;
orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
if (orig_insn.opcode == XTENSA_UNDEFINED)
{
xtensa_format fmt = xtensa_format_lookup (isa, opname);
if (fmt == XTENSA_UNDEFINED)
{
as_bad (_("unknown opcode or format name '%s'"), opname);
error_reset_cur_vinsn ();
return;
}
if (!cur_vinsn.inside_bundle)
{
as_bad (_("format names only valid inside bundles"));
error_reset_cur_vinsn ();
return;
}
if (cur_vinsn.format != XTENSA_UNDEFINED)
as_warn (_("multiple formats specified for one bundle; using '%s'"),
opname);
cur_vinsn.format = fmt;
free (has_underbar ? opname - 1 : opname);
error_reset_cur_vinsn ();
return;
}
if (parse_arguments (&orig_insn, num_args, arg_strings))
{
as_bad (_("syntax error"));
error_reset_cur_vinsn ();
return;
}
free (has_underbar ? opname - 1 : opname);
opname = 0;
while (num_args-- > 0)
free (arg_strings[num_args]);
if (get_invisible_operands (&orig_insn))
{
error_reset_cur_vinsn ();
return;
}
if (tinsn_check_arguments (&orig_insn))
{
error_reset_cur_vinsn ();
return;
}
dwarf2_where (&orig_insn.loc);
xg_add_branch_and_loop_targets (&orig_insn);
if (orig_insn.opcode == xtensa_entry_opcode)
{
if (orig_insn.ntok >= 3)
{
expressionS *exp = &orig_insn.tok[2];
switch (exp->X_op)
{
case O_constant:
if (exp->X_add_number < 16)
as_warn (_("entry instruction with stack decrement < 16"));
break;
default:
as_warn (_("entry instruction with non-constant decrement"));
}
}
}
if (!cur_vinsn.inside_bundle)
emit_single_op (&orig_insn);
else
{
cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
cur_vinsn.num_slots++;
if (*input_line_pointer == '}'
|| *(input_line_pointer - 1) == '}'
|| *(input_line_pointer - 2) == '}')
finish_vinsn (&cur_vinsn);
}
xtensa_clear_insn_labels ();
}
void
xtensa_handle_align (fragS *fragP)
{
if (linkrelax
&& ! 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)
{
fragP->tc_frag_data.is_align = TRUE;
fragP->tc_frag_data.alignment = fragP->fr_offset;
}
if (fragP->fr_type == rs_align_test)
{
int count;
count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
if (count != 0)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("unaligned entry instruction"));
}
}
void
xtensa_frag_init (fragS *frag)
{
xtensa_set_frag_assembly_state (frag);
}
symbolS *
md_undefined_symbol (char *name ATTRIBUTE_UNUSED)
{
return NULL;
}
valueT
md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
{
return size;
}
long
md_pcrel_from (fixS *fixP)
{
char *insn_p;
static xtensa_insnbuf insnbuf = NULL;
static xtensa_insnbuf slotbuf = NULL;
int opnum;
uint32 opnd_value;
xtensa_opcode opcode;
xtensa_format fmt;
int slot;
xtensa_isa isa = xtensa_default_isa;
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
bfd_boolean alt_reloc;
if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
return 0;
if (!insnbuf)
{
insnbuf = xtensa_insnbuf_alloc (isa);
slotbuf = xtensa_insnbuf_alloc (isa);
}
insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) insn_p, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
as_fatal (_("bad instruction format"));
if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
as_fatal (_("invalid relocation"));
xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
if (alt_reloc || opcode == xtensa_const16_opcode)
{
if (opcode != xtensa_l32r_opcode
&& opcode != xtensa_const16_opcode)
as_fatal (_("invalid relocation for '%s' instruction"),
xtensa_opcode_name (isa, opcode));
return 0;
}
opnum = get_relaxable_immed (opcode);
opnd_value = 0;
if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
|| xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
{
as_bad_where (fixP->fx_file,
fixP->fx_line,
_("invalid relocation for operand %d of '%s'"),
opnum, xtensa_opcode_name (isa, opcode));
return 0;
}
return 0 - opnd_value;
}
int
xtensa_force_relocation (fixS *fix)
{
switch (fix->fx_r_type)
{
case BFD_RELOC_XTENSA_ASM_EXPAND:
case BFD_RELOC_XTENSA_SLOT0_ALT:
case BFD_RELOC_XTENSA_SLOT1_ALT:
case BFD_RELOC_XTENSA_SLOT2_ALT:
case BFD_RELOC_XTENSA_SLOT3_ALT:
case BFD_RELOC_XTENSA_SLOT4_ALT:
case BFD_RELOC_XTENSA_SLOT5_ALT:
case BFD_RELOC_XTENSA_SLOT6_ALT:
case BFD_RELOC_XTENSA_SLOT7_ALT:
case BFD_RELOC_XTENSA_SLOT8_ALT:
case BFD_RELOC_XTENSA_SLOT9_ALT:
case BFD_RELOC_XTENSA_SLOT10_ALT:
case BFD_RELOC_XTENSA_SLOT11_ALT:
case BFD_RELOC_XTENSA_SLOT12_ALT:
case BFD_RELOC_XTENSA_SLOT13_ALT:
case BFD_RELOC_XTENSA_SLOT14_ALT:
return 1;
default:
break;
}
if (linkrelax && fix->fx_addsy
&& relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
return 1;
return generic_force_reloc (fix);
}
int
xtensa_validate_fix_sub (fixS *fix)
{
segT add_symbol_segment, sub_symbol_segment;
if (!linkrelax || fix->fx_addsy == NULL)
return 0;
add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
if (! SEG_NORMAL (add_symbol_segment) ||
! relaxable_section (add_symbol_segment))
return 0;
sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
return (sub_symbol_segment == add_symbol_segment);
}
bfd_boolean
xtensa_check_inside_bundle (void)
{
if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
as_bad (_("directives are not valid inside bundles"));
return FALSE;
}
void
xtensa_elf_section_change_hook (void)
{
if (!frag_now->tc_frag_data.is_assembly_state_set)
xtensa_set_frag_assembly_state (frag_now);
}
bfd_boolean
xtensa_fix_adjustable (fixS *fixP)
{
if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
&& relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
return 0;
if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
return 0;
return 1;
}
void
md_apply_fix3 (fixS *fixP, valueT *valP, segT seg)
{
char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
valueT val;
switch (fixP->fx_r_type)
{
case BFD_RELOC_32:
case BFD_RELOC_16:
case BFD_RELOC_8:
if (linkrelax && fixP->fx_subsy)
{
switch (fixP->fx_r_type)
{
case BFD_RELOC_8:
fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
break;
case BFD_RELOC_16:
fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
break;
case BFD_RELOC_32:
fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
break;
default:
break;
}
if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("cannot represent subtraction with an offset"));
val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
- S_GET_VALUE (fixP->fx_subsy));
*valP = val;
fixP->fx_offset -= val;
fixP->fx_subsy = NULL;
}
else if (! fixP->fx_addsy)
{
val = *valP;
fixP->fx_done = 1;
}
else
break;
md_number_to_chars (fixpos, val, fixP->fx_size);
fixP->fx_no_overflow = 0;
break;
case BFD_RELOC_XTENSA_SLOT0_OP:
case BFD_RELOC_XTENSA_SLOT1_OP:
case BFD_RELOC_XTENSA_SLOT2_OP:
case BFD_RELOC_XTENSA_SLOT3_OP:
case BFD_RELOC_XTENSA_SLOT4_OP:
case BFD_RELOC_XTENSA_SLOT5_OP:
case BFD_RELOC_XTENSA_SLOT6_OP:
case BFD_RELOC_XTENSA_SLOT7_OP:
case BFD_RELOC_XTENSA_SLOT8_OP:
case BFD_RELOC_XTENSA_SLOT9_OP:
case BFD_RELOC_XTENSA_SLOT10_OP:
case BFD_RELOC_XTENSA_SLOT11_OP:
case BFD_RELOC_XTENSA_SLOT12_OP:
case BFD_RELOC_XTENSA_SLOT13_OP:
case BFD_RELOC_XTENSA_SLOT14_OP:
if (linkrelax)
{
assert (fixP->fx_addsy);
if (S_GET_SEGMENT (fixP->fx_addsy) == seg && !fixP->fx_plt
&& !S_FORCE_RELOC (fixP->fx_addsy, 1))
{
val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
- md_pcrel_from (fixP));
(void) xg_apply_fix_value (fixP, val);
}
}
else if (! fixP->fx_addsy)
{
val = *valP;
if (xg_apply_fix_value (fixP, val))
fixP->fx_done = 1;
}
break;
case BFD_RELOC_XTENSA_PLT:
case BFD_RELOC_XTENSA_ASM_EXPAND:
case BFD_RELOC_XTENSA_SLOT0_ALT:
case BFD_RELOC_XTENSA_SLOT1_ALT:
case BFD_RELOC_XTENSA_SLOT2_ALT:
case BFD_RELOC_XTENSA_SLOT3_ALT:
case BFD_RELOC_XTENSA_SLOT4_ALT:
case BFD_RELOC_XTENSA_SLOT5_ALT:
case BFD_RELOC_XTENSA_SLOT6_ALT:
case BFD_RELOC_XTENSA_SLOT7_ALT:
case BFD_RELOC_XTENSA_SLOT8_ALT:
case BFD_RELOC_XTENSA_SLOT9_ALT:
case BFD_RELOC_XTENSA_SLOT10_ALT:
case BFD_RELOC_XTENSA_SLOT11_ALT:
case BFD_RELOC_XTENSA_SLOT12_ALT:
case BFD_RELOC_XTENSA_SLOT13_ALT:
case BFD_RELOC_XTENSA_SLOT14_ALT:
break;
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
fixP->fx_done = 0;
break;
default:
as_bad (_("unhandled local relocation fix %s"),
bfd_get_reloc_code_name (fixP->fx_r_type));
}
}
char *
md_atof (int type, char *litP, int *sizeP)
{
int prec;
LITTLENUM_TYPE words[4];
char *t;
int i;
switch (type)
{
case 'f':
prec = 2;
break;
case 'd':
prec = 4;
break;
default:
*sizeP = 0;
return "bad call to md_atof";
}
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
*sizeP = prec * 2;
for (i = prec - 1; i >= 0; i--)
{
int idx = i;
if (target_big_endian)
idx = (prec - 1 - i);
md_number_to_chars (litP, (valueT) words[idx], 2);
litP += 2;
}
return NULL;
}
int
md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
{
return total_frag_text_expansion (fragP);
}
arelent *
tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
arelent *reloc;
reloc = (arelent *) xmalloc (sizeof (arelent));
reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
assert ((int) fixp->fx_r_type > 0);
reloc->addend = fixp->fx_offset;
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
if (reloc->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("cannot represent `%s' relocation in object file"),
bfd_get_reloc_code_name (fixp->fx_r_type));
free (reloc->sym_ptr_ptr);
free (reloc);
return NULL;
}
if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
as_fatal (_("internal error? cannot generate `%s' relocation"),
bfd_get_reloc_code_name (fixp->fx_r_type));
return reloc;
}
resource_table *
new_resource_table (void *data,
int cycles,
int nu,
unit_num_copies_func uncf,
opcode_num_units_func onuf,
opcode_funcUnit_use_unit_func ouuf,
opcode_funcUnit_use_stage_func ousf)
{
int i;
resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
rt->data = data;
rt->cycles = cycles;
rt->allocated_cycles = cycles;
rt->num_units = nu;
rt->unit_num_copies = uncf;
rt->opcode_num_units = onuf;
rt->opcode_unit_use = ouuf;
rt->opcode_unit_stage = ousf;
rt->units = (char **) xcalloc (cycles, sizeof (char *));
for (i = 0; i < cycles; i++)
rt->units[i] = (char *) xcalloc (nu, sizeof (char));
return rt;
}
void
clear_resource_table (resource_table *rt)
{
int i, j;
for (i = 0; i < rt->allocated_cycles; i++)
for (j = 0; j < rt->num_units; j++)
rt->units[i][j] = 0;
}
void
resize_resource_table (resource_table *rt, int cycles)
{
int i, old_cycles;
rt->cycles = cycles;
if (cycles <= rt->allocated_cycles)
return;
old_cycles = rt->allocated_cycles;
rt->allocated_cycles = cycles;
rt->units = xrealloc (rt->units, sizeof (char *) * rt->allocated_cycles);
for (i = 0; i < old_cycles; i++)
rt->units[i] = xrealloc (rt->units[i], sizeof (char) * rt->num_units);
for (i = old_cycles; i < cycles; i++)
rt->units[i] = xcalloc (rt->num_units, sizeof (char));
}
bfd_boolean
resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
{
int i;
int uses = (rt->opcode_num_units) (rt->data, opcode);
for (i = 0; i < uses; i++)
{
xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
int copies_in_use = rt->units[stage + cycle][unit];
int copies = (rt->unit_num_copies) (rt->data, unit);
if (copies_in_use >= copies)
return FALSE;
}
return TRUE;
}
void
reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
{
int i;
int uses = (rt->opcode_num_units) (rt->data, opcode);
for (i = 0; i < uses; i++)
{
xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
rt->units[stage + cycle][unit]++;
}
}
void
release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
{
int i;
int uses = (rt->opcode_num_units) (rt->data, opcode);
for (i = 0; i < uses; i++)
{
xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
rt->units[stage + cycle][unit]--;
assert (rt->units[stage + cycle][unit] >= 0);
}
}
int
opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
{
xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
return use->unit;
}
int
opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
{
xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
return use->stage;
}
static bfd_boolean
resources_conflict (vliw_insn *vinsn)
{
int i;
static resource_table *rt = NULL;
if (vinsn->num_slots == 1)
return FALSE;
if (rt == NULL)
{
xtensa_isa isa = xtensa_default_isa;
rt = new_resource_table
(isa, xtensa_isa_num_pipe_stages (isa),
xtensa_isa_num_funcUnits (isa),
(unit_num_copies_func) xtensa_funcUnit_num_copies,
(opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
opcode_funcUnit_use_unit,
opcode_funcUnit_use_stage);
}
clear_resource_table (rt);
for (i = 0; i < vinsn->num_slots; i++)
{
if (!resources_available (rt, vinsn->slots[i].opcode, 0))
return TRUE;
reserve_resources (rt, vinsn->slots[i].opcode, 0);
}
return FALSE;
}
static bfd_boolean find_vinsn_conflicts (vliw_insn *);
static xtensa_format xg_find_narrowest_format (vliw_insn *);
static void bundle_single_op (TInsn *);
static void xg_assemble_vliw_tokens (vliw_insn *);
static void
finish_vinsn (vliw_insn *vinsn)
{
IStack slotstack;
int i;
char *file_name;
unsigned line;
if (find_vinsn_conflicts (vinsn))
{
xg_clear_vinsn (vinsn);
return;
}
if (vinsn->format == XTENSA_UNDEFINED)
vinsn->format = xg_find_narrowest_format (vinsn);
if (vinsn->format == XTENSA_UNDEFINED)
{
as_where (&file_name, &line);
as_bad_where (file_name, line,
_("couldn't find a valid instruction format"));
fprintf (stderr, _(" ops were: "));
for (i = 0; i < vinsn->num_slots; i++)
fprintf (stderr, _(" %s;"),
xtensa_opcode_name (xtensa_default_isa,
vinsn->slots[i].opcode));
fprintf (stderr, _("\n"));
xg_clear_vinsn (vinsn);
return;
}
if (vinsn->num_slots
!= xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
{
as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
xtensa_format_name (xtensa_default_isa, vinsn->format),
xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
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"));
fprintf (stderr, " ops were: ");
for (i = 0; i < vinsn->num_slots; i++)
fprintf (stderr, " %s;",
xtensa_opcode_name (xtensa_default_isa,
vinsn->slots[i].opcode));
fprintf (stderr, "\n");
xg_clear_vinsn (vinsn);
return;
}
for (i = 0; i < vinsn->num_slots; i++)
{
if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
{
symbolS *lit_sym = NULL;
int j;
bfd_boolean e = FALSE;
bfd_boolean saved_density = density_supported;
if (vinsn->num_slots > 1)
density_supported = FALSE;
istack_init (&slotstack);
if (vinsn->slots[i].opcode == xtensa_nop_opcode)
{
vinsn->slots[i].opcode =
xtensa_format_slot_nop_opcode (xtensa_default_isa,
vinsn->format, i);
vinsn->slots[i].ntok = 0;
}
if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
{
e = TRUE;
continue;
}
density_supported = saved_density;
if (e)
{
xg_clear_vinsn (vinsn);
return;
}
for (j = 0; j < slotstack.ninsn; j++)
{
TInsn *insn = &slotstack.insn[j];
if (insn->insn_type == ITYPE_LITERAL)
{
assert (lit_sym == NULL);
lit_sym = xg_assemble_literal (insn);
}
else
{
assert (insn->insn_type == ITYPE_INSN);
if (lit_sym)
xg_resolve_literals (insn, lit_sym);
if (j != slotstack.ninsn - 1)
emit_single_op (insn);
}
}
if (vinsn->num_slots > 1)
{
if (opcode_fits_format_slot
(slotstack.insn[slotstack.ninsn - 1].opcode,
vinsn->format, i))
{
vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
}
else
{
bundle_single_op (&slotstack.insn[slotstack.ninsn - 1]);
if (vinsn->format == XTENSA_UNDEFINED)
vinsn->slots[i].opcode = xtensa_nop_opcode;
else
vinsn->slots[i].opcode
= xtensa_format_slot_nop_opcode (xtensa_default_isa,
vinsn->format, i);
vinsn->slots[i].ntok = 0;
}
}
else
{
vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
vinsn->format = XTENSA_UNDEFINED;
}
}
}
if (resources_conflict (vinsn))
{
as_where (&file_name, &line);
as_bad_where (file_name, line, _("illegal resource usage in bundle"));
fprintf (stderr, " ops were: ");
for (i = 0; i < vinsn->num_slots; i++)
fprintf (stderr, " %s;",
xtensa_opcode_name (xtensa_default_isa,
vinsn->slots[i].opcode));
fprintf (stderr, "\n");
xg_clear_vinsn (vinsn);
return;
}
if (vinsn->format == XTENSA_UNDEFINED)
vinsn->format = xg_find_narrowest_format (vinsn);
xg_assemble_vliw_tokens (vinsn);
xg_clear_vinsn (vinsn);
}
static char check_t1_t2_reads_and_writes (TInsn *, TInsn *);
static bfd_boolean
find_vinsn_conflicts (vliw_insn *vinsn)
{
int i, j;
int branches = 0;
xtensa_isa isa = xtensa_default_isa;
assert (!past_xtensa_end);
for (i = 0 ; i < vinsn->num_slots; i++)
{
TInsn *op1 = &vinsn->slots[i];
if (op1->is_specific_opcode)
op1->keep_wide = TRUE;
else
op1->keep_wide = FALSE;
}
for (i = 0 ; i < vinsn->num_slots; i++)
{
TInsn *op1 = &vinsn->slots[i];
if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
branches++;
for (j = 0; j < vinsn->num_slots; j++)
{
if (i != j)
{
TInsn *op2 = &vinsn->slots[j];
char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
switch (conflict_type)
{
case 'c':
as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
xtensa_opcode_name (isa, op1->opcode), i,
xtensa_opcode_name (isa, op2->opcode), j);
return TRUE;
case 'd':
as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
xtensa_opcode_name (isa, op1->opcode), i,
xtensa_opcode_name (isa, op2->opcode), j);
return TRUE;
case 'e':
as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same queue"),
xtensa_opcode_name (isa, op1->opcode), i,
xtensa_opcode_name (isa, op2->opcode), j);
return TRUE;
case 'f':
as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile queue accesses"),
xtensa_opcode_name (isa, op1->opcode), i,
xtensa_opcode_name (isa, op2->opcode), j);
return TRUE;
default:
break;
}
op2->is_specific_opcode = (op2->is_specific_opcode
|| conflict_type == 'a');
}
}
}
if (branches > 1)
{
as_bad (_("multiple branches or jumps in the same bundle"));
return TRUE;
}
return FALSE;
}
static char
check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_regfile t1_regfile, t2_regfile;
int t1_reg, t2_reg;
int t1_base_reg, t1_last_reg;
int t2_base_reg, t2_last_reg;
char t1_inout, t2_inout;
int i, j;
char conflict = 'b';
int t1_states;
int t2_states;
int t1_interfaces;
int t2_interfaces;
bfd_boolean t1_volatile = FALSE;
bfd_boolean t2_volatile = FALSE;
for (j = 0; j < t2->ntok; j++)
{
if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
continue;
t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
t2_base_reg = t2->tok[j].X_add_number;
t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
for (i = 0; i < t1->ntok; i++)
{
if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
continue;
t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
if (t1_regfile != t2_regfile)
continue;
t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
|| xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
{
if (t1_inout == 'm' || t1_inout == 'o'
|| t2_inout == 'm' || t2_inout == 'o')
{
conflict = 'a';
continue;
}
}
t1_base_reg = t1->tok[i].X_add_number;
t1_last_reg = (t1_base_reg
+ xtensa_operand_num_regs (isa, t1->opcode, i));
for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
{
for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
{
if (t1_reg != t2_reg)
continue;
if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
{
conflict = 'a';
continue;
}
if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
{
conflict = 'a';
continue;
}
if (t1_inout != 'i' && t2_inout != 'i')
return 'c';
}
}
}
}
t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
for (j = 0; j < t2_states; j++)
{
xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
for (i = 0; i < t1_states; i++)
{
xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
if (t1_so != t2_so)
continue;
if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
{
conflict = 'a';
continue;
}
if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
{
conflict = 'a';
continue;
}
if (t1_inout != 'i' && t2_inout != 'i')
return 'd';
}
}
t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
for (j = 0; j < t2_interfaces; j++)
{
xtensa_interface t2_int
= xtensa_interfaceOperand_interface (isa, t2->opcode, j);
int t2_class = xtensa_interface_class_id (isa, t2_int);
t2_inout = xtensa_interface_inout (isa, j);
if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
t2_volatile = TRUE;
for (i = 0; i < t1_interfaces; i++)
{
xtensa_interface t1_int
= xtensa_interfaceOperand_interface (isa, t1->opcode, j);
int t1_class = xtensa_interface_class_id (isa, t1_int);
t1_inout = xtensa_interface_inout (isa, i);
if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
t1_volatile = TRUE;
if (t1_volatile && t2_volatile && (t1_class == t2_class))
return 'f';
if (t1_int != t2_int)
continue;
if (t2_inout == 'i' && t1_inout == 'o')
{
conflict = 'a';
continue;
}
if (t1_inout == 'i' && t2_inout == 'o')
{
conflict = 'a';
continue;
}
if (t1_inout != 'i' && t2_inout != 'i')
return 'e';
}
}
return conflict;
}
static xtensa_format
xg_find_narrowest_format (vliw_insn *vinsn)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_format format;
vliw_insn v_copy = *vinsn;
xtensa_opcode nop_opcode = xtensa_nop_opcode;
for (format = 0; format < xtensa_isa_num_formats (isa); format++)
{
v_copy = *vinsn;
if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
{
int slot;
int fit = 0;
for (slot = 0; slot < v_copy.num_slots; slot++)
{
if (v_copy.slots[slot].opcode == nop_opcode)
{
v_copy.slots[slot].opcode =
xtensa_format_slot_nop_opcode (isa, format, slot);
v_copy.slots[slot].ntok = 0;
}
if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
format, slot))
fit++;
else if (v_copy.num_slots > 1)
{
TInsn widened;
if (!v_copy.slots[slot].keep_wide
&& !v_copy.slots[slot].is_specific_opcode
&& xg_is_narrow_insn (&v_copy.slots[slot])
&& !xg_expand_narrow (&widened, &v_copy.slots[slot])
&& opcode_fits_format_slot (widened.opcode,
format, slot))
{
v_copy.slots[slot] = widened;
fit++;
}
}
}
if (fit == v_copy.num_slots)
{
*vinsn = v_copy;
xtensa_format_encode (isa, format, vinsn->insnbuf);
vinsn->format = format;
break;
}
}
}
if (format == xtensa_isa_num_formats (isa))
return XTENSA_UNDEFINED;
return format;
}
static int
relaxation_requirements (vliw_insn *vinsn)
{
int extra_space = 0;
int slot;
for (slot = 0; slot < vinsn->num_slots; slot++)
{
TInsn *tinsn = &vinsn->slots[slot];
if (!tinsn_has_symbolic_operands (tinsn))
{
if (xg_is_narrow_insn (tinsn)
&& !tinsn->is_specific_opcode
&& vinsn->num_slots == 1)
{
extra_space += 1;
tinsn->subtype = RELAX_NARROW;
tinsn->record_fix = TRUE;
break;
}
else
{
tinsn->record_fix = FALSE;
}
}
else
{
if (workaround_b_j_loop_end
&& tinsn->opcode == xtensa_jx_opcode
&& use_transform ())
{
extra_space += 3;
tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
}
if (xg_is_relaxable_insn (tinsn, 0)
&& !tinsn->is_specific_opcode)
{
int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
int max_literal_size =
xg_get_max_insn_widen_literal_size (tinsn->opcode);
tinsn->literal_space = max_literal_size;
tinsn->subtype = RELAX_IMMED;
tinsn->record_fix = FALSE;
extra_space += max_size;
}
else
{
tinsn->record_fix = TRUE;
}
}
}
return extra_space;
}
static void
bundle_single_op (TInsn *orig_insn)
{
xtensa_isa isa = xtensa_default_isa;
vliw_insn v;
int slot;
xg_init_vinsn (&v);
v.format = op_placement_table[orig_insn->opcode].narrowest;
assert (v.format != XTENSA_UNDEFINED);
v.num_slots = xtensa_format_num_slots (isa, v.format);
for (slot = 0;
!opcode_fits_format_slot (orig_insn->opcode, v.format, slot);
slot++)
{
v.slots[slot].opcode =
xtensa_format_slot_nop_opcode (isa, v.format, slot);
v.slots[slot].ntok = 0;
v.slots[slot].insn_type = ITYPE_INSN;
}
v.slots[slot] = *orig_insn;
slot++;
for ( ; slot < v.num_slots; slot++)
{
v.slots[slot].opcode =
xtensa_format_slot_nop_opcode (isa, v.format, slot);
v.slots[slot].ntok = 0;
v.slots[slot].insn_type = ITYPE_INSN;
}
finish_vinsn (&v);
xg_free_vinsn (&v);
}
static bfd_boolean
emit_single_op (TInsn *orig_insn)
{
int i;
IStack istack;
symbolS *lit_sym = NULL;
symbolS *label_sym = NULL;
istack_init (&istack);
if ((orig_insn->opcode == xtensa_movi_opcode
|| orig_insn->opcode == xtensa_movi_n_opcode)
&& !cur_vinsn.inside_bundle
&& (orig_insn->tok[1].X_op == O_symbol
|| orig_insn->tok[1].X_op == O_pltrel))
xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
else
if (xg_expand_assembly_insn (&istack, orig_insn))
return TRUE;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *insn = &istack.insn[i];
switch (insn->insn_type)
{
case ITYPE_LITERAL:
assert (lit_sym == NULL);
lit_sym = xg_assemble_literal (insn);
break;
case ITYPE_LABEL:
{
static int relaxed_sym_idx = 0;
char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
colon (label);
assert (label_sym == NULL);
label_sym = symbol_find_or_make (label);
assert (label_sym);
free (label);
}
break;
case ITYPE_INSN:
if (lit_sym)
xg_resolve_literals (insn, lit_sym);
if (label_sym)
xg_resolve_labels (insn, label_sym);
bundle_single_op (insn);
break;
default:
assert (0);
break;
}
}
return FALSE;
}
static int
total_frag_text_expansion (fragS *fragP)
{
int slot;
int total_expansion = 0;
for (slot = 0; slot < MAX_SLOTS; slot++)
total_expansion += fragP->tc_frag_data.text_expansion[slot];
return total_expansion;
}
static void
xg_assemble_vliw_tokens (vliw_insn *vinsn)
{
bfd_boolean finish_frag = FALSE;
bfd_boolean is_jump = FALSE;
bfd_boolean is_branch = FALSE;
xtensa_isa isa = xtensa_default_isa;
int i;
int insn_size;
int extra_space;
char *f = NULL;
int slot;
struct dwarf2_line_info best_loc;
best_loc.line = INT_MAX;
if (generating_literals)
{
static int reported = 0;
if (reported < 4)
as_bad_where (frag_now->fr_file, frag_now->fr_line,
_("cannot assemble into a literal fragment"));
if (reported == 3)
as_bad (_("..."));
reported++;
return;
}
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
|| (directive_state[directive_longcalls]
!= frag_now->tc_frag_data.use_longcalls)
|| (directive_state[directive_absolute_literals]
!= frag_now->tc_frag_data.use_absolute_literals)))
{
frag_wane (frag_now);
frag_new (0);
xtensa_set_frag_assembly_state (frag_now);
}
if (workaround_a0_b_retw
&& vinsn->num_slots == 1
&& (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
&& xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
&& use_transform ())
{
has_a0_b_retw = TRUE;
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, 4,
RELAX_ADD_NOP_IF_A0_B_RETW,
frag_now->fr_symbol,
frag_now->fr_offset,
NULL);
xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, 4,
RELAX_ADD_NOP_IF_A0_B_RETW,
frag_now->fr_symbol,
frag_now->fr_offset,
NULL);
xtensa_set_frag_assembly_state (frag_now);
}
for (i = 0; i < vinsn->num_slots; i++)
{
if (xtensa_opcode_is_loop (isa, vinsn->slots[i].opcode) == 1)
record_alignment (now_seg, 2);
best_loc = vinsn->slots[i].loc.line < best_loc.line
? vinsn->slots[i].loc : best_loc;
}
if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
{
int max_fill;
xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.is_insn = TRUE;
max_fill = get_text_align_max_fill_size
(get_text_align_power (xtensa_fetch_width),
TRUE, frag_now->tc_frag_data.is_no_density);
if (use_transform ())
frag_var (rs_machine_dependent, max_fill, max_fill,
RELAX_ALIGN_NEXT_OPCODE,
frag_now->fr_symbol,
frag_now->fr_offset,
NULL);
else
frag_var (rs_machine_dependent, 0, 0,
RELAX_CHECK_ALIGN_NEXT_OPCODE, 0, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
xtensa_move_labels (frag_now, 0, FALSE);
}
if (vinsn->slots[0].opcode == xtensa_entry_opcode
&& !vinsn->slots[0].is_specific_opcode)
{
xtensa_mark_literal_pool_location ();
xtensa_move_labels (frag_now, 0, TRUE);
frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
}
if (vinsn->num_slots == 1)
{
if (workaround_a0_b_retw && use_transform ())
set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
is_register_writer (&vinsn->slots[0], "a", 0));
set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
is_bad_loopend_opcode (&vinsn->slots[0]));
}
else
set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
insn_size = xtensa_format_length (isa, vinsn->format);
extra_space = relaxation_requirements (vinsn);
if (vinsn->format != XTENSA_UNDEFINED)
{
f = frag_more (insn_size + extra_space);
xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.is_insn = TRUE;
}
vinsn_to_insnbuf (vinsn, f, frag_now, TRUE);
if (vinsn->format == XTENSA_UNDEFINED)
return;
xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
xtensa_dwarf2_emit_insn (insn_size - extra_space, &best_loc);
for (slot = 0; slot < vinsn->num_slots; slot++)
{
TInsn *tinsn = &vinsn->slots[slot];
frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
frag_now->tc_frag_data.slot_sub_symbols[slot] = tinsn->sub_symbol;
frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
if (tinsn->literal_space != 0)
xg_assemble_literal_space (tinsn->literal_space, slot);
if (tinsn->subtype == RELAX_NARROW)
assert (vinsn->num_slots == 1);
if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
is_jump = TRUE;
if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
is_branch = TRUE;
if (tinsn->subtype || tinsn->symbol || tinsn->record_fix
|| tinsn->offset || tinsn->literal_frag || is_jump || is_branch)
finish_frag = TRUE;
}
if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
frag_now->tc_frag_data.is_specific_opcode = TRUE;
if (finish_frag)
{
frag_variant (rs_machine_dependent,
extra_space, extra_space, RELAX_SLOTS,
frag_now->fr_symbol, frag_now->fr_offset, f);
xtensa_set_frag_assembly_state (frag_now);
}
if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode)
&& !vinsn->slots[0].is_specific_opcode)
{
if (workaround_short_loop && use_transform ())
{
maybe_has_short_loop = TRUE;
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, 4,
RELAX_ADD_NOP_IF_SHORT_LOOP,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, 4,
RELAX_ADD_NOP_IF_SHORT_LOOP,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
}
if (workaround_close_loop_end && use_transform ())
{
maybe_has_close_loop_end = TRUE;
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 12, 12,
RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
}
}
if (use_transform ())
{
if (is_jump)
{
assert (finish_frag);
frag_var (rs_machine_dependent,
UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
RELAX_UNREACHABLE,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
}
else if (is_branch && do_align_targets ())
{
assert (finish_frag);
frag_var (rs_machine_dependent,
UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
RELAX_MAYBE_UNREACHABLE,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
frag_var (rs_machine_dependent,
0, 0,
RELAX_MAYBE_DESIRE_ALIGN,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
}
}
if (do_align_targets ()
&& xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
{
float freq = get_subseg_total_freq (now_seg, now_subseg);
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_set_frag_assembly_state (frag_now);
}
if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
{
frag_wane (frag_now);
frag_new (0);
xtensa_set_frag_assembly_state (frag_now);
}
}
static void xtensa_cleanup_align_frags (void);
static void xtensa_fix_target_frags (void);
static void xtensa_mark_narrow_branches (void);
static void xtensa_mark_zcl_first_insns (void);
static void xtensa_fix_a0_b_retw_frags (void);
static void xtensa_fix_b_j_loop_end_frags (void);
static void xtensa_fix_close_loop_end_frags (void);
static void xtensa_fix_short_loop_frags (void);
static void xtensa_sanity_check (void);
void
xtensa_end (void)
{
directive_balance ();
xtensa_flush_pending_output ();
past_xtensa_end = TRUE;
xtensa_move_literals ();
xtensa_reorder_segments ();
xtensa_cleanup_align_frags ();
xtensa_fix_target_frags ();
if (workaround_a0_b_retw && has_a0_b_retw)
xtensa_fix_a0_b_retw_frags ();
if (workaround_b_j_loop_end)
xtensa_fix_b_j_loop_end_frags ();
if (workaround_close_loop_end && maybe_has_close_loop_end)
xtensa_fix_close_loop_end_frags ();
if (workaround_short_loop && maybe_has_short_loop)
xtensa_fix_short_loop_frags ();
xtensa_mark_narrow_branches ();
xtensa_mark_zcl_first_insns ();
xtensa_sanity_check ();
}
static void
xtensa_cleanup_align_frags (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
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 (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;
}
}
}
static void
xtensa_fix_target_frags (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
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;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
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;
const expressionS *expr;
symbolS *symbolP;
vinsn_from_chars (&vinsn, fragP->fr_opcode);
tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
expr = &vinsn.slots[0].tok[1];
symbolP = expr->X_add_symbol;
if (vinsn.num_slots == 1
&& xtensa_opcode_is_branch (xtensa_default_isa,
vinsn.slots[0].opcode)
&& 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;
}
}
}
}
}
#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 *expr = &tinsn->tok[1];
symbolS *symbolP = expr->X_add_symbol;
fragS *target_frag = symbol_get_frag (symbolP);
offsetT max_distance = expr->X_add_number;
max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
if (is_branch_jmp_to_next (tinsn, fragP))
return FALSE;
fragP = fragP->fr_next;
while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
{
max_distance += unrelaxed_frag_max_size (fragP);
fragP = fragP->fr_next;
}
if (max_distance <= MAX_IMMED6 && fragP == target_frag)
return TRUE;
return FALSE;
}
static void
xtensa_mark_zcl_first_insns (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
|| fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
{
fragS *targ_frag = next_non_empty_frag (fragP);
targ_frag = next_non_empty_frag (targ_frag);
if (targ_frag)
{
targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
frag_wane (fragP);
}
}
}
}
}
static bfd_boolean next_instrs_are_b_retw (fragS *);
static void
xtensa_fix_a0_b_retw_frags (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
{
if (next_instrs_are_b_retw (fragP))
{
if (fragP->tc_frag_data.is_no_transform)
as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
else
relax_frag_add_nop (fragP);
}
frag_wane (fragP);
}
}
}
}
static bfd_boolean
next_instrs_are_b_retw (fragS *fragP)
{
xtensa_opcode opcode;
xtensa_format fmt;
const fragS *next_fragP = next_non_empty_frag (fragP);
static xtensa_insnbuf insnbuf = NULL;
static xtensa_insnbuf slotbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
int offset = 0;
int slot;
bfd_boolean branch_seen = FALSE;
if (!insnbuf)
{
insnbuf = xtensa_insnbuf_alloc (isa);
slotbuf = xtensa_insnbuf_alloc (isa);
}
if (next_fragP == NULL)
return FALSE;
xtensa_insnbuf_from_chars
(isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
return FALSE;
for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
{
xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
branch_seen = (branch_seen
|| xtensa_opcode_is_branch (isa, opcode) == 1);
}
if (!branch_seen)
return FALSE;
offset += xtensa_format_length (isa, fmt);
if (offset == next_fragP->fr_fix)
{
next_fragP = next_non_empty_frag (next_fragP);
offset = 0;
}
if (next_fragP == NULL)
return FALSE;
xtensa_insnbuf_from_chars
(isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED
|| xtensa_format_num_slots (isa, fmt) != 1)
return FALSE;
xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
if (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode)
return TRUE;
return FALSE;
}
static bfd_boolean next_instr_is_loop_end (fragS *);
static void
xtensa_fix_b_j_loop_end_frags (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
{
if (next_instr_is_loop_end (fragP))
{
if (fragP->tc_frag_data.is_no_transform)
as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
else
relax_frag_add_nop (fragP);
}
frag_wane (fragP);
}
}
}
}
static bfd_boolean
next_instr_is_loop_end (fragS *fragP)
{
const fragS *next_fragP;
if (next_frag_is_loop_target (fragP))
return FALSE;
next_fragP = next_non_empty_frag (fragP);
if (next_fragP == NULL)
return FALSE;
if (!next_frag_is_loop_target (next_fragP))
return FALSE;
if (next_fragP->fr_fix > 3)
return FALSE;
return TRUE;
}
static offsetT min_bytes_to_other_loop_end
(fragS *, fragS *, offsetT, offsetT);
static void
xtensa_fix_close_loop_end_frags (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
fragS *current_target = NULL;
offsetT current_offset = 0;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& ((fragP->fr_subtype == RELAX_IMMED)
|| ((fragP->fr_subtype == RELAX_SLOTS)
&& (fragP->tc_frag_data.slot_subtypes[0]
== RELAX_IMMED))))
{
TInsn t_insn;
tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
if (xtensa_opcode_is_loop (xtensa_default_isa,
t_insn.opcode) == 1)
{
if (fragP->tc_frag_data.slot_symbols[0])
{
symbolS *sym = fragP->tc_frag_data.slot_symbols[0];
current_target = symbol_get_frag (sym);
current_offset = fragP->fr_offset;
}
}
}
if (current_target
&& fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
{
offsetT min_bytes;
int bytes_added = 0;
#define REQUIRED_LOOP_DIVIDING_BYTES 12
min_bytes = min_bytes_to_other_loop_end
(fragP->fr_next, current_target, current_offset,
REQUIRED_LOOP_DIVIDING_BYTES);
if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
{
if (fragP->tc_frag_data.is_no_transform)
as_bad (_("loop end too close to another loop end may trigger hardware errata"));
else
{
while (min_bytes + bytes_added
< REQUIRED_LOOP_DIVIDING_BYTES)
{
int length = 3;
if (fragP->fr_var < length)
as_fatal (_("fr_var %lu < length %d"),
(long) fragP->fr_var, length);
else
{
assemble_nop (length,
fragP->fr_literal + fragP->fr_fix);
fragP->fr_fix += length;
fragP->fr_var -= length;
}
bytes_added += length;
}
}
}
frag_wane (fragP);
}
assert (fragP->fr_type != rs_machine_dependent
|| fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
}
}
}
static offsetT unrelaxed_frag_min_size (fragS *);
static offsetT
min_bytes_to_other_loop_end (fragS *fragP,
fragS *current_target,
offsetT current_offset,
offsetT max_size)
{
offsetT offset = 0;
fragS *current_fragP;
for (current_fragP = fragP;
current_fragP;
current_fragP = current_fragP->fr_next)
{
if (current_fragP->tc_frag_data.is_loop_target
&& current_fragP != current_target)
return offset + current_offset;
offset += unrelaxed_frag_min_size (current_fragP);
if (offset + current_offset >= max_size)
return max_size;
}
return max_size;
}
static offsetT
unrelaxed_frag_min_size (fragS *fragP)
{
offsetT size = fragP->fr_fix;
if (fragP->fr_type == rs_fill)
size += fragP->fr_offset;
return size;
}
static offsetT
unrelaxed_frag_max_size (fragS *fragP)
{
offsetT size = fragP->fr_fix;
switch (fragP->fr_type)
{
case 0:
break;
case rs_fill:
case rs_org:
case rs_space:
size += fragP->fr_offset;
break;
case rs_align:
case rs_align_code:
case rs_align_test:
case rs_leb128:
case rs_cfa:
case rs_dwarf2dbg:
break;
case rs_machine_dependent:
if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
size += fragP->fr_var;
break;
default:
assert (0);
break;
}
return size;
}
static int count_insns_to_loop_end (fragS *, bfd_boolean, int);
static bfd_boolean branch_before_loop_end (fragS *);
static void
xtensa_fix_short_loop_frags (void)
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
fragS *current_target = NULL;
offsetT current_offset = 0;
xtensa_opcode current_opcode = XTENSA_UNDEFINED;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& ((fragP->fr_subtype == RELAX_IMMED)
|| ((fragP->fr_subtype == RELAX_SLOTS)
&& (fragP->tc_frag_data.slot_subtypes[0]
== RELAX_IMMED))))
{
TInsn t_insn;
tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
if (xtensa_opcode_is_loop (xtensa_default_isa,
t_insn.opcode) == 1)
{
if (fragP->tc_frag_data.slot_symbols[0])
{
symbolS *sym = fragP->tc_frag_data.slot_symbols[0];
current_target = symbol_get_frag (sym);
current_offset = fragP->fr_offset;
current_opcode = t_insn.opcode;
}
}
}
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
{
if (count_insns_to_loop_end (fragP->fr_next, TRUE, 3) < 3
&& (branch_before_loop_end (fragP->fr_next)
|| (workaround_all_short_loops
&& current_opcode != XTENSA_UNDEFINED
&& current_opcode != xtensa_loop_opcode)))
{
if (fragP->tc_frag_data.is_no_transform)
as_bad (_("loop containing less than three instructions may trigger hardware errata"));
else
relax_frag_add_nop (fragP);
}
frag_wane (fragP);
}
}
}
}
static int unrelaxed_frag_min_insn_count (fragS *);
static int
count_insns_to_loop_end (fragS *base_fragP,
bfd_boolean count_relax_add,
int max_count)
{
fragS *fragP = NULL;
int insn_count = 0;
fragP = base_fragP;
for (; fragP && !fragP->tc_frag_data.is_loop_target; fragP = fragP->fr_next)
{
insn_count += unrelaxed_frag_min_insn_count (fragP);
if (insn_count >= max_count)
return max_count;
if (count_relax_add)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
{
insn_count++;
if (insn_count >= max_count)
return max_count;
}
}
}
return insn_count;
}
static int
unrelaxed_frag_min_insn_count (fragS *fragP)
{
xtensa_isa isa = xtensa_default_isa;
static xtensa_insnbuf insnbuf = NULL;
int insn_count = 0;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return insn_count;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
while (offset < fragP->fr_fix)
{
xtensa_format fmt;
xtensa_insnbuf_from_chars
(isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
{
as_fatal (_("undecodable instruction in instruction frag"));
return insn_count;
}
offset += xtensa_format_length (isa, fmt);
insn_count++;
}
return insn_count;
}
static bfd_boolean unrelaxed_frag_has_b_j (fragS *);
static bfd_boolean
branch_before_loop_end (fragS *base_fragP)
{
fragS *fragP;
for (fragP = base_fragP;
fragP && !fragP->tc_frag_data.is_loop_target;
fragP = fragP->fr_next)
{
if (unrelaxed_frag_has_b_j (fragP))
return TRUE;
}
return FALSE;
}
static bfd_boolean
unrelaxed_frag_has_b_j (fragS *fragP)
{
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return FALSE;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
while (offset < fragP->fr_fix)
{
xtensa_format fmt;
int slot;
xtensa_insnbuf_from_chars
(isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
return FALSE;
for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
{
xtensa_opcode opcode =
get_opcode_from_buf (fragP->fr_literal + offset, slot);
if (xtensa_opcode_is_branch (isa, opcode) == 1
|| xtensa_opcode_is_jump (isa, opcode) == 1)
return TRUE;
}
offset += xtensa_format_length (isa, fmt);
}
return FALSE;
}
static bfd_boolean is_empty_loop (const TInsn *, fragS *);
static bfd_boolean is_local_forward_loop (const TInsn *, fragS *);
static void
xtensa_sanity_check (void)
{
char *file_name;
unsigned line;
frchainS *frchP;
as_where (&file_name, &line);
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_IMMED)
{
static xtensa_insnbuf insnbuf = NULL;
TInsn t_insn;
if (fragP->fr_opcode != NULL)
{
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
tinsn_immed_from_frag (&t_insn, fragP, 0);
if (xtensa_opcode_is_loop (xtensa_default_isa,
t_insn.opcode) == 1)
{
if (is_empty_loop (&t_insn, fragP))
{
new_logical_line (fragP->fr_file, fragP->fr_line);
as_bad (_("invalid empty loop"));
}
if (!is_local_forward_loop (&t_insn, fragP))
{
new_logical_line (fragP->fr_file, fragP->fr_line);
as_bad (_("loop target does not follow "
"loop instruction in section"));
}
}
}
}
}
}
new_logical_line (file_name, line);
}
#define LOOP_IMMED_OPN 1
static bfd_boolean
is_empty_loop (const TInsn *insn, fragS *fragP)
{
const expressionS *expr;
symbolS *symbolP;
fragS *next_fragP;
if (insn->insn_type != ITYPE_INSN)
return FALSE;
if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
return FALSE;
if (insn->ntok <= LOOP_IMMED_OPN)
return FALSE;
expr = &insn->tok[LOOP_IMMED_OPN];
if (expr->X_op != O_symbol)
return FALSE;
symbolP = expr->X_add_symbol;
if (!symbolP)
return FALSE;
if (symbol_get_frag (symbolP) == NULL)
return FALSE;
if (S_GET_VALUE (symbolP) != 0)
return FALSE;
for (next_fragP = fragP->fr_next;
next_fragP != NULL;
next_fragP = next_fragP->fr_next)
{
if (next_fragP == symbol_get_frag (symbolP))
return TRUE;
if (next_fragP->fr_fix != 0)
return FALSE;
}
return FALSE;
}
static bfd_boolean
is_local_forward_loop (const TInsn *insn, fragS *fragP)
{
const expressionS *expr;
symbolS *symbolP;
fragS *next_fragP;
if (insn->insn_type != ITYPE_INSN)
return FALSE;
if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) == 0)
return FALSE;
if (insn->ntok <= LOOP_IMMED_OPN)
return FALSE;
expr = &insn->tok[LOOP_IMMED_OPN];
if (expr->X_op != O_symbol)
return FALSE;
symbolP = expr->X_add_symbol;
if (!symbolP)
return FALSE;
if (symbol_get_frag (symbolP) == NULL)
return FALSE;
for (next_fragP = fragP->fr_next;
next_fragP != NULL;
next_fragP = next_fragP->fr_next)
{
if (next_fragP == symbol_get_frag (symbolP))
return TRUE;
}
return FALSE;
}
static int
get_text_align_power (unsigned target_size)
{
int i = 0;
unsigned power = 1;
assert (target_size <= INT_MAX);
while (target_size > power)
{
power <<= 1;
i += 1;
}
return i;
}
static int
get_text_align_max_fill_size (int align_pow,
bfd_boolean use_nops,
bfd_boolean use_no_density)
{
if (!use_nops)
return (1 << align_pow);
if (use_no_density)
return 3 * (1 << align_pow);
return 1 + (1 << align_pow);
}
static int
get_text_align_fill_size (addressT address,
int align_pow,
int target_size,
bfd_boolean use_nops,
bfd_boolean use_no_density)
{
addressT alignment, fill, fill_limit, fill_step;
bfd_boolean skip_one = FALSE;
alignment = (1 << align_pow);
assert (target_size > 0 && alignment >= (addressT) target_size);
if (!use_nops)
{
fill_limit = alignment;
fill_step = 1;
}
else if (!use_no_density)
{
fill_limit = alignment * 2;
fill_step = 1;
skip_one = TRUE;
}
else
{
fill_limit = alignment * 3;
fill_step = 3;
}
for (fill = 0; fill < fill_limit; fill += fill_step)
{
if (skip_one && fill == 1)
continue;
if ((address + fill) >> align_pow
== (address + fill + target_size - 1) >> align_pow)
return fill;
}
assert (0);
return 0;
}
static int
branch_align_power (segT sec)
{
if (xtensa_fetch_width == 8)
{
if (get_recorded_alignment (sec) >= 3)
return 3;
}
else
assert (xtensa_fetch_width == 4);
return 2;
}
static int
get_text_align_nop_count (offsetT fill_size, bfd_boolean use_no_density)
{
int count = 0;
if (use_no_density)
{
assert (fill_size % 3 == 0);
return (fill_size / 3);
}
assert (fill_size != 1);
while (fill_size > 1)
{
int insn_size = 3;
if (fill_size == 2 || fill_size == 4)
insn_size = 2;
fill_size -= insn_size;
count++;
}
assert (fill_size != 1);
return count;
}
static int
get_text_align_nth_nop_size (offsetT fill_size,
int n,
bfd_boolean use_no_density)
{
int count = 0;
if (use_no_density)
return 3;
assert (fill_size != 1);
while (fill_size > 1)
{
int insn_size = 3;
if (fill_size == 2 || fill_size == 4)
insn_size = 2;
fill_size -= insn_size;
count++;
if (n + 1 == count)
return insn_size;
}
assert (0);
return 0;
}
static addressT
get_noop_aligned_address (fragS *fragP, addressT address)
{
int fill_size = 0;
int first_insn_size;
int loop_insn_size;
addressT pre_opcode_bytes;
int align_power;
fragS *first_insn;
xtensa_opcode opcode;
bfd_boolean is_loop;
assert (fragP->fr_type == rs_machine_dependent);
assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
first_insn = next_non_empty_frag (fragP);
first_insn = next_non_empty_frag (first_insn);
is_loop = next_frag_opcode_is_loop (fragP, &opcode);
assert (is_loop);
loop_insn_size = xg_get_single_size (opcode);
pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
pre_opcode_bytes += loop_insn_size;
if (first_insn == NULL)
return address;
assert (first_insn->tc_frag_data.is_first_loop_insn);
first_insn_size = frag_format_size (first_insn);
if (first_insn_size == 2 || first_insn_size == XTENSA_UNDEFINED)
first_insn_size = 3;
align_power = get_text_align_power (first_insn_size);
record_alignment (now_seg, align_power);
fill_size = get_text_align_fill_size
(address + pre_opcode_bytes, align_power, first_insn_size, TRUE,
fragP->tc_frag_data.is_no_density);
return address + fill_size;
}
static offsetT
get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
{
addressT target_address, loop_insn_offset;
int target_size;
xtensa_opcode loop_opcode;
bfd_boolean is_loop;
int align_power;
offsetT opt_diff;
addressT branch_align;
assert (fragP->fr_type == rs_machine_dependent);
switch (fragP->fr_subtype)
{
case RELAX_DESIRE_ALIGN:
target_size = next_frag_format_size (fragP);
if (target_size == XTENSA_UNDEFINED)
target_size = 3;
align_power = branch_align_power (now_seg);
branch_align = 1 << align_power;
if (target_size > branch_align)
target_size = branch_align;
opt_diff = get_text_align_fill_size (address, align_power,
target_size, FALSE, FALSE);
*max_diff = (opt_diff + branch_align
- (target_size + ((address + opt_diff) % branch_align)));
assert (*max_diff >= opt_diff);
return opt_diff;
case RELAX_ALIGN_NEXT_OPCODE:
target_size = next_frag_format_size (fragP);
loop_insn_offset = 0;
is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
assert (is_loop);
if (next_non_empty_frag(fragP)->tc_frag_data.slot_subtypes[0]
!= RELAX_IMMED)
loop_insn_offset = get_expanded_loop_offset (loop_opcode);
if (target_size == 2)
target_size = 3;
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;
assert (*max_diff >= opt_diff);
return opt_diff;
default:
break;
}
assert (0);
return 0;
}
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);
long
xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
{
xtensa_isa isa = xtensa_default_isa;
int unreported = fragP->tc_frag_data.unreported_expansion;
long new_stretch = 0;
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);
new_logical_line (fragP->fr_file, fragP->fr_line);
fragP->tc_frag_data.unreported_expansion = 0;
switch (fragP->fr_subtype)
{
case RELAX_ALIGN_NEXT_OPCODE:
if (fragP->tc_frag_data.relax_seen)
new_stretch = relax_frag_loop_align (fragP, stretch);
break;
case RELAX_LOOP_END:
break;
case RELAX_LOOP_END_ADD_NOP:
new_stretch = relax_frag_add_nop (fragP);
frag_wane (fragP);
break;
case RELAX_DESIRE_ALIGN:
break;
case RELAX_LITERAL:
case RELAX_LITERAL_FINAL:
return 0;
case RELAX_LITERAL_NR:
lit_size = 4;
fragP->fr_subtype = RELAX_LITERAL_FINAL;
assert (unreported == lit_size);
memset (&fragP->fr_literal[fragP->fr_fix], 0, 4);
fragP->fr_var -= lit_size;
fragP->fr_fix += lit_size;
new_stretch = 4;
break;
case RELAX_SLOTS:
if (vbuf == NULL)
vbuf = xtensa_insnbuf_alloc (isa);
xtensa_insnbuf_from_chars
(isa, vbuf, (unsigned char *) fragP->fr_opcode, 0);
fmt = xtensa_format_decode (isa, vbuf);
num_slots = xtensa_format_num_slots (isa, fmt);
for (slot = 0; slot < num_slots; slot++)
{
switch (fragP->tc_frag_data.slot_subtypes[slot])
{
case RELAX_NARROW:
if (fragP->tc_frag_data.relax_seen)
new_stretch += relax_frag_for_align (fragP, stretch);
break;
case RELAX_IMMED:
case RELAX_IMMED_STEP1:
case RELAX_IMMED_STEP2:
new_stretch += relax_frag_immed
(now_seg, fragP, stretch,
fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
fmt, slot, stretched_p, FALSE);
break;
default:
break;
}
}
break;
case RELAX_LITERAL_POOL_BEGIN:
case RELAX_LITERAL_POOL_END:
case RELAX_MAYBE_UNREACHABLE:
case RELAX_MAYBE_DESIRE_ALIGN:
break;
case RELAX_FILL_NOP:
case RELAX_UNREACHABLE:
if (fragP->tc_frag_data.relax_seen)
new_stretch += relax_frag_for_align (fragP, stretch);
break;
default:
as_bad (_("bad relaxation state"));
}
if (! fragP->tc_frag_data.relax_seen)
{
fragP->tc_frag_data.relax_seen = TRUE;
*stretched_p = 1;
}
new_logical_line (file_name, line);
return new_stretch;
}
static long
relax_frag_loop_align (fragS *fragP, long stretch)
{
addressT old_address, old_next_address, old_size;
addressT new_address, new_next_address, new_size;
addressT growth;
old_address = fragP->fr_address - stretch;
old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
fragP->tc_frag_data.text_expansion[0]);
old_size = old_next_address - old_address;
new_address = fragP->fr_address;
new_next_address =
get_noop_aligned_address (fragP, fragP->fr_address + fragP->fr_fix);
new_size = new_next_address - new_address;
growth = new_size - old_size;
fragP->tc_frag_data.text_expansion[0] += growth;
return growth;
}
static long
relax_frag_add_nop (fragS *fragP)
{
char *nop_buf = fragP->fr_literal + fragP->fr_fix;
int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
assemble_nop (length, nop_buf);
fragP->tc_frag_data.is_insn = TRUE;
if (fragP->fr_var < length)
{
as_fatal (_("fr_var (%ld) < length (%d)"), (long) fragP->fr_var, length);
return 0;
}
fragP->fr_fix += length;
fragP->fr_var -= length;
return length;
}
static long future_alignment_required (fragS *, long);
static long
relax_frag_for_align (fragS *fragP, long stretch)
{
long stretch_me;
long diff;
assert (fragP->fr_subtype == RELAX_FILL_NOP
|| fragP->fr_subtype == RELAX_UNREACHABLE
|| (fragP->fr_subtype == RELAX_SLOTS
&& fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
stretch_me = future_alignment_required (fragP, stretch);
diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
if (diff == 0)
return 0;
if (diff < 0)
{
long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
if (shrink <= stretch && stretch > 0)
{
fragP->tc_frag_data.text_expansion[0] = stretch_me;
return -shrink;
}
return 0;
}
fragP->tc_frag_data.text_expansion[0] = stretch_me;
return diff;
}
static addressT
find_address_of_next_align_frag (fragS **fragPP,
int *wide_nops,
int *narrow_nops,
int *widens,
bfd_boolean *paddable)
{
fragS *fragP = *fragPP;
addressT address = fragP->fr_address;
while (fragP)
{
if (*widens > 8)
{
*fragPP = fragP;
return 0;
}
address += fragP->fr_fix;
if (fragP->fr_type == rs_fill)
address += fragP->fr_offset * fragP->fr_var;
else if (fragP->fr_type == rs_machine_dependent)
{
switch (fragP->fr_subtype)
{
case RELAX_UNREACHABLE:
*paddable = TRUE;
break;
case RELAX_FILL_NOP:
(*wide_nops)++;
if (!fragP->tc_frag_data.is_no_density)
(*narrow_nops)++;
break;
case RELAX_SLOTS:
if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
{
(*widens)++;
break;
}
address += total_frag_text_expansion (fragP);;
break;
case RELAX_IMMED:
address += fragP->tc_frag_data.text_expansion[0];
break;
case RELAX_ALIGN_NEXT_OPCODE:
case RELAX_DESIRE_ALIGN:
*fragPP = fragP;
return address;
case RELAX_MAYBE_UNREACHABLE:
case RELAX_MAYBE_DESIRE_ALIGN:
break;
default:
*fragPP = fragP;
return 0;
}
}
else
{
*fragPP = fragP;
return 0;
}
fragP = fragP->fr_next;
}
*fragPP = fragP;
return 0;
}
static long bytes_to_stretch (fragS *, int, int, int, int);
#define LOOKAHEAD_ALIGNER 1
static long
future_alignment_required (fragS *fragP, long stretch ATTRIBUTE_UNUSED)
{
fragS *this_frag = fragP;
long address;
int num_widens = 0;
int wide_nops = 0;
int narrow_nops = 0;
bfd_boolean paddable = FALSE;
offsetT local_opt_diff;
offsetT opt_diff;
offsetT max_diff;
int stretch_amount = 0;
int local_stretch_amount;
int global_stretch_amount;
address = find_address_of_next_align_frag
(&fragP, &wide_nops, &narrow_nops, &num_widens, &paddable);
if (address)
{
local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
opt_diff = local_opt_diff;
assert (opt_diff >= 0);
assert (max_diff >= opt_diff);
if (max_diff == 0)
return 0;
#ifdef LOOKAHEAD_ALIGNER
if (fragP)
fragP = fragP->fr_next;
while (fragP && opt_diff < max_diff && address)
{
int glob_widens = 0;
int dnn = 0;
int dw = 0;
bfd_boolean glob_pad = 0;
address = find_address_of_next_align_frag
(&fragP, &glob_widens, &dnn, &dw, &glob_pad);
if (glob_pad || glob_widens >= (1 << branch_align_power (now_seg)))
break;
if (address)
{
offsetT next_m_diff;
offsetT next_o_diff;
address += stretch;
address -= this_frag->tc_frag_data.text_expansion[0];
address += opt_diff;
next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
next_o_diff += opt_diff;
next_m_diff += opt_diff;
if (next_o_diff <= max_diff && next_o_diff > opt_diff)
opt_diff = next_o_diff;
if (next_m_diff < max_diff)
max_diff = next_m_diff;
fragP = fragP->fr_next;
}
}
#endif
if (paddable)
{
if (this_frag->fr_subtype == RELAX_UNREACHABLE)
{
assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
return opt_diff;
}
return 0;
}
local_stretch_amount
= bytes_to_stretch (this_frag, wide_nops, narrow_nops,
num_widens, local_opt_diff);
#ifdef LOOKAHEAD_ALIGNER
global_stretch_amount
= bytes_to_stretch (this_frag, wide_nops, narrow_nops,
num_widens, opt_diff);
if (global_stretch_amount < local_stretch_amount)
stretch_amount = local_stretch_amount;
else
stretch_amount = global_stretch_amount;
#else
stretch_amount = local_stretch_amount;
#endif
if (this_frag->fr_subtype == RELAX_SLOTS
&& this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
assert (stretch_amount <= 1);
else if (this_frag->fr_subtype == RELAX_FILL_NOP)
{
if (this_frag->tc_frag_data.is_no_density)
assert (stretch_amount == 3 || stretch_amount == 0);
else
assert (stretch_amount <= 3);
}
}
return stretch_amount;
}
static long
bytes_to_stretch (fragS *this_frag,
int wide_nops,
int narrow_nops,
int num_widens,
int desired_diff)
{
int bytes_short = desired_diff - num_widens;
assert (desired_diff >= 0 && desired_diff < 8);
if (desired_diff == 0)
return 0;
assert (wide_nops > 0 || num_widens > 0);
if (bytes_short < 0)
{
return 0;
}
if (bytes_short == 0)
{
if (this_frag->fr_subtype == RELAX_FILL_NOP)
return 0;
else
return 1;
}
if (this_frag->fr_subtype == RELAX_FILL_NOP)
{
switch (desired_diff)
{
case 1:
return 0;
case 2:
if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
return 2;
return 0;
case 3:
if (wide_nops > 1)
return 0;
else
return 3;
case 4:
if (num_widens >= 1 && wide_nops == 1)
return 3;
if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
return 2;
return 0;
case 5:
if (num_widens >= 2 && wide_nops == 1)
return 3;
if (wide_nops < 2 || narrow_nops == 0)
return 0;
if (wide_nops > 2 && narrow_nops > 1)
return 0;
if (!this_frag->tc_frag_data.is_no_density)
return 2;
else
return 3;
return 0;
case 6:
if (wide_nops == 2)
return 3;
else if (num_widens >= 3 && wide_nops == 1)
return 3;
return 0;
case 7:
if (wide_nops == 1 && num_widens >= 4)
return 3;
else if (wide_nops == 2 && num_widens >= 1)
return 3;
return 0;
default:
assert (0);
}
}
else
{
switch (desired_diff)
{
case 1:
assert (0);
return 0;
case 2:
case 3:
return 0;
case 4:
if (wide_nops >= 1 && num_widens == 1)
return 1;
return 0;
case 5:
if (wide_nops >= 1 && num_widens == 2)
return 1;
return 0;
case 6:
if (wide_nops >= 2)
return 0;
else if (wide_nops >= 1 && num_widens == 3)
return 1;
return 0;
case 7:
if (wide_nops >= 1 && num_widens == 4)
return 1;
else if (wide_nops >= 2 && num_widens == 1)
return 1;
return 0;
default:
assert (0);
return 0;
}
}
assert (0);
return 0;
}
static long
relax_frag_immed (segT segP,
fragS *fragP,
long stretch,
int min_steps,
xtensa_format fmt,
int slot,
int *stretched_p,
bfd_boolean estimate_only)
{
TInsn tinsn;
vliw_insn orig_vinsn;
int old_size;
bfd_boolean negatable_branch = FALSE;
bfd_boolean branch_jmp_to_next = FALSE;
bfd_boolean wide_insn = FALSE;
xtensa_isa isa = xtensa_default_isa;
IStack istack;
offsetT frag_offset;
int num_steps;
fragS *lit_fragP;
int num_text_bytes, num_literal_bytes;
int literal_diff, total_text_diff, this_text_diff, first;
assert (fragP->fr_opcode != NULL);
xg_init_vinsn (&orig_vinsn);
vinsn_from_chars (&orig_vinsn, fragP->fr_opcode);
if (xtensa_format_num_slots (isa, fmt) > 1)
wide_insn = TRUE;
tinsn = orig_vinsn.slots[slot];
tinsn_immed_from_frag (&tinsn, fragP, slot);
if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode))
return 0;
if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
old_size = xtensa_format_length (isa, fmt);
if (branch_jmp_to_next
&& ((old_size == 2) || (old_size == 3))
&& !next_frag_is_loop_target (fragP))
return 0;
frag_offset = fragP->fr_opcode - fragP->fr_literal;
istack_init (&istack);
num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
min_steps, stretch);
if (num_steps < min_steps)
{
as_fatal (_("internal error: relaxation failed"));
return 0;
}
if (num_steps > RELAX_IMMED_MAXSTEPS)
{
as_fatal (_("internal error: relaxation requires too many steps"));
return 0;
}
fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
lit_fragP = 0;
num_literal_bytes = get_num_stack_literal_bytes (&istack);
literal_diff =
num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
first = 0;
while (istack.insn[first].opcode == XTENSA_UNDEFINED)
first++;
num_text_bytes = get_num_stack_text_bytes (&istack);
if (wide_insn)
{
num_text_bytes += old_size;
if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
}
total_text_diff = num_text_bytes - old_size;
this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
assert (num_text_bytes >= 0);
assert (literal_diff >= 0);
assert (total_text_diff >= 0);
fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
if (literal_diff != 0)
{
lit_fragP = fragP->tc_frag_data.literal_frags[slot];
if (lit_fragP)
{
assert (literal_diff == 4);
lit_fragP->tc_frag_data.unreported_expansion += literal_diff;
assert (lit_fragP->fr_type == rs_machine_dependent
&& lit_fragP->fr_subtype == RELAX_LITERAL);
lit_fragP->fr_subtype = RELAX_LITERAL_NR;
(*stretched_p)++;
}
}
if (negatable_branch && istack.ninsn > 1)
update_next_frag_state (fragP);
return this_text_diff;
}
static void convert_frag_align_next_opcode (fragS *);
static void convert_frag_narrow (segT, fragS *, xtensa_format, int);
static void convert_frag_fill_nop (fragS *);
static void convert_frag_immed (segT, fragS *, int, xtensa_format, int);
void
md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
{
static xtensa_insnbuf vbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
int slot;
int num_slots;
xtensa_format fmt;
char *file_name;
unsigned line;
as_where (&file_name, &line);
new_logical_line (fragp->fr_file, fragp->fr_line);
switch (fragp->fr_subtype)
{
case RELAX_ALIGN_NEXT_OPCODE:
convert_frag_align_next_opcode (fragp);
break;
case RELAX_DESIRE_ALIGN:
break;
case RELAX_LITERAL:
case RELAX_LITERAL_FINAL:
break;
case RELAX_SLOTS:
if (vbuf == NULL)
vbuf = xtensa_insnbuf_alloc (isa);
xtensa_insnbuf_from_chars
(isa, vbuf, (unsigned char *) fragp->fr_opcode, 0);
fmt = xtensa_format_decode (isa, vbuf);
num_slots = xtensa_format_num_slots (isa, fmt);
for (slot = 0; slot < num_slots; slot++)
{
switch (fragp->tc_frag_data.slot_subtypes[slot])
{
case RELAX_NARROW:
convert_frag_narrow (sec, fragp, fmt, slot);
break;
case RELAX_IMMED:
case RELAX_IMMED_STEP1:
case RELAX_IMMED_STEP2:
convert_frag_immed
(sec, fragp,
fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
fmt, slot);
break;
default:
break;
}
}
break;
case RELAX_UNREACHABLE:
memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
frag_wane (fragp);
break;
case RELAX_MAYBE_UNREACHABLE:
case RELAX_MAYBE_DESIRE_ALIGN:
frag_wane (fragp);
break;
case RELAX_FILL_NOP:
convert_frag_fill_nop (fragp);
break;
case RELAX_LITERAL_NR:
if (use_literal_section)
{
fragS *f;
fragp->fr_subtype = RELAX_LITERAL_FINAL;
assert (fragp->tc_frag_data.unreported_expansion == 4);
memset (&fragp->fr_literal[fragp->fr_fix], 0, 4);
fragp->fr_var -= 4;
fragp->fr_fix += 4;
for (f = fragp->fr_next; f; f = f->fr_next)
f->fr_address += 4;
}
else
as_bad (_("invalid relaxation fragment result"));
break;
}
fragp->fr_var = 0;
new_logical_line (file_name, line);
}
static void
convert_frag_align_next_opcode (fragS *fragp)
{
char *nop_buf;
bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
addressT aligned_address;
offsetT fill_size;
int nop, nop_count;
aligned_address = get_noop_aligned_address (fragp, fragp->fr_address +
fragp->fr_fix);
fill_size = aligned_address - (fragp->fr_address + fragp->fr_fix);
nop_count = get_text_align_nop_count (fill_size, use_no_density);
nop_buf = fragp->fr_literal + fragp->fr_fix;
for (nop = 0; nop < nop_count; nop++)
{
int nop_size;
nop_size = get_text_align_nth_nop_size (fill_size, nop, use_no_density);
assemble_nop (nop_size, nop_buf);
nop_buf += nop_size;
}
fragp->fr_fix += fill_size;
fragp->fr_var -= fill_size;
}
static void
convert_frag_narrow (segT segP, fragS *fragP, xtensa_format fmt, int slot)
{
TInsn tinsn, single_target;
xtensa_format single_fmt;
int size, old_size, diff, error_val;
offsetT frag_offset;
assert (slot == 0);
tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
if (xtensa_opcode_is_branch (xtensa_default_isa, tinsn.opcode) == 1)
{
assert (fragP->tc_frag_data.text_expansion[0] == 1
|| fragP->tc_frag_data.text_expansion[0] == 0);
convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
fmt, slot);
return;
}
if (fragP->tc_frag_data.text_expansion[0] == 0)
{
fragP->fr_var = 0;
return;
}
assert (fragP->fr_opcode != NULL);
tinsn_immed_from_frag (&tinsn, fragP, 0);
size = 0;
old_size = xg_get_single_size (tinsn.opcode);
tinsn_init (&single_target);
frag_offset = fragP->fr_opcode - fragP->fr_literal;
error_val = xg_expand_narrow (&single_target, &tinsn);
if (error_val)
{
as_bad (_("unable to widen instruction"));
return;
}
size = xg_get_single_size (single_target.opcode);
single_fmt = xg_get_single_format (single_target.opcode);
xg_emit_insn_to_buf (&single_target, single_fmt, fragP->fr_opcode,
fragP, frag_offset, TRUE);
diff = size - old_size;
assert (diff >= 0);
assert (diff <= fragP->fr_var);
fragP->fr_var -= diff;
fragP->fr_fix += diff;
fragP->fr_var = 0;
}
static void
convert_frag_fill_nop (fragS *fragP)
{
char *loc = &fragP->fr_literal[fragP->fr_fix];
int size = fragP->tc_frag_data.text_expansion[0];
assert ((unsigned) size == (fragP->fr_next->fr_address
- fragP->fr_address - fragP->fr_fix));
if (size == 0)
{
fragP->fr_var = 0;
return;
}
assemble_nop (size, loc);
fragP->tc_frag_data.is_insn = TRUE;
fragP->fr_var -= size;
fragP->fr_fix += size;
frag_wane (fragP);
}
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,
fragS *fragP,
int min_steps,
xtensa_format fmt,
int slot)
{
char *immed_instr = fragP->fr_opcode;
TInsn orig_tinsn;
bfd_boolean expanded = FALSE;
bfd_boolean branch_jmp_to_next = FALSE;
char *fr_opcode = fragP->fr_opcode;
vliw_insn orig_vinsn;
xtensa_isa isa = xtensa_default_isa;
bfd_boolean wide_insn = FALSE;
int bytes;
bfd_boolean is_loop;
assert (fr_opcode != NULL);
xg_init_vinsn (&orig_vinsn);
vinsn_from_chars (&orig_vinsn, fr_opcode);
if (xtensa_format_num_slots (isa, fmt) > 1)
wide_insn = TRUE;
orig_tinsn = orig_vinsn.slots[slot];
tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
branch_jmp_to_next = is_branch_jmp_to_next (&orig_tinsn, fragP);
if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
{
bytes = xtensa_format_length (isa, fmt);
if (bytes >= 4)
{
orig_vinsn.slots[slot].opcode =
xtensa_format_slot_nop_opcode (isa, orig_vinsn.format, slot);
orig_vinsn.slots[slot].ntok = 0;
}
else
{
bytes += fragP->tc_frag_data.text_expansion[0];
assert (bytes == 2 || bytes == 3);
build_nop (&orig_vinsn.slots[0], bytes);
fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
}
vinsn_to_insnbuf (&orig_vinsn, fr_opcode, frag_now, FALSE);
xtensa_insnbuf_to_chars
(isa, orig_vinsn.insnbuf, (unsigned char *) fr_opcode, 0);
fragP->fr_var = 0;
}
else
{
IStack istack;
int i;
symbolS *lit_sym = NULL;
int total_size = 0;
int target_offset = 0;
int old_size;
int diff;
symbolS *gen_label = NULL;
offsetT frag_offset;
bfd_boolean first = TRUE;
bfd_boolean last_is_jump;
frag_offset = fr_opcode - fragP->fr_literal;
istack_init (&istack);
xg_assembly_relax (&istack, &orig_tinsn,
segP, fragP, frag_offset, min_steps, 0);
old_size = xtensa_format_length (isa, fmt);
target_offset = 0;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *tinsn = &istack.insn[i];
fragS *lit_frag;
switch (tinsn->insn_type)
{
case ITYPE_LITERAL:
if (lit_sym != NULL)
as_bad (_("multiple literals in expansion"));
lit_frag = fragP->tc_frag_data.literal_frags[slot];
if (lit_frag == NULL)
as_bad (_("no registered fragment for literal"));
if (tinsn->ntok != 1)
as_bad (_("number of literal tokens != 1"));
lit_sym = lit_frag->fr_symbol;
break;
case ITYPE_LABEL:
if (align_targets && !is_loop)
{
fragS *unreach = fragP->fr_next;
while (!(unreach->fr_type == rs_machine_dependent
&& (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
|| unreach->fr_subtype == RELAX_UNREACHABLE)))
{
unreach = unreach->fr_next;
}
assert (unreach->fr_type == rs_machine_dependent
&& (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
|| unreach->fr_subtype == RELAX_UNREACHABLE));
target_offset += unreach->tc_frag_data.text_expansion[0];
}
assert (gen_label == NULL);
gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
fr_opcode - fragP->fr_literal
+ target_offset, fragP);
break;
case ITYPE_INSN:
if (first && wide_insn)
{
target_offset += xtensa_format_length (isa, fmt);
first = FALSE;
if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
target_offset += xg_get_single_size (tinsn->opcode);
}
else
target_offset += xg_get_single_size (tinsn->opcode);
break;
}
}
total_size = 0;
first = TRUE;
last_is_jump = FALSE;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *tinsn = &istack.insn[i];
fragS *lit_frag;
int size;
segT target_seg;
bfd_reloc_code_real_type reloc_type;
switch (tinsn->insn_type)
{
case ITYPE_LITERAL:
lit_frag = fragP->tc_frag_data.literal_frags[slot];
assert (lit_frag != NULL);
assert (lit_sym != NULL);
assert (tinsn->ntok == 1);
target_seg = S_GET_SEGMENT (lit_sym);
assert (target_seg);
if (tinsn->tok[0].X_op == O_pltrel)
reloc_type = BFD_RELOC_XTENSA_PLT;
else
reloc_type = BFD_RELOC_32;
fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
&tinsn->tok[0], FALSE, reloc_type);
break;
case ITYPE_LABEL:
break;
case ITYPE_INSN:
xg_resolve_labels (tinsn, gen_label);
xg_resolve_literals (tinsn, lit_sym);
if (wide_insn && first)
{
first = FALSE;
if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
{
tinsn->record_fix = TRUE;
orig_vinsn.slots[slot] = *tinsn;
}
else
{
orig_vinsn.slots[slot].opcode =
xtensa_format_slot_nop_opcode (isa, fmt, slot);
orig_vinsn.slots[slot].ntok = 0;
orig_vinsn.slots[slot].record_fix = FALSE;
}
vinsn_to_insnbuf (&orig_vinsn, immed_instr, fragP, TRUE);
xtensa_insnbuf_to_chars (isa, orig_vinsn.insnbuf,
(unsigned char *) immed_instr, 0);
fragP->tc_frag_data.is_insn = TRUE;
size = xtensa_format_length (isa, fmt);
if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
{
xtensa_format single_fmt =
xg_get_single_format (tinsn->opcode);
xg_emit_insn_to_buf
(tinsn, single_fmt, immed_instr + size, fragP,
immed_instr - fragP->fr_literal + size, TRUE);
size += xg_get_single_size (tinsn->opcode);
}
}
else
{
xtensa_format single_format;
size = xg_get_single_size (tinsn->opcode);
single_format = xg_get_single_format (tinsn->opcode);
xg_emit_insn_to_buf (tinsn, single_format, immed_instr,
fragP,
immed_instr - fragP->fr_literal, TRUE);
}
immed_instr += size;
total_size += size;
break;
}
}
diff = total_size - old_size;
assert (diff >= 0);
if (diff != 0)
expanded = TRUE;
assert (diff <= fragP->fr_var);
fragP->fr_var -= diff;
fragP->fr_fix += diff;
}
xg_free_vinsn (&orig_vinsn);
if (is_loop)
{
symbolS *sym;
sym = orig_tinsn.tok[1].X_add_symbol;
if (sym != NULL && !S_IS_DEFINED (sym))
{
as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
return;
}
sym = orig_tinsn.tok[1].X_op_symbol;
if (sym != NULL && !S_IS_DEFINED (sym))
{
as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
return;
}
}
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))
{
fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
&orig_tinsn.tok[0], TRUE,
BFD_RELOC_XTENSA_ASM_EXPAND);
}
}
static fixS *
fix_new_exp_in_seg (segT new_seg,
subsegT new_subseg,
fragS *frag,
int where,
int size,
expressionS *exp,
int pcrel,
bfd_reloc_code_real_type r_type)
{
fixS *new_fix;
segT seg = now_seg;
subsegT subseg = now_subseg;
assert (new_seg != 0);
subseg_set (new_seg, new_subseg);
new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
subseg_set (seg, subseg);
return new_fix;
}
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);
loop_offset = get_expanded_loop_offset (tinsn->opcode);
tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
tinsn_immed_from_frag (&loop_insn, fragP, 0);
assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
addi_offset += loop_offset;
addmi_offset += loop_offset;
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)
{
symbolS *sym = tinsn->tok[1].X_add_symbol;
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;
}
know (symbolP);
know (symbolP->sy_frag);
know (!(S_GET_SEGMENT (symbolP) == absolute_section)
|| symbol_get_frag (symbolP) == &zero_address_frag);
loop_length = target - (fragP->fr_address + fragP->fr_fix);
loop_length_hi = loop_length & ~0x0ff;
loop_length_lo = loop_length & 0x0ff;
if (loop_length_lo >= 128)
{
loop_length_lo -= 256;
loop_length_hi += 256;
}
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);
assert (addi_insn.opcode == xtensa_addi_opcode);
tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
assert (addmi_insn.opcode == xtensa_addmi_opcode);
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);
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;
}
}
typedef struct subseg_map_struct
{
segT seg;
subsegT subseg;
unsigned flags;
float total_freq;
float target_freq;
struct subseg_map_struct *next;
} subseg_map;
static subseg_map *sseg_map = NULL;
static subseg_map *
get_subseg_info (segT seg, subsegT subseg)
{
subseg_map *subseg_e;
for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
{
if (seg == subseg_e->seg && subseg == subseg_e->subseg)
break;
}
return subseg_e;
}
static subseg_map *
add_subseg_info (segT seg, subsegT subseg)
{
subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
memset (subseg_e, 0, sizeof (subseg_map));
subseg_e->seg = seg;
subseg_e->subseg = subseg;
subseg_e->flags = 0;
subseg_e->target_freq = 1.0;
subseg_e->total_freq = 1.0;
subseg_e->next = sseg_map;
sseg_map = subseg_e;
return subseg_e;
}
static unsigned
get_last_insn_flags (segT seg, subsegT subseg)
{
subseg_map *subseg_e = get_subseg_info (seg, subseg);
if (subseg_e)
return subseg_e->flags;
return 0;
}
static void
set_last_insn_flags (segT seg,
subsegT subseg,
unsigned fl,
bfd_boolean val)
{
subseg_map *subseg_e = get_subseg_info (seg, subseg);
if (! subseg_e)
subseg_e = add_subseg_info (seg, subseg);
if (val)
subseg_e->flags |= fl;
else
subseg_e->flags &= ~fl;
}
static float
get_subseg_total_freq (segT seg, subsegT subseg)
{
subseg_map *subseg_e = get_subseg_info (seg, subseg);
if (subseg_e)
return subseg_e->total_freq;
return 1.0;
}
static float
get_subseg_target_freq (segT seg, subsegT subseg)
{
subseg_map *subseg_e = get_subseg_info (seg, subseg);
if (subseg_e)
return subseg_e->target_freq;
return 1.0;
}
static void
set_subseg_freq (segT seg, subsegT subseg, float total_f, float target_f)
{
subseg_map *subseg_e = get_subseg_info (seg, subseg);
if (! subseg_e)
subseg_e = add_subseg_info (seg, subseg);
subseg_e->total_freq = total_f;
subseg_e->target_freq = target_f;
}
static void
xtensa_remove_section (segT sec)
{
segT *ps_next_ptr = &stdoutput->sections;
while (*ps_next_ptr != sec && *ps_next_ptr != NULL)
ps_next_ptr = &(*ps_next_ptr)->next;
assert (*ps_next_ptr != NULL);
bfd_section_list_remove (stdoutput, ps_next_ptr);
}
static void
xtensa_insert_section (segT after_sec, segT sec)
{
segT *after_sec_next;
if (after_sec == NULL)
after_sec_next = &stdoutput->sections;
else
after_sec_next = &after_sec->next;
bfd_section_list_insert (stdoutput, after_sec_next, sec);
}
static void
xtensa_move_seg_list_to_beginning (seg_list *head)
{
head = head->next;
while (head)
{
segT literal_section = head->seg;
assert (literal_section);
xtensa_remove_section (literal_section);
xtensa_insert_section (NULL, literal_section);
head = head->next;
}
}
static void mark_literal_frags (seg_list *);
static void
xtensa_move_literals (void)
{
seg_list *segment;
frchainS *frchain_from, *frchain_to;
fragS *search_frag, *next_frag, *last_frag, *literal_pool, *insert_after;
fragS **frag_splice;
emit_state state;
segT dest_seg;
fixS *fix, *next_fix, **fix_splice;
sym_list *lit;
mark_literal_frags (literal_head->next);
mark_literal_frags (init_literal_head->next);
mark_literal_frags (fini_literal_head->next);
if (use_literal_section)
return;
segment = literal_head->next;
while (segment)
{
frchain_from = seg_info (segment->seg)->frchainP;
search_frag = frchain_from->frch_root;
literal_pool = NULL;
frchain_to = NULL;
frag_splice = &(frchain_from->frch_root);
while (!search_frag->tc_frag_data.literal_frag)
{
assert (search_frag->fr_fix == 0
|| search_frag->fr_type == rs_align);
search_frag = search_frag->fr_next;
}
assert (search_frag->tc_frag_data.literal_frag->fr_subtype
== RELAX_LITERAL_POOL_BEGIN);
xtensa_switch_section_emit_state (&state, segment->seg, 0);
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
last_frag = frag_now;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_set_frag_assembly_state (frag_now);
while (search_frag != frag_now)
{
next_frag = search_frag->fr_next;
if (search_frag->tc_frag_data.literal_frag)
{
literal_pool = search_frag->tc_frag_data.literal_frag;
assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
frchain_to = literal_pool->tc_frag_data.lit_frchain;
assert (frchain_to);
}
insert_after = literal_pool;
while (insert_after->fr_next->fr_subtype != RELAX_LITERAL_POOL_END)
insert_after = insert_after->fr_next;
dest_seg = insert_after->fr_next->tc_frag_data.lit_seg;
*frag_splice = next_frag;
search_frag->fr_next = insert_after->fr_next;
insert_after->fr_next = search_frag;
search_frag->tc_frag_data.lit_seg = dest_seg;
fix = frchain_from->fix_root;
fix_splice = &(frchain_from->fix_root);
while (fix)
{
next_fix = fix->fx_next;
if (fix->fx_frag == search_frag)
{
*fix_splice = next_fix;
fix->fx_next = frchain_to->fix_root;
frchain_to->fix_root = fix;
if (frchain_to->fix_tail == NULL)
frchain_to->fix_tail = fix;
}
else
fix_splice = &(fix->fx_next);
fix = next_fix;
}
search_frag = next_frag;
}
if (frchain_from->fix_root != NULL)
{
frchain_from = seg_info (segment->seg)->frchainP;
as_warn (_("fixes not all moved from %s"), segment->seg->name);
assert (frchain_from->fix_root == NULL);
}
frchain_from->fix_tail = NULL;
xtensa_restore_emit_state (&state);
segment = segment->next;
}
for (lit = literal_syms; lit; lit = lit->next)
{
symbolS *lit_sym = lit->sym;
segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
if (dest_seg)
S_SET_SEGMENT (lit_sym, dest_seg);
}
}
static void
mark_literal_frags (seg_list *segment)
{
frchainS *frchain_from;
fragS *search_frag;
while (segment)
{
frchain_from = seg_info (segment->seg)->frchainP;
search_frag = frchain_from->frch_root;
while (search_frag)
{
search_frag->tc_frag_data.is_literal = TRUE;
search_frag = search_frag->fr_next;
}
segment = segment->next;
}
}
static void
xtensa_reorder_seg_list (seg_list *head, segT after)
{
head = head->next;
while (head)
{
segT literal_section = head->seg;
assert (literal_section);
if (literal_section != after)
{
xtensa_remove_section (literal_section);
xtensa_insert_section (after, literal_section);
}
head = head->next;
}
}
static void
xtensa_reorder_segments (void)
{
segT sec;
segT last_sec = 0;
int old_count = 0;
int new_count = 0;
for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
{
last_sec = sec;
old_count++;
}
xtensa_reorder_seg_list (literal_head, last_sec);
xtensa_reorder_seg_list (init_literal_head, last_sec);
xtensa_reorder_seg_list (fini_literal_head, last_sec);
for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
new_count++;
assert (new_count == old_count);
}
static void
xtensa_switch_to_literal_fragment (emit_state *result)
{
if (directive_state[directive_absolute_literals])
{
cache_literal_section (0, default_lit_sections.lit4_seg_name,
&default_lit_sections.lit4_seg, FALSE);
xtensa_switch_section_emit_state (result,
default_lit_sections.lit4_seg, 0);
}
else
xtensa_switch_to_non_abs_literal_fragment (result);
frag_align (2, 0, 0);
record_alignment (now_seg, 2);
}
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);
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));
if (pool_location == NULL
&& !use_literal_section
&& !recursive
&& !is_init && ! is_fini)
{
as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
recursive = TRUE;
xtensa_mark_literal_pool_location ();
recursive = FALSE;
}
if (is_init)
{
cache_literal_section (init_literal_head,
default_lit_sections.init_lit_seg_name,
&default_lit_sections.init_lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.init_lit_seg, 0);
}
else if (is_fini)
{
cache_literal_section (fini_literal_head,
default_lit_sections.fini_lit_seg_name,
&default_lit_sections.fini_lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.fini_lit_seg, 0);
}
else
{
cache_literal_section (literal_head,
default_lit_sections.lit_seg_name,
&default_lit_sections.lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.lit_seg, 0);
}
if (!use_literal_section
&& !is_init && !is_fini
&& get_literal_pool_location (now_seg) != pool_location)
{
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_set_frag_assembly_state (frag_now);
}
}
static void
xtensa_switch_section_emit_state (emit_state *state,
segT new_now_seg,
subsegT new_now_subseg)
{
state->name = now_seg->name;
state->now_seg = now_seg;
state->now_subseg = now_subseg;
state->generating_literals = generating_literals;
generating_literals++;
subseg_set (new_now_seg, new_now_subseg);
}
static void
xtensa_restore_emit_state (emit_state *state)
{
generating_literals = state->generating_literals;
subseg_set (state->now_seg, state->now_subseg);
}
static void
cache_literal_section (seg_list *head,
const char *name,
segT *pseg,
bfd_boolean is_code)
{
segT current_section = now_seg;
int current_subsec = now_subseg;
segT seg;
if (*pseg != 0)
return;
for (seg = stdoutput->sections; seg; seg = seg->next)
{
if (!strcmp (segment_name (seg), name))
break;
}
if (!seg)
{
seg = subseg_new (name, (subsegT) 0);
if (head)
{
seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
n->seg = seg;
n->next = head->next;
head->next = n;
}
bfd_set_section_flags (stdoutput, seg, SEC_HAS_CONTENTS |
SEC_READONLY | SEC_ALLOC | SEC_LOAD
| (is_code ? SEC_CODE : SEC_DATA));
bfd_set_section_alignment (stdoutput, seg, 2);
}
*pseg = seg;
subseg_set (current_section, current_subsec);
}
#define XTENSA_INSN_SEC_NAME ".xt.insn"
#define XTENSA_LIT_SEC_NAME ".xt.lit"
#define XTENSA_PROP_SEC_NAME ".xt.prop"
typedef bfd_boolean (*frag_predicate) (const fragS *);
typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
static bfd_boolean get_frag_is_literal (const fragS *);
static void xtensa_create_property_segments
(frag_predicate, frag_predicate, const char *, xt_section_type);
static void xtensa_create_xproperty_segments
(frag_flags_fn, const char *, xt_section_type);
static segment_info_type *retrieve_segment_info (segT);
static segT retrieve_xtensa_section (char *);
static bfd_boolean section_has_property (segT, frag_predicate);
static bfd_boolean section_has_xproperty (segT, frag_flags_fn);
static void add_xt_block_frags
(segT, segT, xtensa_block_info **, frag_predicate, frag_predicate);
static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
static void xtensa_frag_flags_init (frag_flags *);
static void get_frag_property_flags (const fragS *, frag_flags *);
static bfd_vma frag_flags_to_number (const frag_flags *);
static void add_xt_prop_frags
(segT, segT, xtensa_block_info **, frag_flags_fn);
void
xtensa_post_relax_hook (void)
{
xtensa_move_seg_list_to_beginning (literal_head);
xtensa_move_seg_list_to_beginning (init_literal_head);
xtensa_move_seg_list_to_beginning (fini_literal_head);
xtensa_find_unmarked_state_frags ();
if (use_literal_section)
xtensa_create_property_segments (get_frag_is_literal,
NULL,
XTENSA_LIT_SEC_NAME,
xt_literal_sec);
xtensa_create_xproperty_segments (get_frag_property_flags,
XTENSA_PROP_SEC_NAME,
xt_prop_sec);
if (warn_unaligned_branch_targets)
bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
}
static bfd_boolean
get_frag_is_literal (const fragS *fragP)
{
assert (fragP != NULL);
return fragP->tc_frag_data.is_literal;
}
static void
xtensa_create_property_segments (frag_predicate property_function,
frag_predicate end_property_function,
const char *section_name_base,
xt_section_type sec_type)
{
segT *seclist;
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segT sec = *seclist;
flagword flags;
flags = bfd_get_section_flags (stdoutput, sec);
if (flags & SEC_DEBUGGING)
continue;
if (!(flags & SEC_ALLOC))
continue;
if (section_has_property (sec, property_function))
{
char *property_section_name =
xtensa_get_property_section_name (sec, section_name_base);
segT insn_sec = retrieve_xtensa_section (property_section_name);
segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
xtensa_block_info **xt_blocks =
&xt_seg_info->tc_segment_info_data.blocks[sec_type];
add_xt_block_frags (sec, insn_sec, xt_blocks, property_function,
end_property_function);
}
}
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segment_info_type *seginfo;
xtensa_block_info *block;
segT sec = *seclist;
seginfo = seg_info (sec);
block = seginfo->tc_segment_info_data.blocks[sec_type];
if (block)
{
xtensa_block_info *cur_block;
int num_recs = 0;
bfd_size_type rec_size;
for (cur_block = block; cur_block; cur_block = cur_block->next)
num_recs++;
rec_size = num_recs * 8;
bfd_set_section_size (stdoutput, sec, rec_size);
if (num_recs)
{
fragS *fragP;
bfd_size_type frag_size;
fixS *fixes;
frchainS *frchainP;
int i;
char *frag_data;
frag_size = sizeof (fragS) + rec_size;
fragP = (fragS *) xmalloc (frag_size);
memset (fragP, 0, frag_size);
fragP->fr_address = 0;
fragP->fr_next = NULL;
fragP->fr_fix = rec_size;
fragP->fr_var = 0;
fragP->fr_type = rs_fill;
frchainP = seginfo->frchainP;
frchainP->frch_root = fragP;
frchainP->frch_last = fragP;
fixes = (fixS *) xmalloc (sizeof (fixS) * num_recs);
memset (fixes, 0, sizeof (fixS) * num_recs);
seginfo->fix_root = fixes;
seginfo->fix_tail = &fixes[num_recs - 1];
cur_block = block;
frag_data = &fragP->fr_literal[0];
for (i = 0; i < num_recs; i++)
{
fixS *fix = &fixes[i];
assert (cur_block);
if (i != num_recs - 1)
fix->fx_next = &fixes[i + 1];
else
fix->fx_next = NULL;
fix->fx_size = 4;
fix->fx_done = 0;
fix->fx_frag = fragP;
fix->fx_where = i * 8;
fix->fx_addsy = section_symbol (cur_block->sec);
fix->fx_offset = cur_block->offset;
fix->fx_r_type = BFD_RELOC_32;
fix->fx_file = "Internal Assembly";
fix->fx_line = 0;
md_number_to_chars (&frag_data[4 + 8 * i],
cur_block->size, 4);
cur_block = cur_block->next;
}
}
}
}
}
static void
xtensa_create_xproperty_segments (frag_flags_fn flag_fn,
const char *section_name_base,
xt_section_type sec_type)
{
segT *seclist;
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segT sec = *seclist;
flagword flags;
flags = bfd_get_section_flags (stdoutput, sec);
if ((flags & SEC_DEBUGGING)
|| !(flags & SEC_ALLOC)
|| (flags & SEC_MERGE))
continue;
if (section_has_xproperty (sec, flag_fn))
{
char *property_section_name =
xtensa_get_property_section_name (sec, section_name_base);
segT insn_sec = retrieve_xtensa_section (property_section_name);
segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
xtensa_block_info **xt_blocks =
&xt_seg_info->tc_segment_info_data.blocks[sec_type];
add_xt_prop_frags (sec, insn_sec, xt_blocks, flag_fn);
}
}
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segment_info_type *seginfo;
xtensa_block_info *block;
segT sec = *seclist;
seginfo = seg_info (sec);
block = seginfo->tc_segment_info_data.blocks[sec_type];
if (block)
{
xtensa_block_info *cur_block;
int num_recs = 0;
bfd_size_type rec_size;
for (cur_block = block; cur_block; cur_block = cur_block->next)
num_recs++;
rec_size = num_recs * (8 + 4);
bfd_set_section_size (stdoutput, sec, rec_size);
if (num_recs)
{
fragS *fragP;
bfd_size_type frag_size;
fixS *fixes;
frchainS *frchainP;
int i;
char *frag_data;
frag_size = sizeof (fragS) + rec_size;
fragP = (fragS *) xmalloc (frag_size);
memset (fragP, 0, frag_size);
fragP->fr_address = 0;
fragP->fr_next = NULL;
fragP->fr_fix = rec_size;
fragP->fr_var = 0;
fragP->fr_type = rs_fill;
frchainP = seginfo->frchainP;
frchainP->frch_root = fragP;
frchainP->frch_last = fragP;
fixes = (fixS *) xmalloc (sizeof (fixS) * num_recs);
memset (fixes, 0, sizeof (fixS) * num_recs);
seginfo->fix_root = fixes;
seginfo->fix_tail = &fixes[num_recs - 1];
cur_block = block;
frag_data = &fragP->fr_literal[0];
for (i = 0; i < num_recs; i++)
{
fixS *fix = &fixes[i];
assert (cur_block);
if (i != num_recs - 1)
fix->fx_next = &fixes[i + 1];
else
fix->fx_next = NULL;
fix->fx_size = 4;
fix->fx_done = 0;
fix->fx_frag = fragP;
fix->fx_where = i * (8 + 4);
fix->fx_addsy = section_symbol (cur_block->sec);
fix->fx_offset = cur_block->offset;
fix->fx_r_type = BFD_RELOC_32;
fix->fx_file = "Internal Assembly";
fix->fx_line = 0;
md_number_to_chars (&frag_data[4 + (8+4) * i],
cur_block->size, 4);
md_number_to_chars (&frag_data[8 + (8+4) * i],
frag_flags_to_number (&cur_block->flags),
4);
cur_block = cur_block->next;
}
}
}
}
}
static segment_info_type *
retrieve_segment_info (segT seg)
{
segment_info_type *seginfo;
seginfo = (segment_info_type *) bfd_get_section_userdata (stdoutput, seg);
if (!seginfo)
{
frchainS *frchainP;
seginfo = (segment_info_type *) xmalloc (sizeof (*seginfo));
memset ((void *) seginfo, 0, sizeof (*seginfo));
seginfo->fix_root = NULL;
seginfo->fix_tail = NULL;
seginfo->bfd_section = seg;
seginfo->sym = 0;
bfd_set_section_userdata (stdoutput, seg, (void *) seginfo);
frchainP = (frchainS *) xmalloc (sizeof (frchainS));
frchainP->frch_root = NULL;
frchainP->frch_last = NULL;
frchainP->frch_next = NULL;
frchainP->frch_seg = seg;
frchainP->frch_subseg = 0;
frchainP->fix_root = NULL;
frchainP->fix_tail = NULL;
frchainP->frch_frag_now = NULL;
seginfo->frchainP = frchainP;
}
return seginfo;
}
static segT
retrieve_xtensa_section (char *sec_name)
{
bfd *abfd = stdoutput;
flagword flags, out_flags, link_once_flags;
segT s;
flags = bfd_get_section_flags (abfd, now_seg);
link_once_flags = (flags & SEC_LINK_ONCE);
if (link_once_flags)
link_once_flags |= (flags & SEC_LINK_DUPLICATES);
out_flags = (SEC_RELOC | SEC_HAS_CONTENTS | SEC_READONLY | link_once_flags);
s = bfd_make_section_old_way (abfd, sec_name);
if (s == NULL)
as_bad (_("could not create section %s"), sec_name);
if (!bfd_set_section_flags (abfd, s, out_flags))
as_bad (_("invalid flag combination on section %s"), sec_name);
return s;
}
static bfd_boolean
section_has_property (segT sec, frag_predicate property_function)
{
segment_info_type *seginfo = seg_info (sec);
fragS *fragP;
if (seginfo && seginfo->frchainP)
{
for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
{
if (property_function (fragP)
&& (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
return TRUE;
}
}
return FALSE;
}
static bfd_boolean
section_has_xproperty (segT sec, frag_flags_fn property_function)
{
segment_info_type *seginfo = seg_info (sec);
fragS *fragP;
if (seginfo && seginfo->frchainP)
{
for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
{
frag_flags prop_flags;
property_function (fragP, &prop_flags);
if (!xtensa_frag_flags_is_empty (&prop_flags))
return TRUE;
}
}
return FALSE;
}
static void
add_xt_block_frags (segT sec,
segT xt_block_sec,
xtensa_block_info **xt_block,
frag_predicate property_function,
frag_predicate end_property_function)
{
segment_info_type *seg_info;
segment_info_type *xt_seg_info;
bfd_vma seg_offset;
fragS *fragP;
xt_seg_info = retrieve_segment_info (xt_block_sec);
seg_info = retrieve_segment_info (sec);
while (*xt_block != NULL)
xt_block = &(*xt_block)->next;
seg_offset = 0;
if (seg_info->frchainP)
{
for (fragP = seg_info->frchainP->frch_root;
fragP;
fragP = fragP->fr_next)
{
if (property_function (fragP)
&& (fragP->fr_type != rs_fill || fragP->fr_fix != 0))
{
if (*xt_block != NULL)
{
if ((*xt_block)->offset + (*xt_block)->size
== fragP->fr_address)
(*xt_block)->size += fragP->fr_fix;
else
xt_block = &((*xt_block)->next);
}
if (*xt_block == NULL)
{
xtensa_block_info *new_block = (xtensa_block_info *)
xmalloc (sizeof (xtensa_block_info));
new_block->sec = sec;
new_block->offset = fragP->fr_address;
new_block->size = fragP->fr_fix;
new_block->next = NULL;
xtensa_frag_flags_init (&new_block->flags);
*xt_block = new_block;
}
if (end_property_function
&& end_property_function (fragP))
{
xt_block = &((*xt_block)->next);
}
}
}
}
}
static bfd_boolean
xtensa_frag_flags_is_empty (const frag_flags *prop_flags)
{
if (prop_flags->is_literal
|| prop_flags->is_insn
|| prop_flags->is_data
|| prop_flags->is_unreachable)
return FALSE;
return TRUE;
}
static void
xtensa_frag_flags_init (frag_flags *prop_flags)
{
memset (prop_flags, 0, sizeof (frag_flags));
}
static void
get_frag_property_flags (const fragS *fragP, frag_flags *prop_flags)
{
xtensa_frag_flags_init (prop_flags);
if (fragP->tc_frag_data.is_literal)
prop_flags->is_literal = TRUE;
if (fragP->tc_frag_data.is_unreachable)
prop_flags->is_unreachable = TRUE;
else if (fragP->tc_frag_data.is_insn)
{
prop_flags->is_insn = TRUE;
if (fragP->tc_frag_data.is_loop_target)
prop_flags->insn.is_loop_target = TRUE;
if (fragP->tc_frag_data.is_branch_target)
prop_flags->insn.is_branch_target = TRUE;
if (fragP->tc_frag_data.is_specific_opcode
|| fragP->tc_frag_data.is_no_transform)
prop_flags->insn.is_no_transform = TRUE;
if (fragP->tc_frag_data.is_no_density)
prop_flags->insn.is_no_density = TRUE;
if (fragP->tc_frag_data.use_absolute_literals)
prop_flags->insn.is_abslit = TRUE;
}
if (fragP->tc_frag_data.is_align)
{
prop_flags->is_align = TRUE;
prop_flags->alignment = fragP->tc_frag_data.alignment;
if (xtensa_frag_flags_is_empty (prop_flags))
prop_flags->is_data = TRUE;
}
}
static bfd_vma
frag_flags_to_number (const frag_flags *prop_flags)
{
bfd_vma num = 0;
if (prop_flags->is_literal)
num |= XTENSA_PROP_LITERAL;
if (prop_flags->is_insn)
num |= XTENSA_PROP_INSN;
if (prop_flags->is_data)
num |= XTENSA_PROP_DATA;
if (prop_flags->is_unreachable)
num |= XTENSA_PROP_UNREACHABLE;
if (prop_flags->insn.is_loop_target)
num |= XTENSA_PROP_INSN_LOOP_TARGET;
if (prop_flags->insn.is_branch_target)
{
num |= XTENSA_PROP_INSN_BRANCH_TARGET;
num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
}
if (prop_flags->insn.is_no_density)
num |= XTENSA_PROP_INSN_NO_DENSITY;
if (prop_flags->insn.is_no_transform)
num |= XTENSA_PROP_INSN_NO_TRANSFORM;
if (prop_flags->insn.is_no_reorder)
num |= XTENSA_PROP_INSN_NO_REORDER;
if (prop_flags->insn.is_abslit)
num |= XTENSA_PROP_INSN_ABSLIT;
if (prop_flags->is_align)
{
num |= XTENSA_PROP_ALIGN;
num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
}
return num;
}
static bfd_boolean
xtensa_frag_flags_combinable (const frag_flags *prop_flags_1,
const frag_flags *prop_flags_2)
{
if (prop_flags_1->is_literal != prop_flags_2->is_literal)
return FALSE;
if (prop_flags_1->is_insn != prop_flags_2->is_insn)
return FALSE;
if (prop_flags_1->is_data != prop_flags_2->is_data)
return FALSE;
if (prop_flags_1->is_insn)
{
if (prop_flags_2->insn.is_loop_target)
return FALSE;
if (prop_flags_2->insn.is_branch_target)
return FALSE;
if (prop_flags_1->insn.is_no_density !=
prop_flags_2->insn.is_no_density)
return FALSE;
if (prop_flags_1->insn.is_no_transform !=
prop_flags_2->insn.is_no_transform)
return FALSE;
if (prop_flags_1->insn.is_no_reorder !=
prop_flags_2->insn.is_no_reorder)
return FALSE;
if (prop_flags_1->insn.is_abslit !=
prop_flags_2->insn.is_abslit)
return FALSE;
}
if (prop_flags_1->is_align)
return FALSE;
return TRUE;
}
static bfd_vma
xt_block_aligned_size (const xtensa_block_info *xt_block)
{
bfd_vma end_addr;
unsigned align_bits;
if (!xt_block->flags.is_align)
return xt_block->size;
end_addr = xt_block->offset + xt_block->size;
align_bits = xt_block->flags.alignment;
end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
return end_addr - xt_block->offset;
}
static bfd_boolean
xtensa_xt_block_combine (xtensa_block_info *xt_block,
const xtensa_block_info *xt_block_2)
{
if (xt_block->sec != xt_block_2->sec)
return FALSE;
if (xt_block->offset + xt_block_aligned_size (xt_block)
!= xt_block_2->offset)
return FALSE;
if (xt_block_2->size == 0
&& (!xt_block_2->flags.is_unreachable
|| xt_block->flags.is_unreachable))
{
if (xt_block_2->flags.is_align
&& xt_block->flags.is_align)
{
if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
return TRUE;
}
else
{
if (xt_block_2->flags.is_align)
{
xt_block->flags.is_align = xt_block_2->flags.is_align;
xt_block->flags.alignment = xt_block_2->flags.alignment;
}
return TRUE;
}
}
if (!xtensa_frag_flags_combinable (&xt_block->flags,
&xt_block_2->flags))
return FALSE;
xt_block->size += xt_block_2->size;
if (xt_block_2->flags.is_align)
{
xt_block->flags.is_align = TRUE;
xt_block->flags.alignment = xt_block_2->flags.alignment;
}
return TRUE;
}
static void
add_xt_prop_frags (segT sec,
segT xt_block_sec,
xtensa_block_info **xt_block,
frag_flags_fn property_function)
{
segment_info_type *seg_info;
segment_info_type *xt_seg_info;
bfd_vma seg_offset;
fragS *fragP;
xt_seg_info = retrieve_segment_info (xt_block_sec);
seg_info = retrieve_segment_info (sec);
while (*xt_block != NULL)
{
xt_block = &(*xt_block)->next;
}
seg_offset = 0;
if (seg_info->frchainP)
{
for (fragP = seg_info->frchainP->frch_root; fragP;
fragP = fragP->fr_next)
{
xtensa_block_info tmp_block;
tmp_block.sec = sec;
tmp_block.offset = fragP->fr_address;
tmp_block.size = fragP->fr_fix;
tmp_block.next = NULL;
property_function (fragP, &tmp_block.flags);
if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
{
if ((*xt_block) == NULL
|| !xtensa_xt_block_combine (*xt_block, &tmp_block))
{
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 = tmp_block;
*xt_block = new_block;
}
}
}
}
}
static void
init_op_placement_info_table (void)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
xtensa_opcode opcode;
xtensa_format fmt;
int slot;
int num_opcodes = xtensa_isa_num_opcodes (isa);
op_placement_table = (op_placement_info_table)
xmalloc (sizeof (op_placement_info) * num_opcodes);
assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
for (opcode = 0; opcode < num_opcodes; opcode++)
{
op_placement_info *opi = &op_placement_table[opcode];
if (xtensa_opcode_num_operands (isa, opcode) >= MAX_INSN_ARGS)
as_fatal (_("too many operands in instruction"));
opi->single = XTENSA_UNDEFINED;
opi->single_size = 0;
opi->widest = XTENSA_UNDEFINED;
opi->widest_size = 0;
opi->narrowest = XTENSA_UNDEFINED;
opi->narrowest_size = 0x7F;
opi->formats = 0;
opi->num_formats = 0;
opi->issuef = 0;
for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
{
opi->slots[fmt] = 0;
for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
{
if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
{
int fmt_length = xtensa_format_length (isa, fmt);
opi->issuef++;
set_bit (fmt, opi->formats);
set_bit (slot, opi->slots[fmt]);
if (fmt_length < opi->narrowest_size)
{
opi->narrowest = fmt;
opi->narrowest_size = fmt_length;
}
if (fmt_length > opi->widest_size)
{
opi->widest = fmt;
opi->widest_size = fmt_length;
}
if (xtensa_format_num_slots (isa, fmt) == 1)
{
if (opi->single_size == 0
|| fmt_length < opi->single_size)
{
opi->single = fmt;
opi->single_size = fmt_length;
}
}
}
}
if (opi->formats)
opi->num_formats++;
}
}
xtensa_insnbuf_free (isa, ibuf);
}
bfd_boolean
opcode_fits_format_slot (xtensa_opcode opcode, xtensa_format fmt, int slot)
{
return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
}
static int
xg_get_single_size (xtensa_opcode opcode)
{
assert (op_placement_table[opcode].single != XTENSA_UNDEFINED);
return op_placement_table[opcode].single_size;
}
static xtensa_format
xg_get_single_format (xtensa_opcode opcode)
{
return op_placement_table[opcode].single;
}
void
istack_init (IStack *stack)
{
memset (stack, 0, sizeof (IStack));
stack->ninsn = 0;
}
bfd_boolean
istack_empty (IStack *stack)
{
return (stack->ninsn == 0);
}
bfd_boolean
istack_full (IStack *stack)
{
return (stack->ninsn == MAX_ISTACK);
}
TInsn *
istack_top (IStack *stack)
{
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
return &stack->insn[rec];
}
void
istack_push (IStack *stack, TInsn *insn)
{
int rec = stack->ninsn;
assert (!istack_full (stack));
stack->insn[rec] = *insn;
stack->ninsn++;
}
TInsn *
istack_push_space (IStack *stack)
{
int rec = stack->ninsn;
TInsn *insn;
assert (!istack_full (stack));
insn = &stack->insn[rec];
memset (insn, 0, sizeof (TInsn));
stack->ninsn++;
return insn;
}
void
istack_pop (IStack *stack)
{
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
stack->ninsn--;
memset (&stack->insn[rec], 0, sizeof (TInsn));
}
void
tinsn_init (TInsn *dst)
{
memset (dst, 0, sizeof (TInsn));
}
expressionS *
tinsn_get_tok (TInsn *insn, int num)
{
assert (num < insn->ntok);
return &insn->tok[num];
}
static bfd_boolean
tinsn_has_symbolic_operands (const TInsn *insn)
{
int i;
int n = insn->ntok;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
switch (insn->tok[i].X_op)
{
case O_register:
case O_constant:
break;
default:
return TRUE;
}
}
return FALSE;
}
bfd_boolean
tinsn_has_invalid_symbolic_operands (const TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
int i;
int n = insn->ntok;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
switch (insn->tok[i].X_op)
{
case O_register:
case O_constant:
break;
case O_big:
case O_illegal:
case O_absent:
break;
case O_hi16:
case O_lo16:
default:
if (i != get_relaxable_immed (insn->opcode)
|| (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
&& insn->opcode != xtensa_const16_opcode))
{
as_bad (_("invalid symbolic operand %d on '%s'"),
i, xtensa_opcode_name (isa, insn->opcode));
return TRUE;
}
}
}
return FALSE;
}
static bfd_boolean
tinsn_has_complex_operands (const TInsn *insn)
{
int i;
int n = insn->ntok;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
switch (insn->tok[i].X_op)
{
case O_register:
case O_constant:
case O_symbol:
case O_lo16:
case O_hi16:
break;
default:
return TRUE;
}
}
return FALSE;
}
static bfd_boolean
tinsn_to_insnbuf (TInsn *tinsn, xtensa_insnbuf insnbuf)
{
static xtensa_insnbuf slotbuf = 0;
xtensa_isa isa = xtensa_default_isa;
xtensa_opcode opcode = tinsn->opcode;
xtensa_format fmt = xg_get_single_format (opcode);
bfd_boolean has_fixup = FALSE;
int noperands = xtensa_opcode_num_operands (isa, opcode);
int i;
uint32 opnd_value;
char *file_name;
unsigned line;
if (!slotbuf)
slotbuf = xtensa_insnbuf_alloc (isa);
assert (tinsn->insn_type == ITYPE_INSN);
if (noperands != tinsn->ntok)
as_fatal (_("operand number mismatch"));
if (xtensa_opcode_encode (isa, fmt, 0, slotbuf, opcode))
as_fatal (_("cannot encode opcode"));
for (i = 0; i < noperands; ++i)
{
expressionS *expr = &tinsn->tok[i];
switch (expr->X_op)
{
case O_register:
if (xtensa_operand_is_visible (isa, opcode, i) == 0)
break;
opnd_value = expr->X_add_number;
(void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
xtensa_operand_set_field (isa, opcode, i, fmt, 0,
slotbuf, opnd_value);
break;
case O_constant:
if (xtensa_operand_is_visible (isa, opcode, i) == 0)
break;
as_where (&file_name, &line);
xtensa_insnbuf_set_operand (slotbuf, fmt, 0, opcode, i,
expr->X_add_number, file_name, line);
break;
default:
has_fixup = TRUE;
break;
}
}
xtensa_format_encode (isa, fmt, insnbuf);
xtensa_format_set_slot (isa, fmt, 0, insnbuf, slotbuf);
return has_fixup;
}
static bfd_boolean
tinsn_to_slotbuf (xtensa_format fmt,
int slot,
TInsn *tinsn,
xtensa_insnbuf slotbuf)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_opcode opcode = tinsn->opcode;
bfd_boolean has_fixup = FALSE;
int noperands = xtensa_opcode_num_operands (isa, opcode);
int i;
*((int *) &slotbuf[0]) = 0;
*((int *) &slotbuf[1]) = 0;
assert (tinsn->insn_type == ITYPE_INSN);
if (noperands != tinsn->ntok)
as_fatal (_("operand number mismatch"));
if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
{
as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
return FALSE;
}
for (i = 0; i < noperands; i++)
{
expressionS *expr = &tinsn->tok[i];
int rc;
unsigned line;
char *file_name;
uint32 opnd_value;
switch (expr->X_op)
{
case O_register:
if (xtensa_operand_is_visible (isa, opcode, i) == 0)
break;
opnd_value = expr->X_add_number;
(void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
opnd_value);
if (rc != 0)
as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
break;
case O_constant:
if (xtensa_operand_is_visible (isa, opcode, i) == 0)
break;
as_where (&file_name, &line);
xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
expr->X_add_number, file_name, line);
break;
default:
has_fixup = TRUE;
break;
}
}
return has_fixup;
}
static bfd_boolean
tinsn_check_arguments (const TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_opcode opcode = insn->opcode;
if (opcode == XTENSA_UNDEFINED)
{
as_bad (_("invalid opcode"));
return TRUE;
}
if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
{
as_bad (_("too few operands"));
return TRUE;
}
if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
{
as_bad (_("too many operands"));
return TRUE;
}
return FALSE;
}
static void
tinsn_from_chars (TInsn *tinsn, char *f, int slot)
{
vliw_insn vinsn;
xg_init_vinsn (&vinsn);
vinsn_from_chars (&vinsn, f);
*tinsn = vinsn.slots[slot];
xg_free_vinsn (&vinsn);
}
static void
tinsn_from_insnbuf (TInsn *tinsn,
xtensa_insnbuf slotbuf,
xtensa_format fmt,
int slot)
{
int i;
xtensa_isa isa = xtensa_default_isa;
tinsn_init (tinsn);
tinsn->insn_type = ITYPE_INSN;
tinsn->is_specific_opcode = FALSE;
tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
for (i = 0; i < tinsn->ntok; i++)
{
set_expr_const (&tinsn->tok[i],
xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
tinsn->opcode, i));
}
}
static void
tinsn_immed_from_frag (TInsn *tinsn, fragS *fragP, int slot)
{
xtensa_opcode opcode = tinsn->opcode;
int opnum;
if (fragP->tc_frag_data.slot_symbols[slot])
{
opnum = get_relaxable_immed (opcode);
assert (opnum >= 0);
if (fragP->tc_frag_data.slot_sub_symbols[slot])
{
set_expr_symbol_offset_diff
(&tinsn->tok[opnum],
fragP->tc_frag_data.slot_symbols[slot],
fragP->tc_frag_data.slot_sub_symbols[slot],
fragP->tc_frag_data.slot_offsets[slot]);
}
else
{
set_expr_symbol_offset
(&tinsn->tok[opnum],
fragP->tc_frag_data.slot_symbols[slot],
fragP->tc_frag_data.slot_offsets[slot]);
}
}
}
static int
get_num_stack_text_bytes (IStack *istack)
{
int i;
int text_bytes = 0;
for (i = 0; i < istack->ninsn; i++)
{
TInsn *tinsn = &istack->insn[i];
if (tinsn->insn_type == ITYPE_INSN)
text_bytes += xg_get_single_size (tinsn->opcode);
}
return text_bytes;
}
static int
get_num_stack_literal_bytes (IStack *istack)
{
int i;
int lit_bytes = 0;
for (i = 0; i < istack->ninsn; i++)
{
TInsn *tinsn = &istack->insn[i];
if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
lit_bytes += 4;
}
return lit_bytes;
}
static void
xg_init_vinsn (vliw_insn *v)
{
int i;
xtensa_isa isa = xtensa_default_isa;
xg_clear_vinsn (v);
v->insnbuf = xtensa_insnbuf_alloc (isa);
if (v->insnbuf == NULL)
as_fatal (_("out of memory"));
for (i = 0; i < MAX_SLOTS; i++)
{
tinsn_init (&v->slots[i]);
v->slots[i].opcode = XTENSA_UNDEFINED;
v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
if (v->slotbuf[i] == NULL)
as_fatal (_("out of memory"));
}
}
static void
xg_clear_vinsn (vliw_insn *v)
{
int i;
v->format = XTENSA_UNDEFINED;
v->num_slots = 0;
v->inside_bundle = FALSE;
if (xt_saved_debug_type != DEBUG_NONE)
debug_type = xt_saved_debug_type;
for (i = 0; i < MAX_SLOTS; i++)
{
memset (&v->slots[i], 0, sizeof (TInsn));
v->slots[i].opcode = XTENSA_UNDEFINED;
}
}
static bfd_boolean
vinsn_has_specific_opcodes (vliw_insn *v)
{
int i;
for (i = 0; i < v->num_slots; i++)
{
if (v->slots[i].is_specific_opcode)
return TRUE;
}
return FALSE;
}
static void
xg_free_vinsn (vliw_insn *v)
{
int i;
xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
for (i = 0; i < MAX_SLOTS; i++)
xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
}
static bfd_boolean
vinsn_to_insnbuf (vliw_insn *vinsn,
char *frag_offset,
fragS *fragP,
bfd_boolean record_fixup)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_format fmt = vinsn->format;
xtensa_insnbuf insnbuf = vinsn->insnbuf;
int slot;
bfd_boolean has_fixup = FALSE;
xtensa_format_encode (isa, fmt, insnbuf);
for (slot = 0; slot < vinsn->num_slots; slot++)
{
TInsn *tinsn = &vinsn->slots[slot];
bfd_boolean tinsn_has_fixup =
tinsn_to_slotbuf (vinsn->format, slot, tinsn,
vinsn->slotbuf[slot]);
xtensa_format_set_slot (isa, fmt, slot,
insnbuf, vinsn->slotbuf[slot]);
if (tinsn_has_fixup && record_fixup)
{
int i;
xtensa_opcode opcode = tinsn->opcode;
int noperands = xtensa_opcode_num_operands (isa, opcode);
has_fixup = TRUE;
for (i = 0; i < noperands; i++)
{
expressionS* expr = &tinsn->tok[i];
switch (expr->X_op)
{
case O_symbol:
case O_lo16:
case O_hi16:
if (get_relaxable_immed (opcode) == i)
{
if (tinsn->record_fix || expr->X_op != O_symbol)
{
if (!xg_add_opcode_fix
(tinsn, i, fmt, slot, expr, fragP,
frag_offset - fragP->fr_literal))
as_bad (_("instruction with constant operands does not fit"));
}
else
{
tinsn->symbol = expr->X_add_symbol;
tinsn->offset = expr->X_add_number;
}
}
else
as_bad (_("invalid operand %d on '%s'"),
i, xtensa_opcode_name (isa, opcode));
break;
case O_constant:
case O_register:
break;
case O_subtract:
if (get_relaxable_immed (opcode) == i)
{
if (tinsn->record_fix)
as_bad (_("invalid subtract operand"));
else
{
tinsn->symbol = expr->X_add_symbol;
tinsn->sub_symbol = expr->X_op_symbol;
tinsn->offset = expr->X_add_number;
}
}
else
as_bad (_("invalid operand %d on '%s'"),
i, xtensa_opcode_name (isa, opcode));
break;
default:
as_bad (_("invalid expression for operand %d on '%s'"),
i, xtensa_opcode_name (isa, opcode));
break;
}
}
}
}
return has_fixup;
}
static void
vinsn_from_chars (vliw_insn *vinsn, char *f)
{
static xtensa_insnbuf insnbuf = NULL;
static xtensa_insnbuf slotbuf = NULL;
int i;
xtensa_format fmt;
xtensa_isa isa = xtensa_default_isa;
if (!insnbuf)
{
insnbuf = xtensa_insnbuf_alloc (isa);
slotbuf = xtensa_insnbuf_alloc (isa);
}
xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) f, 0);
fmt = xtensa_format_decode (isa, insnbuf);
if (fmt == XTENSA_UNDEFINED)
as_fatal (_("cannot decode instruction format"));
vinsn->format = fmt;
vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
for (i = 0; i < vinsn->num_slots; i++)
{
TInsn *tinsn = &vinsn->slots[i];
xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
}
}
bfd_boolean
expr_is_const (const expressionS *s)
{
return (s->X_op == O_constant);
}
offsetT
get_expr_const (const expressionS *s)
{
assert (expr_is_const (s));
return s->X_add_number;
}
void
set_expr_const (expressionS *s, offsetT val)
{
s->X_op = O_constant;
s->X_add_number = val;
s->X_add_symbol = NULL;
s->X_op_symbol = NULL;
}
bfd_boolean
expr_is_register (const expressionS *s)
{
return (s->X_op == O_register);
}
offsetT
get_expr_register (const expressionS *s)
{
assert (expr_is_register (s));
return s->X_add_number;
}
void
set_expr_symbol_offset (expressionS *s, symbolS *sym, offsetT offset)
{
s->X_op = O_symbol;
s->X_add_symbol = sym;
s->X_op_symbol = NULL;
s->X_add_number = offset;
}
static void
set_expr_symbol_offset_diff (expressionS *s,
symbolS *sym,
symbolS *minus_sym,
offsetT offset)
{
s->X_op = O_subtract;
s->X_add_symbol = sym;
s->X_op_symbol = minus_sym;
s->X_add_number = offset;
}
bfd_boolean
expr_is_equal (expressionS *s1, expressionS *s2)
{
if (s1->X_op != s2->X_op)
return FALSE;
if (s1->X_add_symbol != s2->X_add_symbol)
return FALSE;
if (s1->X_op_symbol != s2->X_op_symbol)
return FALSE;
if (s1->X_add_number != s2->X_add_number)
return FALSE;
return TRUE;
}
static void
copy_expr (expressionS *dst, const expressionS *src)
{
memcpy (dst, src, sizeof (expressionS));
}
struct rename_section_struct
{
char *old_name;
char *new_name;
struct rename_section_struct *next;
};
static struct rename_section_struct *section_rename;
static void
build_section_rename (const char *arg)
{
struct rename_section_struct *r;
char *this_arg = NULL;
char *next_arg = NULL;
for (this_arg = xstrdup (arg); this_arg != NULL; this_arg = next_arg)
{
char *old_name, *new_name;
if (this_arg)
{
next_arg = strchr (this_arg, ':');
if (next_arg)
{
*next_arg = '\0';
next_arg++;
}
}
old_name = this_arg;
new_name = strchr (this_arg, '=');
if (*old_name == '\0')
{
as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
continue;
}
if (!new_name || new_name[1] == '\0')
{
as_warn (_("ignoring invalid '-rename-section' specification: '%s'"),
old_name);
continue;
}
*new_name = '\0';
new_name++;
for (r = section_rename; r != NULL; r = r->next)
{
if (strcmp (r->old_name, old_name) == 0)
as_bad (_("section %s renamed multiple times"), old_name);
if (strcmp (r->new_name, new_name) == 0)
as_bad (_("multiple sections remapped to output section %s"),
new_name);
}
r = (struct rename_section_struct *)
xmalloc (sizeof (struct rename_section_struct));
r->old_name = xstrdup (old_name);
r->new_name = xstrdup (new_name);
r->next = section_rename;
section_rename = r;
}
}
char *
xtensa_section_rename (char *name)
{
struct rename_section_struct *r = section_rename;
for (r = section_rename; r != NULL; r = r->next)
{
if (strcmp (r->old_name, name) == 0)
return r->new_name;
}
return name;
}