binutils-gdb/gas/config/tc-xtensa.c

8751 lines
220 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* tc-xtensa.c -- Assemble Xtensa instructions.
Copyright 2003 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA. */
#include <string.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
/* Notes:
There are 3 forms for instructions,
1) the MEMORY format -- this is the encoding 2 or 3 byte instruction
2) the TInsn -- handles instructions/labels and literals;
all operands are assumed to be expressions
3) the IStack -- a stack of TInsn. this allows us to
reason about the generated expansion instructions
Naming conventions (used somewhat inconsistently):
The xtensa_ functions are exported
The xg_ functions are internal
We also have a couple of different extensibility mechanisms.
1) The idiom replacement:
This is used when a line is first parsed to
replace an instruction pattern with another instruction
It is currently limited to replacements of instructions
with constant operands.
2) The xtensa-relax.c mechanism that has stronger instruction
replacement patterns. When an instruction's immediate field
does not fit the next instruction sequence is attempted.
In addition, "narrow" opcodes are supported this way. */
/* Define characters with special meanings to GAS. */
const char comment_chars[] = "#";
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
const char EXP_CHARS[] = "eE";
const char FLT_CHARS[] = "rRsSfFdDxXpP";
/* Flag to indicate whether the hardware supports the density option.
If not, enabling density instructions (via directives or --density flag)
is illegal. */
#if STATIC_LIBISA
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
#else
bfd_boolean density_supported = TRUE;
#endif
#define XTENSA_FETCH_WIDTH 4
/* Flags for properties of the last instruction in a segment. */
#define FLAG_IS_A0_WRITER 0x1
#define FLAG_IS_BAD_LOOPEND 0x2
/* We define a special segment names ".literal" to place literals
into. The .fini and .init sections are special because they
contain code that is moved together by the linker. We give them
their own special .fini.literal and .init.literal sections. */
#define LITERAL_SECTION_NAME xtensa_section_rename (".literal")
#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")
/* This type is used for the directive_stack to keep track of the
state of the literal collection pools. */
typedef struct lit_state_struct
{
const char *lit_seg_name;
const char *init_lit_seg_name;
const char *fini_lit_seg_name;
segT lit_seg;
segT init_lit_seg;
segT fini_lit_seg;
} lit_state;
static lit_state default_lit_sections;
/* We keep lists of literal segments. The seg_list type is the node
for such a list. The *_literal_head locals are the heads of the
various lists. All of these lists have a dummy node at the start. */
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;
/* Lists of symbols. We keep a list of symbols that label the current
instruction, so that we can adjust the symbols when inserting alignment
for various instructions. We also keep a list of all the symbols on
literals, so that we can fix up those symbols when the literals are
later moved into the text sections. */
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;
/* Global flag to indicate when we are emitting literals. */
int generating_literals = 0;
/* Structure for saving the current state before emitting literals. */
typedef struct emit_state_struct
{
const char *name;
segT now_seg;
subsegT now_subseg;
int generating_literals;
} emit_state;
/* Directives. */
typedef enum
{
directive_none = 0,
directive_literal,
directive_density,
directive_generics,
directive_relax,
directive_freeregs,
directive_longcalls,
directive_literal_prefix
} directiveE;
typedef struct
{
const char *name;
bfd_boolean can_be_negated;
} directive_infoS;
const directive_infoS directive_info[] =
{
{"none", FALSE},
{"literal", FALSE},
{"density", TRUE},
{"generics", TRUE},
{"relax", TRUE},
{"freeregs", FALSE},
{"longcalls", TRUE},
{"literal_prefix", FALSE}
};
bfd_boolean directive_state[] =
{
FALSE, /* none */
FALSE, /* literal */
#if STATIC_LIBISA && !XCHAL_HAVE_DENSITY
FALSE, /* density */
#else
TRUE, /* density */
#endif
TRUE, /* generics */
TRUE, /* relax */
FALSE, /* freeregs */
FALSE, /* longcalls */
FALSE /* literal_prefix */
};
enum xtensa_relax_statesE
{
RELAX_ALIGN_NEXT_OPCODE,
/* Use the first opcode of the next fragment to determine the
alignment requirements. This is ONLY used for LOOPS
currently. */
RELAX_DESIRE_ALIGN_IF_TARGET,
/* These are placed in front of labels. They will all be converted
to RELAX_DESIRE_ALIGN / RELAX_LOOP_END or rs_fill of 0 before
relaxation begins. */
RELAX_ADD_NOP_IF_A0_B_RETW,
/* These are placed in front of conditional branches. It will be
turned into a NOP (using a1) if the branch is immediately
followed by a RETW or RETW.N. Otherwise it will be turned into
an rs_fill of 0 before relaxation begins. */
RELAX_ADD_NOP_IF_PRE_LOOP_END,
/* These are placed after JX instructions. It will be turned into a
NOP if there is one instruction before a loop end label.
Otherwise it will be turned into an rs_fill of 0 before
relaxation begins. This is used to avoid a hardware TIE
interlock issue prior to T1040. */
RELAX_ADD_NOP_IF_SHORT_LOOP,
/* These are placed after LOOP instructions. It will be turned into
a NOP when: (1) there are less than 3 instructions in the loop;
we place 2 of these in a row to add up to 2 NOPS in short loops;
or (2) The instructions in the loop do not include a branch or
jump. Otherwise it will be turned into an rs_fill of 0 before
relaxation begins. This is used to avoid hardware bug
PR3830. */
RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
/* These are placed after LOOP instructions. It will be turned into
a NOP if there are less than 12 bytes to the end of some other
loop's end. Otherwise it will be turned into an rs_fill of 0
before relaxation begins. This is used to avoid hardware bug
PR3830. */
RELAX_DESIRE_ALIGN,
/* The next fragment like its first instruction to NOT cross a
4-byte boundary. */
RELAX_LOOP_END,
/* This will be turned into a NOP or NOP.N if the previous
instruction is expanded to negate a loop. */
RELAX_LOOP_END_ADD_NOP,
/* When the code density option is available, this will generate a
NOP.N marked RELAX_NARROW. Otherwise, it will create an rs_fill
fragment with a NOP in it. */
RELAX_LITERAL,
/* Another fragment could generate an expansion here but has not yet. */
RELAX_LITERAL_NR,
/* Expansion has been generated by an instruction that generates a
literal. However, the stretch has NOT been reported yet in this
fragment. */
RELAX_LITERAL_FINAL,
/* Expansion has been generated by an instruction that generates a
literal. */
RELAX_LITERAL_POOL_BEGIN,
RELAX_LITERAL_POOL_END,
/* Technically these are not relaxations at all, but mark a location
to store literals later. Note that fr_var stores the frchain for
BEGIN frags and fr_var stores now_seg for END frags. */
RELAX_NARROW,
/* The last instruction in this fragment (at->fr_opcode) can be
freely replaced with a single wider instruction if a future
alignment desires or needs it. */
RELAX_IMMED,
/* The last instruction in this fragment (at->fr_opcode) contains
the value defined by fr_symbol (fr_offset = 0). If the value
does not fit, use the specified expansion. This is similar to
"NARROW", except that these may not be expanded in order to align
code. */
RELAX_IMMED_STEP1,
/* The last instruction in this fragment (at->fr_opcode) contains a
literal. It has already been expanded at least 1 step. */
RELAX_IMMED_STEP2
/* The last instruction in this fragment (at->fr_opcode) contains a
literal. It has already been expanded at least 2 steps. */
};
/* This is used as a stopper to bound the number of steps that
can be taken. */
#define RELAX_IMMED_MAXSTEPS (RELAX_IMMED_STEP2 - RELAX_IMMED)
typedef bfd_boolean (*frag_predicate) (const fragS *);
/* Directive functions. */
static bfd_boolean use_generics
PARAMS ((void));
static bfd_boolean use_longcalls
PARAMS ((void));
static bfd_boolean code_density_available
PARAMS ((void));
static bfd_boolean can_relax
PARAMS ((void));
static void directive_push
PARAMS ((directiveE, bfd_boolean, const void *));
static void directive_pop
PARAMS ((directiveE *, bfd_boolean *, const char **,
unsigned int *, const void **));
static void directive_balance
PARAMS ((void));
static bfd_boolean inside_directive
PARAMS ((directiveE));
static void get_directive
PARAMS ((directiveE *, bfd_boolean *));
static void xtensa_begin_directive
PARAMS ((int));
static void xtensa_end_directive
PARAMS ((int));
static void xtensa_literal_prefix
PARAMS ((char const *, int));
static void xtensa_literal_position
PARAMS ((int));
static void xtensa_literal_pseudo
PARAMS ((int));
/* Parsing and Idiom Translation Functions. */
static const char *expression_end
PARAMS ((const char *));
static unsigned tc_get_register
PARAMS ((const char *));
static void expression_maybe_register
PARAMS ((xtensa_operand, expressionS *));
static int tokenize_arguments
PARAMS ((char **, char *));
static bfd_boolean parse_arguments
PARAMS ((TInsn *, int, char **));
static int xg_translate_idioms
PARAMS ((char **, int *, char **));
static int xg_translate_sysreg_op
PARAMS ((char **, int *, char **));
static void xg_reverse_shift_count
PARAMS ((char **));
static int xg_arg_is_constant
PARAMS ((char *, offsetT *));
static void xg_replace_opname
PARAMS ((char **, char *));
static int xg_check_num_args
PARAMS ((int *, int, char *, char **));
/* Functions for dealing with the Xtensa ISA. */
static bfd_boolean operand_is_immed
PARAMS ((xtensa_operand));
static bfd_boolean operand_is_pcrel_label
PARAMS ((xtensa_operand));
static int get_relaxable_immed
PARAMS ((xtensa_opcode));
static xtensa_opcode get_opcode_from_buf
PARAMS ((const char *));
static bfd_boolean is_direct_call_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_call_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_entry_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_loop_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_the_loop_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_jx_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_windowed_return_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_conditional_branch_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_branch_or_jump_opcode
PARAMS ((xtensa_opcode));
static bfd_reloc_code_real_type opnum_to_reloc
PARAMS ((int));
static int reloc_to_opnum
PARAMS ((bfd_reloc_code_real_type));
static void xtensa_insnbuf_set_operand
PARAMS ((xtensa_insnbuf, xtensa_opcode, xtensa_operand, int32,
const char *, unsigned int));
static uint32 xtensa_insnbuf_get_operand
PARAMS ((xtensa_insnbuf, xtensa_opcode, int));
static void xtensa_insnbuf_set_immediate_field
PARAMS ((xtensa_opcode, xtensa_insnbuf, int32, const char *,
unsigned int));
static bfd_boolean is_negatable_branch
PARAMS ((TInsn *));
/* Various Other Internal Functions. */
static bfd_boolean is_unique_insn_expansion
PARAMS ((TransitionRule *));
static int xg_get_insn_size
PARAMS ((TInsn *));
static int xg_get_build_instr_size
PARAMS ((BuildInstr *));
static bfd_boolean xg_is_narrow_insn
PARAMS ((TInsn *));
static bfd_boolean xg_is_single_relaxable_insn
PARAMS ((TInsn *));
static int xg_get_max_narrow_insn_size
PARAMS ((xtensa_opcode));
static int xg_get_max_insn_widen_size
PARAMS ((xtensa_opcode));
static int xg_get_max_insn_widen_literal_size
PARAMS ((xtensa_opcode));
static bfd_boolean xg_is_relaxable_insn
PARAMS ((TInsn *, int));
static symbolS *get_special_literal_symbol
PARAMS ((void));
static symbolS *get_special_label_symbol
PARAMS ((void));
static bfd_boolean xg_build_to_insn
PARAMS ((TInsn *, TInsn *, BuildInstr *));
static bfd_boolean xg_build_to_stack
PARAMS ((IStack *, TInsn *, BuildInstr *));
static bfd_boolean xg_expand_to_stack
PARAMS ((IStack *, TInsn *, int));
static bfd_boolean xg_expand_narrow
PARAMS ((TInsn *, TInsn *));
static bfd_boolean xg_immeds_fit
PARAMS ((const TInsn *));
static bfd_boolean xg_symbolic_immeds_fit
PARAMS ((const TInsn *, segT, fragS *, offsetT, long));
static bfd_boolean xg_check_operand
PARAMS ((int32, xtensa_operand));
static int is_dnrange
PARAMS ((fragS *, symbolS *, long));
static int xg_assembly_relax
PARAMS ((IStack *, TInsn *, segT, fragS *, offsetT, int, long));
static void xg_force_frag_space
PARAMS ((int));
static void xg_finish_frag
PARAMS ((char *, enum xtensa_relax_statesE, int, bfd_boolean));
static bfd_boolean is_branch_jmp_to_next
PARAMS ((TInsn *, fragS *));
static void xg_add_branch_and_loop_targets
PARAMS ((TInsn *));
static bfd_boolean xg_instruction_matches_rule
PARAMS ((TInsn *, TransitionRule *));
static TransitionRule *xg_instruction_match
PARAMS ((TInsn *));
static bfd_boolean xg_build_token_insn
PARAMS ((BuildInstr *, TInsn *, TInsn *));
static bfd_boolean xg_simplify_insn
PARAMS ((TInsn *, TInsn *));
static bfd_boolean xg_expand_assembly_insn
PARAMS ((IStack *, TInsn *));
static symbolS *xg_assemble_literal
PARAMS ((TInsn *));
static void xg_assemble_literal_space
PARAMS ((int));
static symbolS *xtensa_create_literal_symbol
PARAMS ((segT, fragS *));
static void xtensa_add_literal_sym
PARAMS ((symbolS *));
static void xtensa_add_insn_label
PARAMS ((symbolS *));
static void xtensa_clear_insn_labels
PARAMS ((void));
static bfd_boolean get_is_linkonce_section
PARAMS ((bfd *, segT));
static bfd_boolean xg_emit_insn
PARAMS ((TInsn *, bfd_boolean));
static bfd_boolean xg_emit_insn_to_buf
PARAMS ((TInsn *, char *, fragS *, offsetT, bfd_boolean));
static bfd_boolean xg_add_opcode_fix
PARAMS ((xtensa_opcode, int, expressionS *, fragS *, offsetT));
static void xg_resolve_literals
PARAMS ((TInsn *, symbolS *));
static void xg_resolve_labels
PARAMS ((TInsn *, symbolS *));
static void xg_assemble_tokens
PARAMS ((TInsn *));
static bfd_boolean is_register_writer
PARAMS ((const TInsn *, const char *, int));
static bfd_boolean is_bad_loopend_opcode
PARAMS ((const TInsn *));
static bfd_boolean is_unaligned_label
PARAMS ((symbolS *));
static fragS *next_non_empty_frag
PARAMS ((const fragS *));
static xtensa_opcode next_frag_opcode
PARAMS ((const fragS *));
static void update_next_frag_nop_state
PARAMS ((fragS *));
static bfd_boolean next_frag_is_branch_target
PARAMS ((const fragS *));
static bfd_boolean next_frag_is_loop_target
PARAMS ((const fragS *));
static addressT next_frag_pre_opcode_bytes
PARAMS ((const fragS *));
static bfd_boolean is_next_frag_target
PARAMS ((const fragS *, const fragS *));
static void xtensa_mark_literal_pool_location
PARAMS ((void));
static void xtensa_move_labels
PARAMS ((fragS *, valueT, bfd_boolean));
static void assemble_nop
PARAMS ((size_t, char *));
static addressT get_expanded_loop_offset
PARAMS ((xtensa_opcode));
static fragS *get_literal_pool_location
PARAMS ((segT));
static void set_literal_pool_location
PARAMS ((segT, fragS *));
/* Helpers for xtensa_end(). */
static void xtensa_cleanup_align_frags
PARAMS ((void));
static void xtensa_fix_target_frags
PARAMS ((void));
static bfd_boolean frag_can_negate_branch
PARAMS ((fragS *));
static void xtensa_fix_a0_b_retw_frags
PARAMS ((void));
static bfd_boolean next_instrs_are_b_retw
PARAMS ((fragS *));
static void xtensa_fix_b_j_loop_end_frags
PARAMS ((void));
static bfd_boolean next_instr_is_loop_end
PARAMS ((fragS *));
static void xtensa_fix_close_loop_end_frags
PARAMS ((void));
static size_t min_bytes_to_other_loop_end
PARAMS ((fragS *, fragS *, offsetT, size_t));
static size_t unrelaxed_frag_min_size
PARAMS ((fragS *));
static void xtensa_fix_short_loop_frags
PARAMS ((void));
static size_t count_insns_to_loop_end
PARAMS ((fragS *, bfd_boolean, size_t));
static size_t unrelaxed_frag_min_insn_count
PARAMS ((fragS *));
static bfd_boolean branch_before_loop_end
PARAMS ((fragS *));
static bfd_boolean unrelaxed_frag_has_b_j
PARAMS ((fragS *));
static void xtensa_sanity_check
PARAMS ((void));
static bfd_boolean is_empty_loop
PARAMS ((const TInsn *, fragS *));
static bfd_boolean is_local_forward_loop
PARAMS ((const TInsn *, fragS *));
/* Alignment Functions. */
static size_t get_text_align_power
PARAMS ((int));
static addressT get_text_align_max_fill_size
PARAMS ((int, bfd_boolean, bfd_boolean));
static addressT get_text_align_fill_size
PARAMS ((addressT, int, int, bfd_boolean, bfd_boolean));
static size_t get_text_align_nop_count
PARAMS ((size_t, bfd_boolean));
static size_t get_text_align_nth_nop_size
PARAMS ((size_t, size_t, bfd_boolean));
static addressT get_noop_aligned_address
PARAMS ((fragS *, addressT));
static addressT get_widen_aligned_address
PARAMS ((fragS *, addressT));
/* Helpers for xtensa_relax_frag(). */
static long relax_frag_text_align
PARAMS ((fragS *, long));
static long relax_frag_add_nop
PARAMS ((fragS *));
static long relax_frag_narrow
PARAMS ((fragS *, long));
static bfd_boolean future_alignment_required
PARAMS ((fragS *, long));
static long relax_frag_immed
PARAMS ((segT, fragS *, long, int, int *));
/* Helpers for md_convert_frag(). */
static void convert_frag_align_next_opcode
PARAMS ((fragS *));
static void convert_frag_narrow
PARAMS ((fragS *));
static void convert_frag_immed
PARAMS ((segT, fragS *, int));
static fixS *fix_new_exp_in_seg
PARAMS ((segT, subsegT, fragS *, int, int, expressionS *, int,
bfd_reloc_code_real_type));
static void convert_frag_immed_finish_loop
PARAMS ((segT, fragS *, TInsn *));
static offsetT get_expression_value
PARAMS ((segT, expressionS *));
/* Flags for the Last Instruction in Each Subsegment. */
static unsigned get_last_insn_flags
PARAMS ((segT, subsegT));
static void set_last_insn_flags
PARAMS ((segT, subsegT, unsigned, bfd_boolean));
/* Segment list functions. */
static void xtensa_remove_section
PARAMS ((segT));
static void xtensa_insert_section
PARAMS ((segT, segT));
static void xtensa_move_seg_list_to_beginning
PARAMS ((seg_list *));
static void xtensa_move_literals
PARAMS ((void));
static void mark_literal_frags
PARAMS ((seg_list *));
static void xtensa_reorder_seg_list
PARAMS ((seg_list *, segT));
static void xtensa_reorder_segments
PARAMS ((void));
static segT get_last_sec
PARAMS ((void));
static void xtensa_switch_to_literal_fragment
PARAMS ((emit_state *));
static void xtensa_switch_section_emit_state
PARAMS ((emit_state *, segT, subsegT));
static void xtensa_restore_emit_state
PARAMS ((emit_state *));
static void cache_literal_section
PARAMS ((seg_list *, const char *, segT *));
static segT retrieve_literal_seg
PARAMS ((seg_list *, const char *));
static segT seg_present
PARAMS ((const char *));
static void add_seg_list
PARAMS ((seg_list *, segT));
/* Property Table (e.g., ".xt.insn" and ".xt.lit") Functions. */
static void xtensa_create_property_segments
PARAMS ((frag_predicate, const char *, xt_section_type));
static segment_info_type *retrieve_segment_info
PARAMS ((segT));
static segT retrieve_xtensa_section
PARAMS ((char *));
static bfd_boolean section_has_property
PARAMS ((segT sec, frag_predicate));
static void add_xt_block_frags
PARAMS ((segT, segT, xtensa_block_info **, frag_predicate));
static bfd_boolean get_frag_is_literal
PARAMS ((const fragS *));
static bfd_boolean get_frag_is_insn
PARAMS ((const fragS *));
/* Import from elf32-xtensa.c in BFD library. */
extern char *xtensa_get_property_section_name
PARAMS ((asection *, const char *));
/* TInsn and IStack functions. */
static bfd_boolean tinsn_has_symbolic_operands
PARAMS ((const TInsn *));
static bfd_boolean tinsn_has_invalid_symbolic_operands
PARAMS ((const TInsn *));
static bfd_boolean tinsn_has_complex_operands
PARAMS ((const TInsn *));
static bfd_boolean tinsn_to_insnbuf
PARAMS ((TInsn *, xtensa_insnbuf));
static bfd_boolean tinsn_check_arguments
PARAMS ((const TInsn *));
static void tinsn_from_chars
PARAMS ((TInsn *, char *));
static void tinsn_immed_from_frag
PARAMS ((TInsn *, fragS *));
static int get_num_stack_text_bytes
PARAMS ((IStack *));
static int get_num_stack_literal_bytes
PARAMS ((IStack *));
/* Expression Utilities. */
bfd_boolean expr_is_const
PARAMS ((const expressionS *));
offsetT get_expr_const
PARAMS ((const expressionS *));
void set_expr_const
PARAMS ((expressionS *, offsetT));
void set_expr_symbol_offset
PARAMS ((expressionS *, symbolS *, offsetT));
bfd_boolean expr_is_equal
PARAMS ((expressionS *, expressionS *));
static void copy_expr
PARAMS ((expressionS *, const expressionS *));
#ifdef XTENSA_SECTION_RENAME
static void build_section_rename
PARAMS ((const char *));
static void add_section_rename
PARAMS ((char *, char *));
#endif
/* ISA imported from bfd. */
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_entry_opcode;
static xtensa_opcode xtensa_isync_opcode;
static xtensa_opcode xtensa_j_opcode;
static xtensa_opcode xtensa_jx_opcode;
static xtensa_opcode xtensa_loop_opcode;
static xtensa_opcode xtensa_loopnez_opcode;
static xtensa_opcode xtensa_loopgtz_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_opcode;
static xtensa_opcode xtensa_waiti_opcode;
/* Command-line Options. */
bfd_boolean use_literal_section = TRUE;
static bfd_boolean align_targets = TRUE;
static bfd_boolean align_only_targets = FALSE;
static bfd_boolean software_a0_b_retw_interlock = TRUE;
static bfd_boolean has_a0_b_retw = FALSE;
static bfd_boolean workaround_a0_b_retw = TRUE;
static bfd_boolean software_avoid_b_j_loop_end = TRUE;
static bfd_boolean workaround_b_j_loop_end = TRUE;
static bfd_boolean maybe_has_b_j_loop_end = FALSE;
static bfd_boolean software_avoid_short_loop = TRUE;
static bfd_boolean workaround_short_loop = TRUE;
static bfd_boolean maybe_has_short_loop = FALSE;
static bfd_boolean software_avoid_close_loop_end = TRUE;
static bfd_boolean workaround_close_loop_end = TRUE;
static bfd_boolean maybe_has_close_loop_end = FALSE;
/* When avoid_short_loops is true, all loops with early exits must
have at least 3 instructions. avoid_all_short_loops is a modifier
to the avoid_short_loop flag. In addition to the avoid_short_loop
actions, all straightline loopgtz and loopnez must have at least 3
instructions. */
static bfd_boolean software_avoid_all_short_loops = TRUE;
static bfd_boolean workaround_all_short_loops = TRUE;
/* This is on a per-instruction basis. */
static bfd_boolean specific_opcode = FALSE;
enum
{
option_density = OPTION_MD_BASE,
option_no_density,
option_relax,
option_no_relax,
option_generics,
option_no_generics,
option_text_section_literals,
option_no_text_section_literals,
option_align_targets,
option_no_align_targets,
option_align_only_targets,
option_no_align_only_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,
#ifdef XTENSA_SECTION_RENAME
option_literal_section_name,
option_text_section_name,
option_data_section_name,
option_bss_section_name,
option_rename_section_name,
#endif
option_eb,
option_el
};
const char *md_shortopts = "";
struct option md_longopts[] =
{
{"density", no_argument, NULL, option_density},
{"no-density", no_argument, NULL, option_no_density},
/* At least as early as alameda, --[no-]relax didn't work as
documented, so as of albany, --[no-]relax is equivalent to
--[no-]generics. Both of these will be deprecated in
BearValley. */
{"relax", no_argument, NULL, option_generics},
{"no-relax", no_argument, NULL, option_no_generics},
{"generics", no_argument, NULL, option_generics},
{"no-generics", no_argument, NULL, option_no_generics},
{"text-section-literals", no_argument, NULL, option_text_section_literals},
{"no-text-section-literals", no_argument, NULL,
option_no_text_section_literals},
/* This option was changed from -align-target to -target-align
because it conflicted with the "-al" option. */
{"target-align", no_argument, NULL, option_align_targets},
{"no-target-align", no_argument, NULL,
option_no_align_targets},
#if 0
/* This option should do a better job aligning targets because
it will only attempt to align targets that are the target of a
branch. */
{ "target-align-only", no_argument, NULL, option_align_only_targets },
{ "no-target-align-only", no_argument, NULL, option_no_align_only_targets },
#endif /* 0 */
{"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},
{"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},
{"no-workarounds", no_argument, NULL, option_no_workarounds},
#ifdef XTENSA_SECTION_RENAME
{"literal-section-name", required_argument, NULL,
option_literal_section_name},
{"text-section-name", required_argument, NULL,
option_text_section_name},
{"data-section-name", required_argument, NULL,
option_data_section_name},
{"rename-section", required_argument, NULL,
option_rename_section_name},
{"bss-section-name", required_argument, NULL,
option_bss_section_name},
#endif /* XTENSA_SECTION_RENAME */
{NULL, no_argument, NULL, 0}
};
size_t md_longopts_size = sizeof md_longopts;
int
md_parse_option (c, arg)
int c;
char *arg;
{
switch (c)
{
case option_density:
if (!density_supported)
{
as_bad (_("'--density' option not supported in this Xtensa "
"configuration"));
return 0;
}
directive_state[directive_density] = TRUE;
return 1;
case option_no_density:
directive_state[directive_density] = FALSE;
return 1;
case option_generics:
directive_state[directive_generics] = TRUE;
return 1;
case option_no_generics:
directive_state[directive_generics] = FALSE;
return 1;
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_workaround_a0_b_retw:
workaround_a0_b_retw = TRUE;
software_a0_b_retw_interlock = TRUE;
return 1;
case option_no_workaround_a0_b_retw:
workaround_a0_b_retw = FALSE;
software_a0_b_retw_interlock = FALSE;
return 1;
case option_workaround_b_j_loop_end:
workaround_b_j_loop_end = TRUE;
software_avoid_b_j_loop_end = TRUE;
return 1;
case option_no_workaround_b_j_loop_end:
workaround_b_j_loop_end = FALSE;
software_avoid_b_j_loop_end = FALSE;
return 1;
case option_workaround_short_loop:
workaround_short_loop = TRUE;
software_avoid_short_loop = TRUE;
return 1;
case option_no_workaround_short_loop:
workaround_short_loop = FALSE;
software_avoid_short_loop = FALSE;
return 1;
case option_workaround_all_short_loops:
workaround_all_short_loops = TRUE;
software_avoid_all_short_loops = TRUE;
return 1;
case option_no_workaround_all_short_loops:
workaround_all_short_loops = FALSE;
software_avoid_all_short_loops = FALSE;
return 1;
case option_workaround_close_loop_end:
workaround_close_loop_end = TRUE;
software_avoid_close_loop_end = TRUE;
return 1;
case option_no_workaround_close_loop_end:
workaround_close_loop_end = FALSE;
software_avoid_close_loop_end = FALSE;
return 1;
case option_no_workarounds:
workaround_a0_b_retw = FALSE;
software_a0_b_retw_interlock = FALSE;
workaround_b_j_loop_end = FALSE;
software_avoid_b_j_loop_end = FALSE;
workaround_short_loop = FALSE;
software_avoid_short_loop = FALSE;
workaround_all_short_loops = FALSE;
software_avoid_all_short_loops = FALSE;
workaround_close_loop_end = FALSE;
software_avoid_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_align_only_targets:
align_only_targets = TRUE;
return 1;
case option_no_align_only_targets:
align_only_targets = FALSE;
return 1;
#ifdef XTENSA_SECTION_RENAME
case option_literal_section_name:
add_section_rename (".literal", arg);
as_warn (_("'--literal-section-name' is deprecated; "
"use '--rename-section .literal=NEWNAME'"));
return 1;
case option_text_section_name:
add_section_rename (".text", arg);
as_warn (_("'--text-section-name' is deprecated; "
"use '--rename-section .text=NEWNAME'"));
return 1;
case option_data_section_name:
add_section_rename (".data", arg);
as_warn (_("'--data-section-name' is deprecated; "
"use '--rename-section .data=NEWNAME'"));
return 1;
case option_bss_section_name:
add_section_rename (".bss", arg);
as_warn (_("'--bss-section-name' is deprecated; "
"use '--rename-section .bss=NEWNAME'"));
return 1;
case option_rename_section_name:
build_section_rename (arg);
return 1;
#endif /* XTENSA_SECTION_RENAME */
case 'Q':
/* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
should be emitted or not. FIXME: Not implemented. */
return 1;
default:
return 0;
}
}
void
md_show_usage (stream)
FILE *stream;
{
fputs ("\nXtensa options:\n"
"--[no-]density [Do not] emit density instructions\n"
"--[no-]relax [Do not] perform branch relaxation\n"
"--[no-]generics [Do not] transform instructions\n"
"--[no-]longcalls [Do not] emit 32-bit call sequences\n"
"--[no-]target-align [Do not] try to align branch targets\n"
"--[no-]text-section-literals\n"
" [Do not] put literals in the text section\n"
"--no-workarounds Do not use any Xtensa workarounds\n"
#ifdef XTENSA_SECTION_RENAME
"--rename-section old=new(:old1=new1)*\n"
" Rename section 'old' to 'new'\n"
"\nThe following Xtensa options are deprecated\n"
"--literal-section-name Name of literal section (default .literal)\n"
"--text-section-name Name of text section (default .text)\n"
"--data-section-name Name of data section (default .data)\n"
"--bss-section-name Name of bss section (default .bss)\n"
#endif
, stream);
}
/* Directive data and functions. */
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}, /* Defaulting is invalid (0) */
{"literal_position", xtensa_literal_position, 0},
{"frame", s_ignore, 0}, /* formerly used for STABS debugging */
{"word", cons, 4},
{"begin", xtensa_begin_directive, 0},
{"end", xtensa_end_directive, 0},
{"literal", xtensa_literal_pseudo, 0},
{NULL, 0, 0},
};
bfd_boolean
use_generics ()
{
return directive_state[directive_generics];
}
bfd_boolean
use_longcalls ()
{
return directive_state[directive_longcalls];
}
bfd_boolean
code_density_available ()
{
return directive_state[directive_density];
}
bfd_boolean
can_relax ()
{
return use_generics ();
}
static void
directive_push (directive, negated, datum)
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 (directive, negated, file, line, datum)
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 ()
{
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 (dir)
directiveE dir;
{
state_stackS *top = directive_state_stack;
while (top && top->directive != dir)
top = top->prev;
return (top != NULL);
}
static void
get_directive (directive, negated)
directiveE *directive;
bfd_boolean *negated;
{
int len;
unsigned i;
if (strncmp (input_line_pointer, "no-", 3) != 0)
*negated = FALSE;
else
{
*negated = TRUE;
input_line_pointer += 3;
}
len = strspn (input_line_pointer,
"abcdefghijklmnopqrstuvwxyz_/0123456789.");
for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
{
if (strncmp (input_line_pointer, 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 can't be negated"),
directive_info[i].name);
return;
}
}
as_bad (_("unknown directive"));
*directive = (directiveE) XTENSA_UNDEFINED;
}
static void
xtensa_begin_directive (ignore)
int ignore ATTRIBUTE_UNUSED;
{
directiveE directive;
bfd_boolean negated;
emit_state *state;
int len;
lit_state *ls;
md_flush_pending_output ();
get_directive (&directive, &negated);
if (directive == (directiveE) XTENSA_UNDEFINED)
{
discard_rest_of_line ();
return;
}
switch (directive)
{
case directive_literal:
if (!inside_directive (directive_literal))
{
/* Previous labels go with whatever follows this directive, not with
the literal, so save them now. */
saved_insn_labels = insn_labels;
insn_labels = NULL;
}
state = (emit_state *) xmalloc (sizeof (emit_state));
xtensa_switch_to_literal_fragment (state);
directive_push (directive_literal, negated, state);
break;
case directive_literal_prefix:
/* Check to see if the current fragment is a literal
fragment. If it is, then this operation is not allowed. */
if (frag_now->tc_frag_data.is_literal)
{
as_bad (_("cannot set literal_prefix inside literal fragment"));
return;
}
/* Allocate the literal state for this section and push
onto the directive stack. */
ls = xmalloc (sizeof (lit_state));
assert (ls);
*ls = default_lit_sections;
directive_push (directive_literal_prefix, negated, ls);
/* Parse the new prefix from the input_line_pointer. */
SKIP_WHITESPACE ();
len = strspn (input_line_pointer,
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz_/0123456789.$");
/* Process the new prefix. */
xtensa_literal_prefix (input_line_pointer, len);
/* Skip the name in the input line. */
input_line_pointer += len;
break;
case directive_freeregs:
/* This information is currently unused, but we'll accept the statement
and just discard the rest of the line. This won't check the syntax,
but it will accept every correct freeregs directive. */
input_line_pointer += strcspn (input_line_pointer, "\n");
directive_push (directive_freeregs, negated, 0);
break;
case directive_density:
if (!density_supported && !negated)
{
as_warn (_("Xtensa density option not supported; ignored"));
break;
}
/* fall through */
default:
directive_push (directive, negated, 0);
break;
}
demand_empty_rest_of_line ();
}
static void
xtensa_end_directive (ignore)
int ignore ATTRIBUTE_UNUSED;
{
directiveE begin_directive, end_directive;
bfd_boolean begin_negated, end_negated;
const char *file;
unsigned int line;
emit_state *state;
lit_state *s;
md_flush_pending_output ();
get_directive (&end_directive, &end_negated);
if (end_directive == (directiveE) XTENSA_UNDEFINED)
{
discard_rest_of_line ();
return;
}
if (end_directive == directive_density && !density_supported && !end_negated)
{
as_warn (_("Xtensa density option not supported; ignored"));
demand_empty_rest_of_line ();
return;
}
directive_pop (&begin_directive, &begin_negated, &file, &line,
(const void **) &state);
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);
free (state);
if (!inside_directive (directive_literal))
{
/* Restore the list of current labels. */
xtensa_clear_insn_labels ();
insn_labels = saved_insn_labels;
}
break;
case directive_freeregs:
break;
case directive_literal_prefix:
/* Restore the default collection sections from saved state. */
s = (lit_state *) state;
assert (s);
if (use_literal_section)
default_lit_sections = *s;
/* free the state storage */
free (s);
break;
default:
break;
}
}
}
demand_empty_rest_of_line ();
}
/* Place an aligned literal fragment at the current location. */
static void
xtensa_literal_position (ignore)
int ignore ATTRIBUTE_UNUSED;
{
if (inside_directive (directive_literal))
as_warn (_(".literal_position inside literal directive; ignoring"));
else if (!use_literal_section)
xtensa_mark_literal_pool_location ();
demand_empty_rest_of_line ();
xtensa_clear_insn_labels ();
}
/* Support .literal label, value@plt + offset. */
static void
xtensa_literal_pseudo (ignored)
int ignored ATTRIBUTE_UNUSED;
{
emit_state state;
char *p, *base_name;
char c;
expressionS expP;
segT dest_seg;
if (inside_directive (directive_literal))
{
as_bad (_(".literal not allowed inside .begin literal region"));
ignore_rest_of_line ();
return;
}
/* Previous labels go with whatever follows this directive, not with
the literal, so save them now. */
saved_insn_labels = insn_labels;
insn_labels = NULL;
/* If we are using text-section literals, then this is the right value... */
dest_seg = now_seg;
base_name = input_line_pointer;
xtensa_switch_to_literal_fragment (&state);
/* ...but if we aren't using text-section-literals, then we
need to put them in the section we just switched to. */
if (use_literal_section)
dest_seg = now_seg;
/* All literals are aligned to four-byte boundaries
which is handled by switch to literal fragment. */
/* frag_align (2, 0, 0); */
c = get_symbol_end ();
/* Just after name is now '\0'. */
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);
do
{
input_line_pointer++; /* skip ',' or ':' */
expr (0, &expP);
/* We only support 4-byte literals with .literal. */
emit_expr (&expP, 4);
}
while (*input_line_pointer == ',');
*p = c;
demand_empty_rest_of_line ();
xtensa_restore_emit_state (&state);
/* Restore the list of current labels. */
xtensa_clear_insn_labels ();
insn_labels = saved_insn_labels;
}
static void
xtensa_literal_prefix (start, len)
char const *start;
int len;
{
segT s_now; /* Storage for the current seg and subseg. */
subsegT ss_now;
char *name; /* Pointer to the name itself. */
char *newname;
if (!use_literal_section)
return;
/* Store away the current section and subsection. */
s_now = now_seg;
ss_now = now_subseg;
/* Get a null-terminated copy of the name. */
name = xmalloc (len + 1);
assert (name);
strncpy (name, start, len);
name[len] = 0;
/* Allocate the sections (interesting note: the memory pointing to
the name is actually used for the name by the new section). */
newname = xmalloc (len + strlen (".literal") + 1);
strcpy (newname, name);
strcpy (newname + len, ".literal");
/* Note that retrieve_literal_seg does not create a segment if
it already exists. */
default_lit_sections.lit_seg = NULL; /* retrieved on demand */
/* Canonicalizing section names allows renaming literal
sections to occur correctly. */
default_lit_sections.lit_seg_name =
tc_canonicalize_symbol_name (newname);
free (name);
/* Restore the current section and subsection and set the
generation into the old segment. */
subseg_set (s_now, ss_now);
}
/* Parsing and Idiom Translation. */
static const char *
expression_end (name)
const char *name;
{
while (1)
{
switch (*name)
{
case ';':
case '\0':
case ',':
return name;
case ' ':
case '\t':
++name;
continue;
default:
return 0;
}
}
}
#define ERROR_REG_NUM ((unsigned) -1)
static unsigned
tc_get_register (prefix)
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;
/* Accept "sp" as a synonym for "a1". */
if (input_line_pointer[0] == 's' && input_line_pointer[1] == 'p'
&& expression_end (input_line_pointer + 2))
{
input_line_pointer += 2;
return 1; /* AR[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;
}
#define PLT_SUFFIX "@PLT"
#define plt_suffix "@plt"
static void
expression_maybe_register (opnd, tok)
xtensa_operand opnd;
expressionS *tok;
{
char *kind = xtensa_operand_kind (opnd);
if ((strlen (kind) == 1)
&& (*kind == 'l' || *kind == 'L' || *kind == 'i' || *kind == 'r'))
{
segT t = expression (tok);
if (t == absolute_section && operand_is_pcrel_label (opnd))
{
assert (tok->X_op == O_constant);
tok->X_op = O_symbol;
tok->X_add_symbol = &abs_symbol;
}
if (tok->X_op == O_symbol
&& (!strncmp (input_line_pointer, PLT_SUFFIX,
strlen (PLT_SUFFIX) - 1)
|| !strncmp (input_line_pointer, plt_suffix,
strlen (plt_suffix) - 1)))
{
symbol_get_tc (tok->X_add_symbol)->plt = 1;
input_line_pointer += strlen (plt_suffix);
}
}
else
{
unsigned reg = tc_get_register (kind);
if (reg != ERROR_REG_NUM) /* Already errored */
{
uint32 buf = reg;
if ((xtensa_operand_encode (opnd, &buf) != xtensa_encode_result_ok)
|| (reg != xtensa_operand_decode (opnd, buf)))
as_bad (_("register number out of range"));
}
tok->X_op = O_register;
tok->X_add_symbol = 0;
tok->X_add_number = reg;
}
}
/* Split up the arguments for an opcode or pseudo-op. */
static int
tokenize_arguments (args, str)
char **args;
char *str;
{
char *old_input_line_pointer;
bfd_boolean saw_comma = FALSE;
bfd_boolean saw_arg = FALSE;
int num_args = 0;
char *arg_end, *arg;
int arg_len;
/* Save and restore input_line_pointer around this function. */
old_input_line_pointer = input_line_pointer;
input_line_pointer = str;
while (*input_line_pointer)
{
SKIP_WHITESPACE ();
switch (*input_line_pointer)
{
case '\0':
goto fini;
case ',':
input_line_pointer++;
if (saw_comma || !saw_arg)
goto err;
saw_comma = TRUE;
break;
default:
if (!saw_comma && 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 (arg_len + 1);
args[num_args] = arg;
strncpy (arg, input_line_pointer, arg_len);
arg[arg_len] = '\0';
input_line_pointer = arg_end;
num_args += 1;
saw_comma = FALSE;
saw_arg = TRUE;
break;
}
}
fini:
if (saw_comma)
goto err;
input_line_pointer = old_input_line_pointer;
return num_args;
err:
input_line_pointer = old_input_line_pointer;
return -1;
}
/* Parse the arguments to an opcode. Return true on error. */
static bfd_boolean
parse_arguments (insn, num_args, arg_strings)
TInsn *insn;
int num_args;
char **arg_strings;
{
expressionS *tok = insn->tok;
xtensa_opcode opcode = insn->opcode;
bfd_boolean had_error = TRUE;
xtensa_isa isa = xtensa_default_isa;
int n;
int opcode_operand_count;
int actual_operand_count = 0;
xtensa_operand opnd = NULL;
char *old_input_line_pointer;
if (insn->insn_type == ITYPE_LITERAL)
opcode_operand_count = 1;
else
opcode_operand_count = xtensa_num_operands (isa, opcode);
memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
/* Save and restore input_line_pointer around this function. */
old_input_line_pointer = input_line_pointer;
for (n = 0; n < num_args; n++)
{
input_line_pointer = arg_strings[n];
if (actual_operand_count >= opcode_operand_count)
{
as_warn (_("too many arguments"));
goto err;
}
assert (actual_operand_count < MAX_INSN_ARGS);
opnd = xtensa_get_operand (isa, opcode, actual_operand_count);
expression_maybe_register (opnd, tok);
if (tok->X_op == O_illegal || tok->X_op == O_absent)
goto err;
actual_operand_count++;
tok++;
}
insn->ntok = tok - insn->tok;
had_error = FALSE;
err:
input_line_pointer = old_input_line_pointer;
return had_error;
}
static void
xg_reverse_shift_count (cnt_argp)
char **cnt_argp;
{
char *cnt_arg, *new_arg;
cnt_arg = *cnt_argp;
/* replace the argument with "31-(argument)" */
new_arg = (char *) xmalloc (strlen (cnt_arg) + 6);
sprintf (new_arg, "31-(%s)", cnt_arg);
free (cnt_arg);
*cnt_argp = new_arg;
}
/* If "arg" is a constant expression, return non-zero with the value
in *valp. */
static int
xg_arg_is_constant (arg, valp)
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 (popname, newop)
char **popname;
char *newop;
{
free (*popname);
*popname = (char *) xmalloc (strlen (newop) + 1);
strcpy (*popname, newop);
}
static int
xg_check_num_args (pnum_args, expected_num, opname, arg_strings)
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 (popname, pnum_args, arg_strings)
char **popname;
int *pnum_args;
char **arg_strings;
{
char *opname, *new_opname;
offsetT val;
bfd_boolean has_underbar = FALSE;
opname = *popname;
if (*opname == '_')
{
has_underbar = TRUE;
opname += 1;
}
/* Opname == [rw]ur... */
if (opname[3] == '\0')
{
/* If the register is not specified as part of the opcode,
then get it from the operand and move it to the opcode. */
if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
return -1;
if (!xg_arg_is_constant (arg_strings[1], &val))
{
as_bad (_("register number for `%s' is not a constant"), opname);
return -1;
}
if ((unsigned) val > 255)
{
as_bad (_("register number (%ld) for `%s' is out of range"),
val, opname);
return -1;
}
/* Remove the last argument, which is now part of the opcode. */
free (arg_strings[1]);
arg_strings[1] = 0;
*pnum_args = 1;
/* Translate the opcode. */
new_opname = (char *) xmalloc (8);
sprintf (new_opname, "%s%cur%u", (has_underbar ? "_" : ""),
opname[0], (unsigned) val);
free (*popname);
*popname = new_opname;
}
return 0;
}
/* If the instruction is an idiom (i.e., a built-in macro), translate it.
Returns non-zero if an error was found. */
static int
xg_translate_idioms (popname, pnum_args, arg_strings)
char **popname;
int *pnum_args;
char **arg_strings;
{
char *opname = *popname;
bfd_boolean has_underbar = FALSE;
if (*opname == '_')
{
has_underbar = TRUE;
opname += 1;
}
if (strcmp (opname, "mov") == 0)
{
if (!has_underbar && code_density_available ())
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 (strcmp (opname, "nop") == 0)
{
if (!has_underbar && code_density_available ())
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[2] == 'r')
return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
/* WIDENING DENSITY OPCODES
questionable relaxations (widening) from old "tai" idioms:
ADD.N --> ADD
BEQZ.N --> BEQZ
RET.N --> RET
RETW.N --> RETW
MOVI.N --> MOVI
MOV.N --> MOV
NOP.N --> NOP
Note: this incomplete list was imported to match the "tai"
behavior; other density opcodes are not handled.
The xtensa-relax code may know how to do these but it doesn't do
anything when these density opcodes appear inside a no-density
region. Somehow GAS should either print an error when that happens
or do the widening. The old "tai" behavior was to do the widening.
For now, I'll make it widen but print a warning.
FIXME: GAS needs to detect density opcodes inside no-density
regions and treat them as errors. This code should be removed
when that is done. */
if (use_generics ()
&& !has_underbar
&& density_supported
&& !code_density_available ())
{
if (strcmp (opname, "add.n") == 0)
xg_replace_opname (popname, "add");
else if (strcmp (opname, "beqz.n") == 0)
xg_replace_opname (popname, "beqz");
else if (strcmp (opname, "ret.n") == 0)
xg_replace_opname (popname, "ret");
else if (strcmp (opname, "retw.n") == 0)
xg_replace_opname (popname, "retw");
else if (strcmp (opname, "movi.n") == 0)
xg_replace_opname (popname, "movi");
else if (strcmp (opname, "mov.n") == 0)
{
if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
return -1;
xg_replace_opname (popname, "or");
arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
strcpy (arg_strings[2], arg_strings[1]);
*pnum_args = 3;
}
else if (strcmp (opname, "nop.n") == 0)
{
if (xg_check_num_args (pnum_args, 0, opname, arg_strings))
return -1;
xg_replace_opname (popname, "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;
}
/* Functions for dealing with the Xtensa ISA. */
/* Return true if the given operand is an immed or target instruction,
i.e., has a reloc associated with it. Currently, this is only true
if the operand kind is "i, "l" or "L". */
static bfd_boolean
operand_is_immed (opnd)
xtensa_operand opnd;
{
const char *opkind = xtensa_operand_kind (opnd);
if (opkind[0] == '\0' || opkind[1] != '\0')
return FALSE;
switch (opkind[0])
{
case 'i':
case 'l':
case 'L':
return TRUE;
}
return FALSE;
}
/* Return true if the given operand is a pc-relative label. This is
true for "l", "L", and "r" operand kinds. */
bfd_boolean
operand_is_pcrel_label (opnd)
xtensa_operand opnd;
{
const char *opkind = xtensa_operand_kind (opnd);
if (opkind[0] == '\0' || opkind[1] != '\0')
return FALSE;
switch (opkind[0])
{
case 'r':
case 'l':
case 'L':
return TRUE;
}
return FALSE;
}
/* Currently the assembler only allows us to use a single target per
fragment. Because of this, only one operand for a given
instruction may be symbolic. If there is an operand of kind "lrL",
the last one is chosen. Otherwise, the result is the number of the
last operand of type "i", and if there are none of those, we fail
and return -1. */
int
get_relaxable_immed (opcode)
xtensa_opcode opcode;
{
int last_immed = -1;
int noperands, opi;
xtensa_operand operand;
if (opcode == XTENSA_UNDEFINED)
return -1;
noperands = xtensa_num_operands (xtensa_default_isa, opcode);
for (opi = noperands - 1; opi >= 0; opi--)
{
operand = xtensa_get_operand (xtensa_default_isa, opcode, opi);
if (operand_is_pcrel_label (operand))
return opi;
if (last_immed == -1 && operand_is_immed (operand))
last_immed = opi;
}
return last_immed;
}
xtensa_opcode
get_opcode_from_buf (buf)
const char *buf;
{
static xtensa_insnbuf insnbuf = NULL;
xtensa_opcode opcode;
xtensa_isa isa = xtensa_default_isa;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
xtensa_insnbuf_from_chars (isa, insnbuf, buf);
opcode = xtensa_decode_insn (isa, insnbuf);
return opcode;
}
static bfd_boolean
is_direct_call_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_call0_opcode
|| opcode == xtensa_call4_opcode
|| opcode == xtensa_call8_opcode
|| opcode == xtensa_call12_opcode);
}
static bfd_boolean
is_call_opcode (opcode)
xtensa_opcode opcode;
{
if (is_direct_call_opcode (opcode))
return TRUE;
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_callx0_opcode
|| opcode == xtensa_callx4_opcode
|| opcode == xtensa_callx8_opcode
|| opcode == xtensa_callx12_opcode);
}
/* Return true if the opcode is an entry opcode. This is used because
"entry" adds an implicit ".align 4" and also the entry instruction
has an extra check for an operand value. */
static bfd_boolean
is_entry_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_entry_opcode);
}
/* Return true if it is one of the loop opcodes. Loops are special
because they need automatic alignment and they have a relaxation so
complex that we hard-coded it. */
static bfd_boolean
is_loop_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_loop_opcode
|| opcode == xtensa_loopnez_opcode
|| opcode == xtensa_loopgtz_opcode);
}
static bfd_boolean
is_the_loop_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_loop_opcode);
}
static bfd_boolean
is_jx_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_jx_opcode);
}
/* Return true if the opcode is a retw or retw.n.
Needed to add nops to avoid a hardware interlock issue. */
static bfd_boolean
is_windowed_return_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
return (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode);
}
/* Return true if the opcode type is "l" and the opcode is NOT a jump. */
static bfd_boolean
is_conditional_branch_opcode (opcode)
xtensa_opcode opcode;
{
xtensa_isa isa = xtensa_default_isa;
int num_ops, i;
if (opcode == xtensa_j_opcode && opcode != XTENSA_UNDEFINED)
return FALSE;
num_ops = xtensa_num_operands (isa, opcode);
for (i = 0; i < num_ops; i++)
{
xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
if (strcmp (xtensa_operand_kind (operand), "l") == 0)
return TRUE;
}
return FALSE;
}
/* Return true if the given opcode is a conditional branch
instruction, i.e., currently this is true if the instruction
is a jx or has an operand with 'l' type and is not a loop. */
bfd_boolean
is_branch_or_jump_opcode (opcode)
xtensa_opcode opcode;
{
int opn, op_count;
if (opcode == XTENSA_UNDEFINED)
return FALSE;
if (is_loop_opcode (opcode))
return FALSE;
if (is_jx_opcode (opcode))
return TRUE;
op_count = xtensa_num_operands (xtensa_default_isa, opcode);
for (opn = 0; opn < op_count; opn++)
{
xtensa_operand opnd =
xtensa_get_operand (xtensa_default_isa, opcode, opn);
const char *opkind = xtensa_operand_kind (opnd);
if (opkind && opkind[0] == 'l' && opkind[1] == '\0')
return TRUE;
}
return FALSE;
}
/* Convert from operand numbers to BFD relocation type code.
Return BFD_RELOC_NONE on failure. */
bfd_reloc_code_real_type
opnum_to_reloc (opnum)
int opnum;
{
switch (opnum)
{
case 0:
return BFD_RELOC_XTENSA_OP0;
case 1:
return BFD_RELOC_XTENSA_OP1;
case 2:
return BFD_RELOC_XTENSA_OP2;
default:
break;
}
return BFD_RELOC_NONE;
}
/* Convert from BFD relocation type code to operand number.
Return -1 on failure. */
int
reloc_to_opnum (reloc)
bfd_reloc_code_real_type reloc;
{
switch (reloc)
{
case BFD_RELOC_XTENSA_OP0:
return 0;
case BFD_RELOC_XTENSA_OP1:
return 1;
case BFD_RELOC_XTENSA_OP2:
return 2;
default:
break;
}
return -1;
}
static void
xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line)
xtensa_insnbuf insnbuf;
xtensa_opcode opcode;
xtensa_operand operand;
int32 value;
const char *file;
unsigned int line;
{
xtensa_encode_result encode_result;
uint32 valbuf = value;
encode_result = xtensa_operand_encode (operand, &valbuf);
switch (encode_result)
{
case xtensa_encode_result_ok:
break;
case xtensa_encode_result_align:
as_bad_where ((char *) file, line,
_("operand %d not properly aligned for '%s'"),
value, xtensa_opcode_name (xtensa_default_isa, opcode));
break;
case xtensa_encode_result_not_in_table:
as_bad_where ((char *) file, line,
_("operand %d not in immediate table for '%s'"),
value, xtensa_opcode_name (xtensa_default_isa, opcode));
break;
case xtensa_encode_result_too_high:
as_bad_where ((char *) file, line,
_("operand %d too large for '%s'"), value,
xtensa_opcode_name (xtensa_default_isa, opcode));
break;
case xtensa_encode_result_too_low:
as_bad_where ((char *) file, line,
_("operand %d too small for '%s'"), value,
xtensa_opcode_name (xtensa_default_isa, opcode));
break;
case xtensa_encode_result_not_ok:
as_bad_where ((char *) file, line,
_("operand %d is invalid for '%s'"), value,
xtensa_opcode_name (xtensa_default_isa, opcode));
break;
default:
abort ();
}
xtensa_operand_set_field (operand, insnbuf, valbuf);
}
static uint32
xtensa_insnbuf_get_operand (insnbuf, opcode, opnum)
xtensa_insnbuf insnbuf;
xtensa_opcode opcode;
int opnum;
{
xtensa_operand op = xtensa_get_operand (xtensa_default_isa, opcode, opnum);
return xtensa_operand_decode (op, xtensa_operand_get_field (op, insnbuf));
}
static void
xtensa_insnbuf_set_immediate_field (opcode, insnbuf, value, file, line)
xtensa_opcode opcode;
xtensa_insnbuf insnbuf;
int32 value;
const char *file;
unsigned int line;
{
xtensa_isa isa = xtensa_default_isa;
int last_opnd = xtensa_num_operands (isa, opcode) - 1;
xtensa_operand operand = xtensa_get_operand (isa, opcode, last_opnd);
xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line);
}
static bfd_boolean
is_negatable_branch (insn)
TInsn *insn;
{
xtensa_isa isa = xtensa_default_isa;
int i;
int num_ops = xtensa_num_operands (isa, insn->opcode);
for (i = 0; i < num_ops; i++)
{
xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
char *kind = xtensa_operand_kind (opnd);
if (strlen (kind) == 1 && *kind == 'l')
return TRUE;
}
return FALSE;
}
/* Various Other Internal Functions. */
static bfd_boolean
is_unique_insn_expansion (r)
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_insn_size (insn)
TInsn *insn;
{
assert (insn->insn_type == ITYPE_INSN);
return xtensa_insn_length (xtensa_default_isa, insn->opcode);
}
static int
xg_get_build_instr_size (insn)
BuildInstr *insn;
{
assert (insn->typ == INSTR_INSTR);
return xtensa_insn_length (xtensa_default_isa, insn->opcode);
}
bfd_boolean
xg_is_narrow_insn (insn)
TInsn *insn;
{
TransitionTable *table = xg_build_widen_table ();
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))
{
/* It only generates one instruction... */
assert (insn->insn_type == ITYPE_INSN);
/* ...and it is a larger instruction. */
if (xg_get_insn_size (insn)
< xg_get_build_instr_size (rule->to_instr))
{
num_match++;
if (num_match > 1)
return FALSE;
}
}
}
return (num_match == 1);
}
bfd_boolean
xg_is_single_relaxable_insn (insn)
TInsn *insn;
{
TransitionTable *table = xg_build_widen_table ();
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);
/* ... and it is a larger instruction. */
if (xg_get_insn_size (insn)
<= xg_get_build_instr_size (rule->to_instr))
{
num_match++;
if (num_match > 1)
return FALSE;
}
}
}
return (num_match == 1);
}
/* Return the largest size instruction that this instruction can
expand to. Currently, in all cases, this is 3 bytes. Of course we
could just calculate this once and generate a table. */
int
xg_get_max_narrow_insn_size (opcode)
xtensa_opcode opcode;
{
/* Go ahead and compute it, but it better be 3. */
TransitionTable *table = xg_build_widen_table ();
TransitionList *l;
int old_size = xtensa_insn_length (xtensa_default_isa, opcode);
assert (opcode < table->num_opcodes);
/* Actually we can do better. Check to see of Only one applies. */
for (l = table->table[opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
/* If it only generates one instruction. */
if (is_unique_insn_expansion (rule))
{
int new_size = xtensa_insn_length (xtensa_default_isa,
rule->to_instr->opcode);
if (new_size > old_size)
{
assert (new_size == 3);
return 3;
}
}
}
return old_size;
}
/* Return the maximum number of bytes this opcode can expand to. */
int
xg_get_max_insn_widen_size (opcode)
xtensa_opcode opcode;
{
TransitionTable *table = xg_build_widen_table ();
TransitionList *l;
int max_size = xtensa_insn_length (xtensa_default_isa, 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 += xtensa_insn_length (xtensa_default_isa,
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;
}
/* Return the maximum number of literal bytes this opcode can generate. */
int
xg_get_max_insn_widen_literal_size (opcode)
xtensa_opcode opcode;
{
TransitionTable *table = xg_build_widen_table ();
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:
/* hard coded 4-byte literal. */
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;
}
bfd_boolean
xg_is_relaxable_insn (insn, lateral_steps)
TInsn *insn;
int lateral_steps;
{
int steps_taken = 0;
TransitionTable *table = xg_build_widen_table ();
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 ()
{
static symbolS *sym = NULL;
if (sym == NULL)
sym = symbol_find_or_make ("SPECIAL_LITERAL0\001");
return sym;
}
static symbolS *
get_special_label_symbol ()
{
static symbolS *sym = NULL;
if (sym == NULL)
sym = symbol_find_or_make ("SPECIAL_LABEL0\001");
return sym;
}
/* Return true on success. */
bfd_boolean
xg_build_to_insn (targ, insn, bi)
TInsn *targ;
TInsn *insn;
BuildInstr *bi;
{
BuildOp *op;
symbolS *sym;
memset (targ, 0, sizeof (TInsn));
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;
default:
/* currently handles:
OP_OPERAND_LOW8
OP_OPERAND_HI24S
OP_OPERAND_F32MINUS */
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; /* We cannot use a relocation for this. */
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);
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;
/* Literal with no ops. is a label? */
assert (op == NULL);
break;
default:
assert (0);
}
return TRUE;
}
/* Return true on success. */
bfd_boolean
xg_build_to_stack (istack, insn, bi)
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;
}
/* Return true on valid expansion. */
bfd_boolean
xg_expand_to_stack (istack, insn, lateral_steps)
IStack *istack;
TInsn *insn;
int lateral_steps;
{
int stack_size = istack->ninsn;
int steps_taken = 0;
TransitionTable *table = xg_build_widen_table ();
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;
/* This is it. Expand the rule to the stack. */
if (!xg_build_to_stack (istack, insn, rule->to_instr))
return FALSE;
/* Check to see if it fits. */
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;
}
bfd_boolean
xg_expand_narrow (targ, insn)
TInsn *targ;
TInsn *insn;
{
TransitionTable *table = xg_build_widen_table ();
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))
{
/* Is it a larger instruction? */
if (xg_get_insn_size (insn)
<= xg_get_build_instr_size (rule->to_instr))
{
xg_build_to_insn (targ, insn, rule->to_instr);
return FALSE;
}
}
}
return TRUE;
}
/* Assumes: All immeds are constants. Check that all constants fit
into their immeds; return false if not. */
static bfd_boolean
xg_immeds_fit (insn)
const TInsn *insn;
{
int i;
int n = insn->ntok;
assert (insn->insn_type == ITYPE_INSN);
for (i = 0; i < n; ++i)
{
const expressionS *expr = &insn->tok[i];
xtensa_operand opnd = xtensa_get_operand (xtensa_default_isa,
insn->opcode, i);
if (!operand_is_immed (opnd))
continue;
switch (expr->X_op)
{
case O_register:
case O_constant:
{
if (xg_check_operand (expr->X_add_number, opnd))
return FALSE;
}
break;
default:
/* The symbol should have a fixup associated with it. */
assert (FALSE);
break;
}
}
return TRUE;
}
/* This should only be called after we have an initial
estimate of the addresses. */
static bfd_boolean
xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
const TInsn *insn;
segT pc_seg;
fragS *pc_frag;
offsetT pc_offset;
long stretch;
{
symbolS *symbolP;
offsetT target, pc, 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];
xtensa_operand opnd = xtensa_get_operand (xtensa_default_isa,
insn->opcode, i);
if (!operand_is_immed (opnd))
continue;
switch (expr->X_op)
{
case O_register:
case O_constant:
if (xg_check_operand (expr->X_add_number, opnd))
return FALSE;
break;
case O_symbol:
/* We only allow symbols for pc-relative stuff.
If pc_frag == 0, then we don't have frag locations yet. */
if (pc_frag == 0)
return FALSE;
/* If it is PC-relative and the symbol is in the same segment as
the PC.... */
if (!xtensa_operand_isPCRelative (opnd)
|| S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
return FALSE;
symbolP = expr->X_add_symbol;
target = S_GET_VALUE (symbolP) + expr->X_add_number;
pc = pc_frag->fr_address + pc_offset;
/* If frag has yet to be reached on this pass, assume it
will move by STRETCH just as we did. If this is not so,
it will be because some frag between grows, and that will
force another pass. Beware zero-length frags. There
should be a faster way to do this. */
if (stretch && is_dnrange (pc_frag, symbolP, stretch))
target += stretch;
new_offset = xtensa_operand_do_reloc (opnd, target, pc);
if (xg_check_operand (new_offset, opnd))
return FALSE;
break;
default:
/* The symbol should have a fixup associated with it. */
return FALSE;
}
}
return TRUE;
}
/* This will check to see if the value can be converted into the
operand type. It will return true if it does not fit. */
static bfd_boolean
xg_check_operand (value, operand)
int32 value;
xtensa_operand operand;
{
uint32 valbuf = value;
return (xtensa_operand_encode (operand, &valbuf) != xtensa_encode_result_ok);
}
/* Check if a symbol is pointing to somewhere after
the start frag, given that the segment has stretched
by stretch during relaxation.
This is more complicated than it might appear at first blush
because of the stretching that goes on. Here is how the check
works:
If the symbol and the frag are in the same segment, then
the symbol could be down range. Note that this function
assumes that start_frag is in now_seg.
If the symbol is pointing to a frag with an address greater than
than the start_frag's address, then it _could_ be down range.
The problem comes because target_frag may or may not have had
stretch bytes added to its address already, depending on if it is
before or after start frag. (And if we knew that, then we wouldn't
need this function.) start_frag has definitely already had stretch
bytes added to its address.
If target_frag's address hasn't been adjusted yet, then to
determine if it comes after start_frag, we need to subtract
stretch from start_frag's address.
If target_frag's address has been adjusted, then it might have
been adjusted such that it comes after start_frag's address minus
stretch bytes.
So, in that case, we scan for it down stream to within
stretch bytes. We could search to the end of the fr_chain, but
that ends up taking too much time (over a minute on some gnu
tests). */
int
is_dnrange (start_frag, sym, stretch)
fragS *start_frag;
symbolS *sym;
long stretch;
{
if (S_GET_SEGMENT (sym) == now_seg)
{
fragS *cur_frag = symbol_get_frag (sym);
if (cur_frag->fr_address >= start_frag->fr_address - stretch)
{
int distance = stretch;
while (cur_frag && distance >= 0)
{
distance -= cur_frag->fr_fix;
if (cur_frag == start_frag)
return 0;
cur_frag = cur_frag->fr_next;
}
return 1;
}
}
return 0;
}
/* Relax the assembly instruction at least "min_steps".
Return the number of steps taken. */
int
xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
stretch)
IStack *istack;
TInsn *insn;
segT pc_seg;
fragS *pc_frag; /* If pc_frag == 0, then no pc-relative. */
offsetT pc_offset; /* Offset in fragment. */
int min_steps; /* Minimum number of conversion steps. */
long stretch; /* Number of bytes stretched so far. */
{
int steps_taken = 0;
/* assert (has no symbolic operands)
Some of its immeds don't fit.
Try to build a relaxed version.
This may go through a couple of stages
of single instruction transformations before
we get there. */
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;
}
tinsn_copy (&current_insn, insn);
/* Walk through all of the single instruction expansions. */
while (xg_is_single_relaxable_insn (&current_insn))
{
int error_val = xg_expand_narrow (&single_target, &current_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;
}
}
tinsn_copy (&current_insn, &single_target);
}
/* Now check for a multi-instruction expansion. */
while (xg_is_relaxable_insn (&current_insn, lateral_steps))
{
if (xg_symbolic_immeds_fit (&current_insn, pc_seg, pc_frag, pc_offset,
stretch))
{
if (steps_taken >= min_steps)
{
istack_push (istack, &current_insn);
return steps_taken;
}
}
steps_taken++;
if (xg_expand_to_stack (istack, &current_insn, lateral_steps))
{
if (steps_taken >= min_steps)
return steps_taken;
}
lateral_steps++;
istack->ninsn = istack_size;
}
/* It's not going to work -- use the original. */
istack_push (istack, insn);
return steps_taken;
}
static void
xg_force_frag_space (size)
int size;
{
/* This may have the side effect of creating a new fragment for the
space to go into. I just do not like the name of the "frag"
functions. */
frag_grow (size);
}
void
xg_finish_frag (last_insn, state, max_growth, is_insn)
char *last_insn;
enum xtensa_relax_statesE state;
int max_growth;
bfd_boolean is_insn;
{
/* Finish off this fragment so that it has at LEAST the desired
max_growth. If it doesn't fit in this fragment, close this one
and start a new one. In either case, return a pointer to the
beginning of the growth area. */
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,
state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
/* Just to make sure that we did not split it up. */
assert (old_frag->fr_next == frag_now);
}
static bfd_boolean
is_branch_jmp_to_next (insn, fragP)
TInsn *insn;
fragS *fragP;
{
xtensa_isa isa = xtensa_default_isa;
int i;
int num_ops = xtensa_num_operands (isa, insn->opcode);
int target_op = -1;
symbolS *sym;
fragS *target_frag;
if (is_loop_opcode (insn->opcode))
return FALSE;
for (i = 0; i < num_ops; i++)
{
xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
char *kind = xtensa_operand_kind (opnd);
if (strlen (kind) == 1 && *kind == 'l')
{
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 (insn)
TInsn *insn;
{
xtensa_isa isa = xtensa_default_isa;
int num_ops = xtensa_num_operands (isa, insn->opcode);
if (is_loop_opcode (insn->opcode))
{
int i = 1;
xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
char *kind = xtensa_operand_kind (opnd);
if (strlen (kind) == 1 && *kind == 'l')
if (insn->tok[i].X_op == O_symbol)
symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
return;
}
/* Currently, we do not add branch targets. This is an optimization
for later that tries to align only branch targets, not just any
label in a text section. */
if (align_only_targets)
{
int i;
for (i = 0; i < insn->ntok && i < num_ops; i++)
{
xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
char *kind = xtensa_operand_kind (opnd);
if (strlen (kind) == 1 && *kind == 'l'
&& 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;
}
}
}
}
/* Return the transition rule that matches or NULL if none matches. */
bfd_boolean
xg_instruction_matches_rule (insn, 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:
/* The expression must be the constant. */
assert (cond->op_num < insn->ntok);
exp1 = &insn->tok[cond->op_num];
if (!expr_is_const (exp1))
return FALSE;
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;
}
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;
}
}
return TRUE;
}
TransitionRule *
xg_instruction_match (insn)
TInsn *insn;
{
TransitionTable *table = xg_build_simplify_table ();
TransitionList *l;
assert (insn->opcode < table->num_opcodes);
/* Walk through all of the possible transitions. */
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;
}
/* Return false if no error. */
bfd_boolean
xg_build_token_insn (instr_spec, old_insn, new_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;
break;
case INSTR_LITERAL_DEF:
new_insn->insn_type = ITYPE_LITERAL;
new_insn->opcode = XTENSA_UNDEFINED;
new_insn->is_specific_opcode = FALSE;
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:
/* The expression must be the 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;
}
/* Return true if it was simplified. */
bfd_boolean
xg_simplify_insn (old_insn, new_insn)
TInsn *old_insn;
TInsn *new_insn;
{
TransitionRule *rule = xg_instruction_match (old_insn);
BuildInstr *insn_spec;
if (rule == NULL)
return FALSE;
insn_spec = rule->to_instr;
/* There should only be one. */
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;
}
/* xg_expand_assembly_insn: (1) Simplify the instruction, i.e., l32i ->
l32i.n. (2) Check the number of operands. (3) Place the instruction
tokens into the stack or if we can relax it at assembly time, place
multiple instructions/literals onto the stack. Return false if no
error. */
static bfd_boolean
xg_expand_assembly_insn (istack, orig_insn)
IStack *istack;
TInsn *orig_insn;
{
int noperands;
TInsn new_insn;
memset (&new_insn, 0, sizeof (TInsn));
/* On return, we will be using the "use_tokens" with "use_ntok".
This will reduce things like addi to addi.n. */
if (code_density_available () && !orig_insn->is_specific_opcode)
{
if (xg_simplify_insn (orig_insn, &new_insn))
orig_insn = &new_insn;
}
noperands = xtensa_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);
/* If there are not enough operands, we will assert above. If there
are too many, just cut out the extras here. */
orig_insn->ntok = noperands;
/* Cases:
Instructions with all constant immeds:
Assemble them and relax the instruction if possible.
Give error if not possible; no fixup needed.
Instructions with symbolic immeds:
Assemble them with a Fix up (that may cause instruction expansion).
Also close out the fragment if the fixup may cause instruction expansion.
There are some other special cases where we need alignment.
1) before certain instructions with required alignment (OPCODE_ALIGN)
2) before labels that have jumps (LABEL_ALIGN)
3) after call instructions (RETURN_ALIGN)
Multiple of these may be possible on the same fragment.
If so, make sure to satisfy the required alignment.
Then try to get the desired alignment. */
if (tinsn_has_invalid_symbolic_operands (orig_insn))
return TRUE;
if (orig_insn->is_specific_opcode || !can_relax ())
{
istack_push (istack, orig_insn);
return FALSE;
}
if (tinsn_has_symbolic_operands (orig_insn))
{
if (tinsn_has_complex_operands (orig_insn))
xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
else
istack_push (istack, orig_insn);
}
else
{
if (xg_immeds_fit (orig_insn))
istack_push (istack, orig_insn);
else
xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
}
#if 0
for (i = 0; i < istack->ninsn; i++)
{
if (xg_simplify_insn (&new_insn, &istack->insn[i]))
istack->insn[i] = new_insn;
}
#endif
return FALSE;
}
/* Currently all literals that are generated here are 32-bit L32R targets. */
symbolS *
xg_assemble_literal (insn)
/* const */ TInsn *insn;
{
emit_state state;
symbolS *lit_sym = NULL;
/* size = 4 for L32R. It could easily be larger when we move to
larger constants. Add a parameter later. */
offsetT litsize = 4;
offsetT litalign = 2; /* 2^2 = 4 */
expressionS saved_loc;
set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
assert (insn->insn_type == ITYPE_LITERAL);
assert (insn->ntok == 1); /* must be only one token here */
xtensa_switch_to_literal_fragment (&state);
/* Force a 4-byte align here. Note that this opens a new frag, so all
literals done with this function have a frag to themselves. That's
important for the way text section literals work. */
frag_align (litalign, 0, 0);
emit_expr (&insn->tok[0], 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;
/* Go back. */
xtensa_restore_emit_state (&state);
return lit_sym;
}
static void
xg_assemble_literal_space (size)
/* const */ int size;
{
emit_state state;
/* We might have to do something about this alignment. It only
takes effect if something is placed here. */
offsetT litalign = 2; /* 2^2 = 4 */
fragS *lit_saved_frag;
expressionS saved_loc;
assert (size % 4 == 0);
set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
xtensa_switch_to_literal_fragment (&state);
/* Force a 4-byte align here. */
frag_align (litalign, 0, 0);
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, size, FALSE);
/* Go back. */
xtensa_restore_emit_state (&state);
frag_now->tc_frag_data.literal_frag = lit_saved_frag;
}
symbolS *
xtensa_create_literal_symbol (sec, frag)
segT sec;
fragS *frag;
{
static int lit_num = 0;
static char name[256];
symbolS *symbolP;
sprintf (name, ".L_lit_sym%d", lit_num);
/* Create a local symbol. If it is in a linkonce section, we have to
be careful to make sure that if it is used in a relocation that the
symbol will be in the output file. */
if (get_is_linkonce_section (stdoutput, sec))
{
symbolP = symbol_new (name, sec, 0, frag);
S_CLEAR_EXTERNAL (symbolP);
/* symbolP->local = 1; */
}
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 void
xtensa_add_literal_sym (sym)
symbolS *sym;
{
sym_list *l;
l = (sym_list *) xmalloc (sizeof (sym_list));
l->sym = sym;
l->next = literal_syms;
literal_syms = l;
}
static void
xtensa_add_insn_label (sym)
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;
}
/* Return true if the section flags are marked linkonce
or the name is .gnu.linkonce*. */
bfd_boolean
get_is_linkonce_section (abfd, sec)
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);
/* Flags might not be set yet. */
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);
}
/* Emit an instruction to the current fragment. If record_fix is true,
then this instruction will not change and we can go ahead and record
the fixup. If record_fix is false, then the instruction may change
and we are going to close out this fragment. Go ahead and set the
fr_symbol and fr_offset instead of adding a fixup. */
static bfd_boolean
xg_emit_insn (t_insn, record_fix)
TInsn *t_insn;
bfd_boolean record_fix;
{
bfd_boolean ok = TRUE;
xtensa_isa isa = xtensa_default_isa;
xtensa_opcode opcode = t_insn->opcode;
bfd_boolean has_fixup = FALSE;
int noperands;
int i, byte_count;
fragS *oldfrag;
size_t old_size;
char *f;
static xtensa_insnbuf insnbuf = NULL;
/* Use a static pointer to the insn buffer so we don't have to call
malloc each time through. */
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
has_fixup = tinsn_to_insnbuf (t_insn, insnbuf);
noperands = xtensa_num_operands (isa, opcode);
assert (noperands == t_insn->ntok);
byte_count = xtensa_insn_length (isa, opcode);
oldfrag = frag_now;
/* This should NEVER cause us to jump into a new frag;
we've already reserved space. */
old_size = frag_now_fix ();
f = frag_more (byte_count);
assert (oldfrag == frag_now);
/* This needs to generate a record that lists the parts that are
instructions. */
if (!frag_now->tc_frag_data.is_insn)
{
/* If we are at the beginning of a fragment, switch this
fragment to an instruction fragment. */
if (now_seg != absolute_section && old_size != 0)
as_warn (_("instruction fragment may contain data"));
frag_now->tc_frag_data.is_insn = TRUE;
}
xtensa_insnbuf_to_chars (isa, insnbuf, f);
dwarf2_emit_insn (byte_count);
/* Now spit out the opcode fixup.... */
if (!has_fixup)
return !ok;
for (i = 0; i < noperands; ++i)
{
expressionS *expr = &t_insn->tok[i];
switch (expr->X_op)
{
case O_symbol:
if (get_relaxable_immed (opcode) == i)
{
if (record_fix)
{
if (!xg_add_opcode_fix (opcode, i, expr, frag_now,
f - frag_now->fr_literal))
ok = FALSE;
}
else
{
/* Write it to the fr_offset, fr_symbol. */
frag_now->fr_symbol = expr->X_add_symbol;
frag_now->fr_offset = expr->X_add_number;
}
}
else
{
as_bad (_("invalid operand %d on '%s'"),
i, xtensa_opcode_name (isa, opcode));
ok = FALSE;
}
break;
case O_constant:
case O_register:
break;
default:
as_bad (_("invalid expression for operand %d on '%s'"),
i, xtensa_opcode_name (isa, opcode));
ok = FALSE;
break;
}
}
return !ok;
}
static bfd_boolean
xg_emit_insn_to_buf (t_insn, buf, fragP, offset, build_fix)
TInsn *t_insn;
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 (t_insn, insnbuf);
if (has_symbolic_immed && build_fix)
{
/* Add a fixup. */
int opnum = get_relaxable_immed (t_insn->opcode);
expressionS *exp = &t_insn->tok[opnum];
if (!xg_add_opcode_fix (t_insn->opcode,
opnum, exp, fragP, offset))
ok = FALSE;
}
fragP->tc_frag_data.is_insn = TRUE;
xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
return ok;
}
/* Put in a fixup record based on the opcode.
Return true on success. */
bfd_boolean
xg_add_opcode_fix (opcode, opnum, expr, fragP, offset)
xtensa_opcode opcode;
int opnum;
expressionS *expr;
fragS *fragP;
offsetT offset;
{
bfd_reloc_code_real_type reloc;
reloc_howto_type *howto;
int insn_length;
fixS *the_fix;
reloc = opnum_to_reloc (opnum);
if (reloc == BFD_RELOC_NONE)
{
as_bad (_("invalid relocation operand %i on '%s'"),
opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
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;
}
insn_length = xtensa_insn_length (xtensa_default_isa, opcode);
the_fix = fix_new_exp (fragP, offset, insn_length, expr,
howto->pc_relative, reloc);
if (expr->X_add_symbol &&
(S_IS_EXTERNAL (expr->X_add_symbol) || S_IS_WEAK (expr->X_add_symbol)))
the_fix->fx_plt = TRUE;
return TRUE;
}
void
xg_resolve_literals (insn, lit_sym)
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;
}
void
xg_resolve_labels (insn, label_sym)
TInsn *insn;
symbolS *label_sym;
{
symbolS *sym = get_special_label_symbol ();
int i;
/* assert(!insn->is_literal); */
for (i = 0; i < insn->ntok; i++)
if (insn->tok[i].X_add_symbol == sym)
insn->tok[i].X_add_symbol = label_sym;
}
static void
xg_assemble_tokens (insn)
/*const */ TInsn *insn;
{
/* By the time we get here, there's not too much left to do.
1) Check our assumptions.
2) Check if the current instruction is "narrow".
If so, then finish the frag, create another one.
We could also go back to change some previous
"narrow" frags into no-change ones if we have more than
MAX_NARROW_ALIGNMENT of them without alignment restrictions
between them.
Cases:
1) It has constant operands and doesn't fit.
Go ahead and assemble it so it will fail.
2) It has constant operands that fit.
If narrow and !is_specific_opcode,
assemble it and put in a relocation
else
assemble it.
3) It has a symbolic immediate operand
a) Find the worst-case relaxation required
b) Find the worst-case literal pool space required.
Insert appropriate alignment & space in the literal.
Assemble it.
Add the relocation. */
assert (insn->insn_type == ITYPE_INSN);
if (!tinsn_has_symbolic_operands (insn))
{
if (xg_is_narrow_insn (insn) && !insn->is_specific_opcode)
{
/* assemble it but add max required space */
int max_size = xg_get_max_narrow_insn_size (insn->opcode);
int min_size = xg_get_insn_size (insn);
char *last_insn;
assert (max_size == 3);
/* make sure we have enough space to widen it */
xg_force_frag_space (max_size);
/* Output the instruction. It may cause an error if some
operands do not fit. */
last_insn = frag_more (0);
if (xg_emit_insn (insn, TRUE))
as_warn (_("instruction with constant operands does not fit"));
xg_finish_frag (last_insn, RELAX_NARROW, max_size - min_size, TRUE);
}
else
{
/* Assemble it. No relocation needed. */
int max_size = xg_get_insn_size (insn);
xg_force_frag_space (max_size);
if (xg_emit_insn (insn, FALSE))
as_warn (_("instruction with constant operands does not "
"fit without widening"));
/* frag_more (max_size); */
/* Special case for jx. If the jx is the next to last
instruction in a loop, we will add a NOP after it. This
avoids a hardware issue that could occur if the jx jumped
to the next instruction. */
if (software_avoid_b_j_loop_end
&& is_jx_opcode (insn->opcode))
{
maybe_has_b_j_loop_end = TRUE;
/* add 2 of these */
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, 4,
RELAX_ADD_NOP_IF_PRE_LOOP_END,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
}
}
}
else
{
/* Need to assemble it with space for the relocation. */
if (!insn->is_specific_opcode)
{
/* Assemble it but add max required space. */
char *last_insn;
int min_size = xg_get_insn_size (insn);
int max_size = xg_get_max_insn_widen_size (insn->opcode);
int max_literal_size =
xg_get_max_insn_widen_literal_size (insn->opcode);
#if 0
symbolS *immed_sym = xg_get_insn_immed_symbol (insn);
set_frag_segment (frag_now, now_seg);
#endif /* 0 */
/* Make sure we have enough space to widen the instruction.
This may open a new fragment. */
xg_force_frag_space (max_size);
if (max_literal_size != 0)
xg_assemble_literal_space (max_literal_size);
/* Output the instruction. It may cause an error if some
operands do not fit. Emit the incomplete instruction. */
last_insn = frag_more (0);
xg_emit_insn (insn, FALSE);
xg_finish_frag (last_insn, RELAX_IMMED, max_size - min_size, TRUE);
/* Special cases for loops:
close_loop_end should be inserted AFTER short_loop.
Make sure that CLOSE loops are processed BEFORE short_loops
when converting them. */
/* "short_loop": add a NOP if the loop is < 4 bytes. */
if (software_avoid_short_loop
&& is_loop_opcode (insn->opcode))
{
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);
}
/* "close_loop_end": Add up to 12 bytes of NOPs to keep a
loop at least 12 bytes away from another loop's loop
end. */
if (software_avoid_close_loop_end
&& is_loop_opcode (insn->opcode))
{
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);
}
}
else
{
/* Assemble it in place. No expansion will be required,
but we'll still need a relocation record. */
int max_size = xg_get_insn_size (insn);
xg_force_frag_space (max_size);
if (xg_emit_insn (insn, TRUE))
as_warn (_("instruction's constant operands do not fit"));
}
}
}
/* Return true if the instruction can write to the specified
integer register. */
static bfd_boolean
is_register_writer (insn, regset, regnum)
const TInsn *insn;
const char *regset;
int regnum;
{
int i;
int num_ops;
xtensa_isa isa = xtensa_default_isa;
num_ops = xtensa_num_operands (isa, insn->opcode);
for (i = 0; i < num_ops; i++)
{
xtensa_operand operand = xtensa_get_operand (isa, insn->opcode, i);
char inout = xtensa_operand_inout (operand);
if (inout == '>' || inout == '=')
{
if (strcmp (xtensa_operand_kind (operand), regset) == 0)
{
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 (tinsn)
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)
return TRUE;
/* An RSR of LCOUNT is illegal as the last opcode in a loop. */
if (opcode == xtensa_rsr_opcode
&& tinsn->ntok >= 2
&& tinsn->tok[1].X_op == O_constant
&& tinsn->tok[1].X_add_number == 2)
return TRUE;
return FALSE;
}
/* Labels that begin with ".Ln" or ".LM" are unaligned.
This allows the debugger to add unaligned labels.
Also, the assembler generates stabs labels that need
not be aligned: FAKE_LABEL_NAME . {"F", "L", "endfunc"}. */
bfd_boolean
is_unaligned_label (sym)
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;
/* FAKE_LABEL_NAME followed by "F", "L" or "endfunc" */
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;
}
fragS *
next_non_empty_frag (fragP)
const fragS *fragP;
{
fragS *next_fragP = fragP->fr_next;
/* Sometimes an empty will end up here due storage allocation issues.
So we have to skip until we find something legit. */
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;
}
xtensa_opcode
next_frag_opcode (fragP)
const fragS * fragP;
{
const fragS *next_fragP = next_non_empty_frag (fragP);
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
if (next_fragP == NULL)
return XTENSA_UNDEFINED;
xtensa_insnbuf_from_chars (isa, insnbuf, next_fragP->fr_literal);
return xtensa_decode_insn (isa, insnbuf);
}
/* Return true if the target frag is one of the next non-empty frags. */
bfd_boolean
is_next_frag_target (fragP, 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;
}
/* If the next legit fragment is an end-of-loop marker,
switch its state so it will instantiate a NOP. */
static void
update_next_frag_nop_state (fragP)
fragS *fragP;
{
fragS *next_fragP = fragP->fr_next;
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 (fragP)
const fragS *fragP;
{
/* Sometimes an empty will end up here due storage allocation issues,
so we have to skip until we find something legit. */
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 (fragP)
const fragS *fragP;
{
/* Sometimes an empty will end up here due storage allocation issues.
So we have to skip until we find something legit. */
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 (fragp)
const fragS *fragp;
{
const fragS *next_fragp = fragp->fr_next;
xtensa_opcode next_opcode = next_frag_opcode (fragp);
if (!is_loop_opcode (next_opcode))
return 0;
/* Sometimes an empty will end up here due storage allocation issues.
So we have to skip until we find something legit. */
while (next_fragp->fr_fix == 0)
next_fragp = next_fragp->fr_next;
if (next_fragp->fr_type != rs_machine_dependent)
return 0;
/* There is some implicit knowledge encoded in here.
The LOOP instructions that are NOT RELAX_IMMED have
been relaxed. */
if (next_fragp->fr_subtype > RELAX_IMMED)
return get_expanded_loop_offset (next_opcode);
return 0;
}
/* Mark a location where we can later insert literal frags. Update
the section's literal_pool_loc, so subsequent literals can be
placed nearest to their use. */
static void
xtensa_mark_literal_pool_location ()
{
/* Any labels pointing to the current location need
to be adjusted to after the literal pool. */
emit_state s;
fragS *pool_location;
frag_align (2, 0, 0);
/* We stash info in the fr_var of these frags
so we can later move the literal's fixes into this
frchain's fix list. We can use fr_var because fr_var's
interpretation depends solely on the fr_type and subtype. */
pool_location = frag_now;
frag_variant (rs_machine_dependent, 0, (int) frchain_now,
RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
frag_variant (rs_machine_dependent, 0, (int) now_seg,
RELAX_LITERAL_POOL_END, NULL, 0, NULL);
/* Now put a frag into the literal pool that points to this location. */
set_literal_pool_location (now_seg, pool_location);
xtensa_switch_to_literal_fragment (&s);
/* Close whatever frag is there. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
frag_now->tc_frag_data.literal_frag = pool_location;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_restore_emit_state (&s);
}
/* The "loops_ok" argument is provided to allow ignoring labels that
define loop ends. This fixes a bug where the NOPs to align a
loop opcode were included in a previous zero-cost loop:
loop a0, loopend
<loop1 body>
loopend:
loop a2, loopend2
<loop2 body>
would become:
loop a0, loopend
<loop1 body>
nop.n <===== bad!
loopend:
loop a2, loopend2
<loop2 body>
This argument is used to prevent moving the NOP to before the
loop-end label, which is what you want in this special case. */
static void
xtensa_move_labels (new_frag, new_offset, loops_ok)
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 == 0)
{
S_SET_VALUE (lit_sym, new_offset);
symbol_set_frag (lit_sym, new_frag);
}
}
}
/* Assemble a NOP of the requested size in the buffer. User must have
allocated "buf" with at least "size" bytes. */
void
assemble_nop (size, buf)
size_t size;
char *buf;
{
static xtensa_insnbuf insnbuf = NULL;
TInsn t_insn;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_init (&t_insn);
switch (size)
{
case 2:
t_insn.opcode = xtensa_nop_n_opcode;
t_insn.ntok = 0;
if (t_insn.opcode == XTENSA_UNDEFINED)
as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
tinsn_to_insnbuf (&t_insn, insnbuf);
xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
break;
case 3:
t_insn.opcode = xtensa_or_opcode;
assert (t_insn.opcode != XTENSA_UNDEFINED);
if (t_insn.opcode == XTENSA_UNDEFINED)
as_fatal (_("opcode 'OR' unavailable in this configuration"));
set_expr_const (&t_insn.tok[0], 1);
set_expr_const (&t_insn.tok[1], 1);
set_expr_const (&t_insn.tok[2], 1);
t_insn.ntok = 3;
tinsn_to_insnbuf (&t_insn, insnbuf);
xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
break;
default:
as_fatal (_("invalid %d-byte NOP requested"), size);
}
}
/* Return the number of bytes for the offset of the expanded loop
instruction. This should be incorporated into the relaxation
specification but is hard-coded here. This is used to auto-align
the loop instruction. It is invalid to call this function if the
configuration does not have loops or if the opcode is not a loop
opcode. */
static addressT
get_expanded_loop_offset (opcode)
xtensa_opcode opcode;
{
/* This is the OFFSET of the loop instruction in the expanded loop.
This MUST correspond directly to the specification of the loop
expansion. It will be validated on fragment conversion. */
if (opcode == XTENSA_UNDEFINED)
as_fatal (_("get_expanded_loop_offset: undefined opcode"));
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;
}
fragS *
get_literal_pool_location (seg)
segT seg;
{
return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
}
static void
set_literal_pool_location (seg, literal_pool_loc)
segT seg;
fragS *literal_pool_loc;
{
seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
}
/* External Functions and Other GAS Hooks. */
const char *
xtensa_target_format ()
{
return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
}
void
xtensa_file_arch_init (abfd)
bfd *abfd;
{
bfd_set_private_flags (abfd, 0x100 | 0x200);
}
void
md_number_to_chars (buf, val, n)
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);
}
/* This function is called once, at assembler startup time. It should
set up all the tables, etc. that the MD part of the assembler will
need. */
void
md_begin ()
{
segT current_section = now_seg;
int current_subsec = now_subseg;
xtensa_isa isa;
#if STATIC_LIBISA
isa = xtensa_isa_init ();
#else
/* ISA was already initialized by xtensa_init(). */
isa = xtensa_default_isa;
#endif
/* Set up the .literal, .fini.literal and .init.literal sections. */
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;
subseg_set (current_section, current_subsec);
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_entry_opcode = xtensa_opcode_lookup (isa, "entry");
xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
xtensa_j_opcode = xtensa_opcode_lookup (isa, "j");
xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
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_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_opcode = xtensa_opcode_lookup (isa, "rsr");
xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
}
/* tc_frob_label hook */
void
xtensa_frob_label (sym)
symbolS *sym;
{
if (generating_literals)
xtensa_add_literal_sym (sym);
else
xtensa_add_insn_label (sym);
if (symbol_get_tc (sym)->is_loop_target
&& (get_last_insn_flags (now_seg, now_subseg)
& FLAG_IS_BAD_LOOPEND) != 0)
as_bad (_("invalid last instruction for a zero-overhead loop"));
/* No target aligning in the absolute section. */
if (now_seg != absolute_section
&& align_targets
&& !is_unaligned_label (sym)
&& !frag_now->tc_frag_data.is_literal)
{
/* frag_now->tc_frag_data.is_insn = TRUE; */
frag_var (rs_machine_dependent, 4, 4,
RELAX_DESIRE_ALIGN_IF_TARGET,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
xtensa_move_labels (frag_now, 0, TRUE);
/* If the label is already known to be a branch target, i.e., a
forward branch, mark the frag accordingly. Backward branches
are handled by xg_add_branch_and_loop_targets. */
if (symbol_get_tc (sym)->is_branch_target)
symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
/* Loops only go forward, so they can be identified here. */
if (symbol_get_tc (sym)->is_loop_target)
symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
}
}
/* md_flush_pending_output hook */
void
xtensa_flush_pending_output ()
{
/* If there is a non-zero instruction fragment, close it. */
if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
{
frag_wane (frag_now);
frag_new (0);
}
frag_now->tc_frag_data.is_insn = FALSE;
xtensa_clear_insn_labels ();
}
void
md_assemble (str)
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;
IStack istack; /* Put instructions into here. */
TInsn orig_insn; /* Original instruction from the input. */
int i;
symbolS *lit_sym = NULL;
if (frag_now->tc_frag_data.is_literal)
{
static bfd_boolean reported = 0;
if (reported < 4)
as_bad (_("cannot assemble '%s' into a literal fragment"), str);
if (reported == 3)
as_bad (_("..."));
reported++;
return;
}
istack_init (&istack);
tinsn_init (&orig_insn);
/* Split off the opcode. */
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;
/* Check for an underbar prefix. */
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_generics ());
specific_opcode = orig_insn.is_specific_opcode;
orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
if (orig_insn.opcode == XTENSA_UNDEFINED)
{
as_bad (_("unknown opcode %s"), opname);
return;
}
if (frag_now_fix () != 0 && !frag_now->tc_frag_data.is_insn)
{
frag_wane (frag_now);
frag_new (0);
}
if (software_a0_b_retw_interlock)
{
if ((get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
&& is_conditional_branch_opcode (orig_insn.opcode))
{
has_a0_b_retw = TRUE;
/* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
After the first assembly pass we will check all of them and
add a nop if needed. */
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);
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);
}
}
/* Special case: The call instructions should be marked "specific opcode"
to keep them from expanding. */
if (!use_longcalls () && is_direct_call_opcode (orig_insn.opcode))
orig_insn.is_specific_opcode = TRUE;
/* Parse the arguments. */
if (parse_arguments (&orig_insn, num_args, arg_strings))
{
as_bad (_("syntax error"));
return;
}
/* Free the opcode and argument strings, now that they've been parsed. */
free (has_underbar ? opname - 1 : opname);
opname = 0;
while (num_args-- > 0)
free (arg_strings[num_args]);
/* Check for the right number and type of arguments. */
if (tinsn_check_arguments (&orig_insn))
return;
/* See if the instruction implies an aligned section. */
if (is_entry_opcode (orig_insn.opcode) || is_loop_opcode (orig_insn.opcode))
record_alignment (now_seg, 2);
xg_add_branch_and_loop_targets (&orig_insn);
/* Special cases for instructions that force an alignment... */
if (!orig_insn.is_specific_opcode && is_loop_opcode (orig_insn.opcode))
{
size_t max_fill;
frag_now->tc_frag_data.is_insn = TRUE;
frag_now->tc_frag_data.is_no_density = !code_density_available ();
max_fill = get_text_align_max_fill_size
(get_text_align_power (XTENSA_FETCH_WIDTH),
TRUE, frag_now->tc_frag_data.is_no_density);
frag_var (rs_machine_dependent, max_fill, max_fill,
RELAX_ALIGN_NEXT_OPCODE, frag_now->fr_symbol,
frag_now->fr_offset, NULL);
xtensa_move_labels (frag_now, 0, FALSE);
}
/* Special-case for "entry" instruction. */
if (is_entry_opcode (orig_insn.opcode))
{
/* Check that the second opcode (#1) is >= 16. */
if (orig_insn.ntok >= 2)
{
expressionS *exp = &orig_insn.tok[1];
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 (!orig_insn.is_specific_opcode)
{
xtensa_mark_literal_pool_location ();
/* Automatically align ENTRY instructions. */
xtensa_move_labels (frag_now, 0, TRUE);
frag_align (2, 0, 0);
}
}
/* Any extra alignment frags have been inserted now, and we're about to
emit a new instruction so clear the list of labels. */
xtensa_clear_insn_labels ();
if (software_a0_b_retw_interlock)
set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
is_register_writer (&orig_insn, "a", 0));
set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
is_bad_loopend_opcode (&orig_insn));
/* Finish it off:
assemble_tokens (opcode, tok, ntok);
expand the tokens from the orig_insn into the
stack of instructions that will not expand
unless required at relaxation time. */
if (xg_expand_assembly_insn (&istack, &orig_insn))
return;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *insn = &istack.insn[i];
if (insn->insn_type == ITYPE_LITERAL)
{
assert (lit_sym == NULL);
lit_sym = xg_assemble_literal (insn);
}
else
{
if (lit_sym)
xg_resolve_literals (insn, lit_sym);
xg_assemble_tokens (insn);
}
}
/* Now, if the original opcode was a call... */
if (align_targets && is_call_opcode (orig_insn.opcode))
{
frag_now->tc_frag_data.is_insn = TRUE;
frag_var (rs_machine_dependent, 4, 4,
RELAX_DESIRE_ALIGN,
frag_now->fr_symbol,
frag_now->fr_offset,
NULL);
}
}
/* TC_CONS_FIX_NEW hook: Check for "@PLT" suffix on symbol references.
If found, use an XTENSA_PLT reloc for 4-byte values. Otherwise, this
is the same as the standard code in read.c. */
void
xtensa_cons_fix_new (frag, where, size, exp)
fragS *frag;
int where;
int size;
expressionS *exp;
{
bfd_reloc_code_real_type r;
bfd_boolean plt = FALSE;
if (*input_line_pointer == '@')
{
if (!strncmp (input_line_pointer, PLT_SUFFIX, strlen (PLT_SUFFIX) - 1)
&& !strncmp (input_line_pointer, plt_suffix,
strlen (plt_suffix) - 1))
{
as_bad (_("undefined @ suffix '%s', expected '%s'"),
input_line_pointer, plt_suffix);
ignore_rest_of_line ();
return;
}
input_line_pointer += strlen (plt_suffix);
plt = TRUE;
}
switch (size)
{
case 1:
r = BFD_RELOC_8;
break;
case 2:
r = BFD_RELOC_16;
break;
case 4:
r = plt ? BFD_RELOC_XTENSA_PLT : BFD_RELOC_32;
break;
case 8:
r = BFD_RELOC_64;
break;
default:
as_bad (_("unsupported BFD relocation size %u"), size);
r = BFD_RELOC_32;
break;
}
fix_new_exp (frag, where, size, exp, 0, r);
}
/* TC_FRAG_INIT hook */
void
xtensa_frag_init (frag)
fragS *frag;
{
frag->tc_frag_data.is_no_density = !code_density_available ();
}
symbolS *
md_undefined_symbol (name)
char *name ATTRIBUTE_UNUSED;
{
return NULL;
}
/* Round up a section size to the appropriate boundary. */
valueT
md_section_align (segment, size)
segT segment ATTRIBUTE_UNUSED;
valueT size;
{
return size; /* Byte alignment is fine. */
}
long
md_pcrel_from (fixP)
fixS *fixP;
{
char *insn_p;
static xtensa_insnbuf insnbuf = NULL;
int opnum;
xtensa_operand operand;
xtensa_opcode opcode;
xtensa_isa isa = xtensa_default_isa;
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
if (fixP->fx_done)
return addr;
if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
return addr;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
xtensa_insnbuf_from_chars (isa, insnbuf, insn_p);
opcode = xtensa_decode_insn (isa, insnbuf);
opnum = reloc_to_opnum (fixP->fx_r_type);
if (opnum < 0)
as_fatal (_("invalid operand relocation for '%s' instruction"),
xtensa_opcode_name (isa, opcode));
if (opnum >= xtensa_num_operands (isa, opcode))
as_fatal (_("invalid relocation for operand %d in '%s' instruction"),
opnum, xtensa_opcode_name (isa, opcode));
operand = xtensa_get_operand (isa, opcode, opnum);
if (!operand)
{
as_warn_where (fixP->fx_file,
fixP->fx_line,
_("invalid relocation type %d for %s instruction"),
fixP->fx_r_type, xtensa_opcode_name (isa, opcode));
return addr;
}
if (!operand_is_pcrel_label (operand))
{
as_bad_where (fixP->fx_file,
fixP->fx_line,
_("invalid relocation for operand %d of '%s'"),
opnum, xtensa_opcode_name (isa, opcode));
return addr;
}
if (!xtensa_operand_isPCRelative (operand))
{
as_warn_where (fixP->fx_file,
fixP->fx_line,
_("non-PCREL relocation operand %d for '%s': %s"),
opnum, xtensa_opcode_name (isa, opcode),
bfd_get_reloc_code_name (fixP->fx_r_type));
return addr;
}
return 0 - xtensa_operand_do_reloc (operand, 0, addr);
}
/* tc_symbol_new_hook */
void
xtensa_symbol_new_hook (symbolP)
symbolS *symbolP;
{
symbol_get_tc (symbolP)->plt = 0;
}
/* tc_fix_adjustable hook */
bfd_boolean
xtensa_fix_adjustable (fixP)
fixS *fixP;
{
/* We need the symbol name for the VTABLE entries. */
if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
return 0;
return 1;
}
void
md_apply_fix3 (fixP, valP, seg)
fixS *fixP;
valueT *valP;
segT seg ATTRIBUTE_UNUSED;
{
if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
{
/* This happens when the relocation is within the current section.
It seems this implies a PCREL operation. We'll catch it and error
if not. */
char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
static xtensa_insnbuf insnbuf = NULL;
xtensa_opcode opcode;
xtensa_isa isa;
switch (fixP->fx_r_type)
{
case BFD_RELOC_XTENSA_ASM_EXPAND:
fixP->fx_done = 1;
break;
case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
as_bad (_("unhandled local relocation fix %s"),
bfd_get_reloc_code_name (fixP->fx_r_type));
break;
case BFD_RELOC_32:
case BFD_RELOC_16:
case BFD_RELOC_8:
/* The only one we support that isn't an instruction field. */
md_number_to_chars (fixpos, *valP, fixP->fx_size);
fixP->fx_done = 1;
break;
case BFD_RELOC_XTENSA_OP0:
case BFD_RELOC_XTENSA_OP1:
case BFD_RELOC_XTENSA_OP2:
isa = xtensa_default_isa;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
xtensa_insnbuf_from_chars (isa, insnbuf, fixpos);
opcode = xtensa_decode_insn (isa, insnbuf);
if (opcode == XTENSA_UNDEFINED)
as_fatal (_("undecodable FIX"));
xtensa_insnbuf_set_immediate_field (opcode, insnbuf, *valP,
fixP->fx_file, fixP->fx_line);
fixP->fx_frag->tc_frag_data.is_insn = TRUE;
xtensa_insnbuf_to_chars (isa, insnbuf, fixpos);
fixP->fx_done = 1;
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 (type, litP, sizeP)
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 (fragP, seg)
fragS *fragP;
segT seg ATTRIBUTE_UNUSED;
{
return fragP->tc_frag_data.text_expansion;
}
/* Translate internal representation of relocation info to BFD target
format. */
arelent *
tc_gen_reloc (section, fixp)
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;
/* Make sure none of our internal relocations make it this far.
They'd better have been fully resolved by this point. */
assert ((int) fixp->fx_r_type > 0);
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));
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));
}
assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
reloc->addend = fixp->fx_offset;
switch (fixp->fx_r_type)
{
case BFD_RELOC_XTENSA_OP0:
case BFD_RELOC_XTENSA_OP1:
case BFD_RELOC_XTENSA_OP2:
case BFD_RELOC_XTENSA_ASM_EXPAND:
case BFD_RELOC_32:
case BFD_RELOC_XTENSA_PLT:
case BFD_RELOC_VTABLE_INHERIT:
case BFD_RELOC_VTABLE_ENTRY:
break;
case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
as_warn (_("emitting simplification relocation"));
break;
default:
as_warn (_("emitting unknown relocation"));
}
return reloc;
}
void
xtensa_end ()
{
directive_balance ();
xtensa_move_literals ();
xtensa_reorder_segments ();
xtensa_cleanup_align_frags ();
xtensa_fix_target_frags ();
if (software_a0_b_retw_interlock && has_a0_b_retw)
xtensa_fix_a0_b_retw_frags ();
if (software_avoid_b_j_loop_end && maybe_has_b_j_loop_end)
xtensa_fix_b_j_loop_end_frags ();
/* "close_loop_end" should be processed BEFORE "short_loop". */
if (software_avoid_close_loop_end && maybe_has_close_loop_end)
xtensa_fix_close_loop_end_frags ();
if (software_avoid_short_loop && maybe_has_short_loop)
xtensa_fix_short_loop_frags ();
xtensa_sanity_check ();
}
static void
xtensa_cleanup_align_frags ()
{
frchainS *frchP;
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if ((fragP->fr_type == rs_align
|| fragP->fr_type == rs_align_code
|| (fragP->fr_type == rs_machine_dependent
&& (fragP->fr_subtype == RELAX_DESIRE_ALIGN
|| fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
&& fragP->fr_fix == 0)
{
fragS * next = fragP->fr_next;
while (next
&& next->fr_type == rs_machine_dependent
&& next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
{
frag_wane (next);
next = next->fr_next;
}
}
}
}
}
/* Re-process all of the fragments looking to convert all of the
RELAX_DESIRE_ALIGN_IF_TARGET fragments. If there is a branch
target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
If the next fragment starts with a loop target, AND the previous
fragment can be expanded to negate the branch, convert this to a
RELAX_LOOP_END. Otherwise, convert to a .fill 0. */
static void
xtensa_fix_target_frags ()
{
frchainS *frchP;
/* When this routine is called, all of the subsections are still intact
so we walk over subsections instead of sections. */
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
bfd_boolean prev_frag_can_negate_branch = FALSE;
fragS *fragP;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
{
if (next_frag_is_loop_target (fragP))
{
if (prev_frag_can_negate_branch)
fragP->fr_subtype = RELAX_LOOP_END;
else
{
if (!align_only_targets ||
next_frag_is_branch_target (fragP))
fragP->fr_subtype = RELAX_DESIRE_ALIGN;
else
frag_wane (fragP);
}
}
else if (!align_only_targets
|| next_frag_is_branch_target (fragP))
fragP->fr_subtype = RELAX_DESIRE_ALIGN;
else
frag_wane (fragP);
}
if (fragP->fr_fix != 0)
prev_frag_can_negate_branch = FALSE;
if (frag_can_negate_branch (fragP))
prev_frag_can_negate_branch = TRUE;
}
}
}
static bfd_boolean
frag_can_negate_branch (fragP)
fragS *fragP;
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_IMMED)
{
TInsn t_insn;
tinsn_from_chars (&t_insn, fragP->fr_opcode);
if (is_negatable_branch (&t_insn))
return TRUE;
}
return FALSE;
}
/* Re-process all of the fragments looking to convert all of the
RELAX_ADD_NOP_IF_A0_B_RETW. If the next instruction is a
conditional branch or a retw/retw.n, convert this frag to one that
will generate a NOP. In any case close it off with a .fill 0. */
static void
xtensa_fix_a0_b_retw_frags ()
{
frchainS *frchP;
/* When this routine is called, all of the subsections are still intact
so we walk over subsections instead of sections. */
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
{
if (next_instrs_are_b_retw (fragP))
relax_frag_add_nop (fragP);
else
frag_wane (fragP);
}
}
}
}
bfd_boolean
next_instrs_are_b_retw (fragP)
fragS * fragP;
{
xtensa_opcode opcode;
const fragS *next_fragP = next_non_empty_frag (fragP);
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
int offset = 0;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
if (next_fragP == NULL)
return FALSE;
/* Check for the conditional branch. */
xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
opcode = xtensa_decode_insn (isa, insnbuf);
if (!is_conditional_branch_opcode (opcode))
return FALSE;
offset += xtensa_insn_length (isa, opcode);
if (offset == next_fragP->fr_fix)
{
next_fragP = next_non_empty_frag (next_fragP);
offset = 0;
}
if (next_fragP == NULL)
return FALSE;
/* Check for the retw/retw.n. */
xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
opcode = xtensa_decode_insn (isa, insnbuf);
if (is_windowed_return_opcode (opcode))
return TRUE;
return FALSE;
}
/* Re-process all of the fragments looking to convert all of the
RELAX_ADD_NOP_IF_PRE_LOOP_END. If there is one instruction and a
loop end label, convert this frag to one that will generate a NOP.
In any case close it off with a .fill 0. */
static void
xtensa_fix_b_j_loop_end_frags ()
{
frchainS *frchP;
/* When this routine is called, all of the subsections are still intact
so we walk over subsections instead of sections. */
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
{
if (next_instr_is_loop_end (fragP))
relax_frag_add_nop (fragP);
else
frag_wane (fragP);
}
}
}
}
bfd_boolean
next_instr_is_loop_end (fragP)
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 the size is >= 3 then there is more than one instruction here.
The hardware bug will not fire. */
if (next_fragP->fr_fix > 3)
return FALSE;
return TRUE;
}
/* Re-process all of the fragments looking to convert all of the
RELAX_ADD_NOP_IF_CLOSE_LOOP_END. If there is an loop end that is
not MY loop's loop end within 12 bytes, add enough nops here to
make it at least 12 bytes away. In any case close it off with a
.fill 0. */
static void
xtensa_fix_close_loop_end_frags ()
{
frchainS *frchP;
/* When this routine is called, all of the subsections are still intact
so we walk over subsections instead of sections. */
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
fragS *current_target = NULL;
offsetT current_offset = 0;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_IMMED)
{
/* Read it. If the instruction is a loop, get the target. */
xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode);
if (is_loop_opcode (opcode))
{
TInsn t_insn;
tinsn_from_chars (&t_insn, fragP->fr_opcode);
tinsn_immed_from_frag (&t_insn, fragP);
/* Get the current fragment target. */
if (fragP->fr_symbol)
{
current_target = symbol_get_frag (fragP->fr_symbol);
current_offset = fragP->fr_offset;
}
}
}
if (current_target
&& fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_CLOSE_LOOP_END)
{
size_t min_bytes;
size_t bytes_added = 0;
#define REQUIRED_LOOP_DIVIDING_BYTES 12
/* Max out at 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)
{
while (min_bytes + bytes_added
< REQUIRED_LOOP_DIVIDING_BYTES)
{
int length = 3;
if (fragP->fr_var < length)
as_warn (_("fr_var %lu < length %d; ignoring"),
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);
}
}
}
}
size_t
min_bytes_to_other_loop_end (fragP, current_target, current_offset, max_size)
fragS *fragP;
fragS *current_target;
offsetT current_offset;
size_t max_size;
{
size_t 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;
}
size_t
unrelaxed_frag_min_size (fragP)
fragS * fragP;
{
size_t size = fragP->fr_fix;
/* add fill size */
if (fragP->fr_type == rs_fill)
size += fragP->fr_offset;
return size;
}
/* Re-process all of the fragments looking to convert all
of the RELAX_ADD_NOP_IF_SHORT_LOOP. If:
A)
1) the instruction size count to the loop end label
is too short (<= 2 instructions),
2) loop has a jump or branch in it
or B)
1) software_avoid_all_short_loops is true
2) The generating loop was a 'loopgtz' or 'loopnez'
3) the instruction size count to the loop end label is too short
(<= 2 instructions)
then convert this frag (and maybe the next one) to generate a NOP.
In any case close it off with a .fill 0. */
static void
xtensa_fix_short_loop_frags ()
{
frchainS *frchP;
/* When this routine is called, all of the subsections are still intact
so we walk over subsections instead of sections. */
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;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
/* check on the current loop */
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_IMMED)
{
/* Read it. If the instruction is a loop, get the target. */
xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode);
if (is_loop_opcode (opcode))
{
TInsn t_insn;
tinsn_from_chars (&t_insn, fragP->fr_opcode);
tinsn_immed_from_frag (&t_insn, fragP);
/* Get the current fragment target. */
if (fragP->fr_symbol)
{
current_target = symbol_get_frag (fragP->fr_symbol);
current_offset = fragP->fr_offset;
current_opcode = opcode;
}
}
}
if (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_SHORT_LOOP)
{
size_t insn_count =
count_insns_to_loop_end (fragP->fr_next, TRUE, 3);
if (insn_count < 3
&& (branch_before_loop_end (fragP->fr_next)
|| (software_avoid_all_short_loops
&& current_opcode != XTENSA_UNDEFINED
&& !is_the_loop_opcode (current_opcode))))
relax_frag_add_nop (fragP);
else
frag_wane (fragP);
}
}
}
}
size_t
count_insns_to_loop_end (base_fragP, count_relax_add, max_count)
fragS *base_fragP;
bfd_boolean count_relax_add;
size_t max_count;
{
fragS *fragP = NULL;
size_t 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)
{
/* In order to add the appropriate number of
NOPs, we count an instruction for downstream
occurrences. */
insn_count++;
if (insn_count >= max_count)
return max_count;
}
}
}
return insn_count;
}
size_t
unrelaxed_frag_min_insn_count (fragP)
fragS *fragP;
{
size_t insn_count = 0;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return insn_count;
/* Decode the fixed instructions. */
while (offset < fragP->fr_fix)
{
xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
if (opcode == XTENSA_UNDEFINED)
{
as_fatal (_("undecodable instruction in instruction frag"));
return insn_count;
}
offset += xtensa_insn_length (xtensa_default_isa, opcode);
insn_count++;
}
return insn_count;
}
bfd_boolean
branch_before_loop_end (base_fragP)
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;
}
bfd_boolean
unrelaxed_frag_has_b_j (fragP)
fragS *fragP;
{
size_t insn_count = 0;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return FALSE;
/* Decode the fixed instructions. */
while (offset < fragP->fr_fix)
{
xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
if (opcode == XTENSA_UNDEFINED)
{
as_fatal (_("undecodable instruction in instruction frag"));
return insn_count;
}
if (is_branch_or_jump_opcode (opcode))
return TRUE;
offset += xtensa_insn_length (xtensa_default_isa, opcode);
}
return FALSE;
}
/* Checks to be made after initial assembly but before relaxation. */
static void
xtensa_sanity_check ()
{
char *file_name;
int line;
frchainS *frchP;
as_where (&file_name, &line);
for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
fragS *fragP;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
/* Currently we only check for empty loops here. */
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);
tinsn_immed_from_frag (&t_insn, fragP);
if (is_loop_opcode (t_insn.opcode))
{
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
/* Return true if the loop target is the next non-zero fragment. */
bfd_boolean
is_empty_loop (insn, fragP)
const TInsn *insn;
fragS *fragP;
{
const expressionS *expr;
symbolS *symbolP;
fragS *next_fragP;
if (insn->insn_type != ITYPE_INSN)
return FALSE;
if (!is_loop_opcode (insn->opcode))
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;
/* Walk through the zero-size fragments from this one. If we find
the target fragment, then this is a zero-size loop. */
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;
}
bfd_boolean
is_local_forward_loop (insn, fragP)
const TInsn *insn;
fragS *fragP;
{
const expressionS *expr;
symbolS *symbolP;
fragS *next_fragP;
if (insn->insn_type != ITYPE_INSN)
return FALSE;
if (!is_loop_opcode (insn->opcode))
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;
/* Walk through fragments until we find the target.
If we do not find the target, then this is an invalid loop. */
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;
}
/* Alignment Functions. */
size_t
get_text_align_power (target_size)
int target_size;
{
size_t i = 0;
for (i = 0; i < sizeof (size_t); i++)
{
if (target_size <= (1 << i))
return i;
}
as_fatal (_("get_text_align_power: argument too large"));
return 0;
}
addressT
get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
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);
}
/* get_text_align_fill_size ()
Desired alignments:
give the address
target_size = size of next instruction
align_pow = get_text_align_power (target_size).
use_nops = 0
use_no_density = 0;
Loop alignments:
address = current address + loop instruction size;
target_size = 3 (for 2 or 3 byte target)
= 8 (for 8 byte target)
align_pow = get_text_align_power (target_size);
use_nops = 1
use_no_density = set appropriately
Text alignments:
address = current address + loop instruction size;
target_size = 0
align_pow = get_text_align_power (target_size);
use_nops = 0
use_no_density = 0. */
addressT
get_text_align_fill_size (address, align_pow, target_size,
use_nops, use_no_density)
addressT address;
int align_pow;
int target_size;
bfd_boolean use_nops;
bfd_boolean use_no_density;
{
/* Input arguments:
align_pow: log2 (required alignment).
target_size: alignment must allow the new_address and
new_address+target_size-1.
use_nops: if true, then we can only use 2 or 3 byte nops.
use_no_density: if use_nops and use_no_density, we can only use
3-byte nops.
Usually, for non-zero target_size, the align_pow is the power of 2
that is greater than or equal to the target_size. This handles the
2-byte, 3-byte and 8-byte instructions. */
size_t alignment = (1 << align_pow);
if (!use_nops)
{
/* This is the easy case. */
size_t mod;
mod = address % alignment;
if (mod != 0)
mod = alignment - mod;
assert ((address + mod) % alignment == 0);
return mod;
}
/* This is the slightly harder case. */
assert ((int) alignment >= target_size);
assert (target_size > 0);
if (!use_no_density)
{
size_t i;
for (i = 0; i < alignment * 2; i++)
{
if (i == 1)
continue;
if ((address + i) >> align_pow ==
(address + i + target_size - 1) >> align_pow)
return i;
}
}
else
{
size_t i;
/* Can only fill multiples of 3. */
for (i = 0; i <= alignment * 3; i += 3)
{
if ((address + i) >> align_pow ==
(address + i + target_size - 1) >> align_pow)
return i;
}
}
assert (0);
return 0;
}
/* This will assert if it is not possible. */
size_t
get_text_align_nop_count (fill_size, use_no_density)
size_t fill_size;
bfd_boolean use_no_density;
{
size_t count = 0;
if (use_no_density)
{
assert (fill_size % 3 == 0);
return (fill_size / 3);
}
assert (fill_size != 1); /* Bad argument. */
while (fill_size > 1)
{
size_t insn_size = 3;
if (fill_size == 2 || fill_size == 4)
insn_size = 2;
fill_size -= insn_size;
count++;
}
assert (fill_size != 1); /* Bad algorithm. */
return count;
}
size_t
get_text_align_nth_nop_size (fill_size, n, use_no_density)
size_t fill_size;
size_t n;
bfd_boolean use_no_density;
{
size_t count = 0;
assert (get_text_align_nop_count (fill_size, use_no_density) > n);
if (use_no_density)
return 3;
while (fill_size > 1)
{
size_t 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;
}
/* For the given fragment, find the appropriate address
for it to begin at if we are using NOPs to align it. */
static addressT
get_noop_aligned_address (fragP, address)
fragS *fragP;
addressT address;
{
static xtensa_insnbuf insnbuf = NULL;
size_t fill_size = 0;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
switch (fragP->fr_type)
{
case rs_machine_dependent:
if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
{
/* The rule is: get next fragment's FIRST instruction. Find
the smallest number of bytes that need to be added to
ensure that the next fragment's FIRST instruction will fit
in a single word.
E.G., 2 bytes : 0, 1, 2 mod 4
3 bytes: 0, 1 mod 4
If the FIRST instruction MIGHT be relaxed,
assume that it will become a 3 byte instruction. */
int target_insn_size;
xtensa_opcode opcode = next_frag_opcode (fragP);
addressT pre_opcode_bytes;
if (opcode == XTENSA_UNDEFINED)
{
as_bad_where (fragP->fr_file, fragP->fr_line,
_("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
as_fatal (_("cannot continue"));
}
target_insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
if (is_loop_opcode (opcode))
{
/* next_fragP should be the loop. */
const fragS *next_fragP = next_non_empty_frag (fragP);
xtensa_opcode next_opcode = next_frag_opcode (next_fragP);
size_t alignment;
pre_opcode_bytes += target_insn_size;
/* For loops, the alignment depends on the size of the
instruction following the loop, not the loop instruction. */
if (next_opcode == XTENSA_UNDEFINED)
target_insn_size = 3;
else
{
target_insn_size =
xtensa_insn_length (xtensa_default_isa, next_opcode);
if (target_insn_size == 2)
target_insn_size = 3; /* ISA specifies this. */
}
/* If it was 8, then we'll need a larger alignment
for the section. */
alignment = get_text_align_power (target_insn_size);
/* Is Now_seg valid */
record_alignment (now_seg, alignment);
}
else
as_fatal (_("expected loop opcode in relax align next target"));
fill_size = get_text_align_fill_size
(address + pre_opcode_bytes,
get_text_align_power (target_insn_size),
target_insn_size, TRUE, fragP->tc_frag_data.is_no_density);
}
break;
#if 0
case rs_align:
case rs_align_code:
fill_size = get_text_align_fill_size
(address, fragP->fr_offset, 1, TRUE,
fragP->tc_frag_data.is_no_density);
break;
#endif
default:
as_fatal (_("expected align_code or RELAX_ALIGN_NEXT_OPCODE"));
}
return address + fill_size;
}
/* 3 mechanisms for relaxing an alignment:
Align to a power of 2.
Align so the next fragment's instruction does not cross a word boundary.
Align the current instruction so that if the next instruction
were 3 bytes, it would not cross a word boundary.
We can align with:
zeros - This is easy; always insert zeros.
nops - 3 and 2 byte instructions
2 - 2 byte nop
3 - 3 byte nop
4 - 2, 2-byte nops
>=5 : 3 byte instruction + fn(n-3)
widening - widen previous instructions. */
static addressT
get_widen_aligned_address (fragP, address)
fragS *fragP;
addressT address;
{
addressT align_pow, new_address, loop_insn_offset;
fragS *next_frag;
int insn_size;
xtensa_opcode opcode, next_opcode;
static xtensa_insnbuf insnbuf = NULL;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
if (fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
{
align_pow = fragP->fr_offset;
new_address = ((address + ((1 << align_pow) - 1))
<< align_pow) >> align_pow;
return new_address;
}
if (fragP->fr_type == rs_machine_dependent)
{
switch (fragP->fr_subtype)
{
case RELAX_DESIRE_ALIGN:
/* The rule is: get the next fragment's FIRST instruction.
Find the smallest number of bytes needed to be added
in order to ensure that the next fragment is FIRST
instruction will fit in a single word.
i.e. 2 bytes : 0, 1, 2. mod 4
3 bytes: 0, 1 mod 4
If the FIRST instruction MIGHT be relaxed,
assume that it will become a 3-byte instruction. */
insn_size = 3;
/* Check to see if it might be 2 bytes. */
next_opcode = next_frag_opcode (fragP);
if (next_opcode != XTENSA_UNDEFINED
&& xtensa_insn_length (xtensa_default_isa, next_opcode) == 2)
insn_size = 2;
assert (insn_size <= 4);
for (new_address = address; new_address < address + 4; new_address++)
{
if (new_address >> 2 == (new_address + insn_size - 1) >> 2)
return new_address;
}
as_bad (_("internal error aligning"));
return address;
case RELAX_ALIGN_NEXT_OPCODE:
/* The rule is: get next fragment's FIRST instruction.
Find the smallest number of bytes needed to be added
in order to ensure that the next fragment's FIRST
instruction will fit in a single word.
i.e. 2 bytes : 0, 1, 2. mod 4
3 bytes: 0, 1 mod 4
If the FIRST instruction MIGHT be relaxed,
assume that it will become a 3 byte instruction. */
opcode = next_frag_opcode (fragP);
if (opcode == XTENSA_UNDEFINED)
{
as_bad_where (fragP->fr_file, fragP->fr_line,
_("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
as_fatal (_("cannot continue"));
}
insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
assert (insn_size <= 4);
assert (is_loop_opcode (opcode));
loop_insn_offset = 0;
next_frag = next_non_empty_frag (fragP);
/* If the loop has been expanded then the loop
instruction could be at an offset from this fragment. */
if (next_frag->fr_subtype != RELAX_IMMED)
loop_insn_offset = get_expanded_loop_offset (opcode);
for (new_address = address; new_address < address + 4; new_address++)
{
if ((new_address + loop_insn_offset + insn_size) >> 2 ==
(new_address + loop_insn_offset + insn_size + 2) >> 2)
return new_address;
}
as_bad (_("internal error aligning"));
return address;
default:
as_bad (_("internal error aligning"));
return address;
}
}
as_bad (_("internal error aligning"));
return address;
}
/* md_relax_frag Hook and Helper Functions. */
/* Return the number of bytes added to this fragment, given that the
input has been stretched already by "stretch". */
long
xtensa_relax_frag (fragP, stretch, stretched_p)
fragS *fragP;
long stretch;
int *stretched_p;
{
int unreported = fragP->tc_frag_data.unreported_expansion;
long new_stretch = 0;
char *file_name;
int line, lit_size;
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:
/* Always convert. */
new_stretch = relax_frag_text_align (fragP, stretch);
break;
case RELAX_LOOP_END:
/* Do nothing. */
break;
case RELAX_LOOP_END_ADD_NOP:
/* Add a NOP and switch to .fill 0. */
new_stretch = relax_frag_add_nop (fragP);
break;
case RELAX_DESIRE_ALIGN:
/* We REALLY want to change the relaxation order here. This
should do NOTHING. The narrowing before it will either align
it or not. */
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_NARROW:
new_stretch = relax_frag_narrow (fragP, stretch);
break;
case RELAX_IMMED:
case RELAX_IMMED_STEP1:
case RELAX_IMMED_STEP2:
/* Place the immediate. */
new_stretch = relax_frag_immed (now_seg, fragP, stretch,
fragP->fr_subtype - RELAX_IMMED,
stretched_p);
break;
case RELAX_LITERAL_POOL_BEGIN:
case RELAX_LITERAL_POOL_END:
/* No relaxation required. */
break;
default:
as_bad (_("bad relaxation state"));
}
new_logical_line (file_name, line);
return new_stretch;
}
static long
relax_frag_text_align (fragP, stretch)
fragS *fragP;
long stretch;
{
addressT old_address, old_next_address, old_size;
addressT new_address, new_next_address, new_size;
addressT growth;
/* Overview of the relaxation procedure for alignment
inside an executable section:
The old size is stored in the tc_frag_data.text_expansion field.
Calculate the new address, fix up the text_expansion and
return the growth. */
/* Calculate the old address of this fragment and the next fragment. */
old_address = fragP->fr_address - stretch;
old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
fragP->tc_frag_data.text_expansion);
old_size = old_next_address - old_address;
/* Calculate the new address of this fragment and the next fragment. */
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;
/* Fix up the text_expansion field and return the new growth. */
fragP->tc_frag_data.text_expansion += growth;
return growth;
}
/* Add a NOP (i.e., "or a1, a1, a1"). Use the 3-byte one because we
don't know about the availability of density yet. TODO: When the
flags are stored per fragment, use NOP.N when possible. */
static long
relax_frag_add_nop (fragP)
fragS *fragP;
{
static xtensa_insnbuf insnbuf = NULL;
TInsn t_insn;
char *nop_buf = fragP->fr_literal + fragP->fr_fix;
int length;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_init (&t_insn);
t_insn.opcode = xtensa_or_opcode;
assert (t_insn.opcode != XTENSA_UNDEFINED);
t_insn.ntok = 3;
set_expr_const (&t_insn.tok[0], 1);
set_expr_const (&t_insn.tok[1], 1);
set_expr_const (&t_insn.tok[2], 1);
tinsn_to_insnbuf (&t_insn, insnbuf);
fragP->tc_frag_data.is_insn = TRUE;
xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, nop_buf);
length = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
if (fragP->fr_var < length)
{
as_warn (_("fr_var (%ld) < length (%d); ignoring"),
fragP->fr_var, length);
frag_wane (fragP);
return 0;
}
fragP->fr_fix += length;
fragP->fr_var -= length;
frag_wane (fragP);
return length;
}
static long
relax_frag_narrow (fragP, stretch)
fragS *fragP;
long stretch;
{
/* Overview of the relaxation procedure for alignment inside an
executable section: Find the number of widenings required and the
number of nop bytes required. Store the number of bytes ALREADY
widened. If there are enough instructions to widen (must go back
ONLY through NARROW fragments), mark each of the fragments as TO BE
widened, recalculate the fragment addresses. */
assert (fragP->fr_type == rs_machine_dependent
&& fragP->fr_subtype == RELAX_NARROW);
if (!future_alignment_required (fragP, 0))
{
/* If already expanded but no longer needed because of a prior
stretch, it is SAFE to unexpand because the next fragment will
NEVER start at an address > the previous time through the
relaxation. */
if (fragP->tc_frag_data.text_expansion)
{
if (stretch > 0)
{
fragP->tc_frag_data.text_expansion = 0;
return -1;
}
/* Otherwise we have to live with this bad choice. */
return 0;
}
return 0;
}
if (fragP->tc_frag_data.text_expansion == 0)
{
fragP->tc_frag_data.text_expansion = 1;
return 1;
}
return 0;
}
static bfd_boolean
future_alignment_required (fragP, stretch)
fragS *fragP;
long stretch;
{
long address = fragP->fr_address + stretch;
int num_widens = 0;
addressT aligned_address;
offsetT desired_diff;
while (fragP)
{
/* Limit this to a small search. */
if (num_widens > 8)
return FALSE;
address += fragP->fr_fix;
switch (fragP->fr_type)
{
case rs_fill:
address += fragP->fr_offset * fragP->fr_var;
break;
case rs_machine_dependent:
switch (fragP->fr_subtype)
{
case RELAX_NARROW:
/* address += fragP->fr_fix; */
num_widens++;
break;
case RELAX_IMMED:
address += (/* fragP->fr_fix + */
fragP->tc_frag_data.text_expansion);
break;
case RELAX_ALIGN_NEXT_OPCODE:
case RELAX_DESIRE_ALIGN:
/* address += fragP->fr_fix; */
aligned_address = get_widen_aligned_address (fragP, address);
desired_diff = aligned_address - address;
assert (desired_diff >= 0);
/* If there are enough wideners in between do it. */
/* return (num_widens == desired_diff); */
if (num_widens == desired_diff)
return TRUE;
if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
return FALSE;
break;
default:
return FALSE;
}
break;
default:
return FALSE;
}
fragP = fragP->fr_next;
}
return FALSE;
}
static long
relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
segT segP;
fragS *fragP;
long stretch;
int min_steps;
int *stretched_p;
{
static xtensa_insnbuf insnbuf = NULL;
TInsn t_insn;
int old_size;
bfd_boolean negatable_branch = FALSE;
bfd_boolean branch_jmp_to_next = FALSE;
IStack istack;
offsetT frag_offset;
int num_steps;
fragS *lit_fragP;
int num_text_bytes, num_literal_bytes;
int literal_diff, text_diff;
assert (fragP->fr_opcode != NULL);
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_from_chars (&t_insn, fragP->fr_opcode);
tinsn_immed_from_frag (&t_insn, fragP);
negatable_branch = is_negatable_branch (&t_insn);
old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
if (software_avoid_b_j_loop_end)
branch_jmp_to_next = is_branch_jmp_to_next (&t_insn, fragP);
/* Special case: replace a branch to the next instruction with a NOP.
This is required to work around a hardware bug in T1040.0 and also
serves as an optimization. */
if (branch_jmp_to_next
&& ((old_size == 2) || (old_size == 3))
&& !next_frag_is_loop_target (fragP))
return 0;
/* Here is the fun stuff: Get the immediate field from this
instruction. If it fits, we are done. If not, find the next
instruction sequence that fits. */
frag_offset = fragP->fr_opcode - fragP->fr_literal;
istack_init (&istack);
num_steps = xg_assembly_relax (&istack, &t_insn, 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->fr_subtype = (int) RELAX_IMMED + num_steps;
/* Figure out the number of bytes needed. */
lit_fragP = 0;
num_text_bytes = get_num_stack_text_bytes (&istack) - old_size;
num_literal_bytes = get_num_stack_literal_bytes (&istack);
literal_diff = num_literal_bytes - fragP->tc_frag_data.literal_expansion;
text_diff = num_text_bytes - fragP->tc_frag_data.text_expansion;
/* It MUST get larger. If not, we could get an infinite loop. */
know (num_text_bytes >= 0);
know (literal_diff >= 0 && text_diff >= 0);
fragP->tc_frag_data.text_expansion = num_text_bytes;
fragP->tc_frag_data.literal_expansion = num_literal_bytes;
/* Find the associated expandable literal for this. */
if (literal_diff != 0)
{
lit_fragP = fragP->tc_frag_data.literal_frag;
if (lit_fragP)
{
assert (literal_diff == 4);
lit_fragP->tc_frag_data.unreported_expansion += literal_diff;
/* We expect that the literal section state has NOT been
modified yet. */
assert (lit_fragP->fr_type == rs_machine_dependent
&& lit_fragP->fr_subtype == RELAX_LITERAL);
lit_fragP->fr_subtype = RELAX_LITERAL_NR;
/* We need to mark this section for another iteration
of relaxation. */
(*stretched_p)++;
}
}
/* This implicitly uses the assumption that a branch is negated
when the size of the output increases by at least 2 bytes. */
if (negatable_branch && num_text_bytes >= 2)
{
/* If next frag is a loop end, then switch it to add a NOP. */
update_next_frag_nop_state (fragP);
}
return text_diff;
}
/* md_convert_frag Hook and Helper Functions. */
void
md_convert_frag (abfd, sec, fragp)
bfd *abfd ATTRIBUTE_UNUSED;
segT sec;
fragS *fragp;
{
char *file_name;
int line;
as_where (&file_name, &line);
new_logical_line (fragp->fr_file, fragp->fr_line);
switch (fragp->fr_subtype)
{
case RELAX_ALIGN_NEXT_OPCODE:
/* Always convert. */
convert_frag_align_next_opcode (fragp);
break;
case RELAX_DESIRE_ALIGN:
/* Do nothing. If not aligned already, too bad. */
break;
case RELAX_LITERAL:
case RELAX_LITERAL_FINAL:
break;
case RELAX_NARROW:
/* No conversion. */
convert_frag_narrow (fragp);
break;
case RELAX_IMMED:
case RELAX_IMMED_STEP1:
case RELAX_IMMED_STEP2:
/* Place the immediate. */
convert_frag_immed (sec, fragp, fragp->fr_subtype - RELAX_IMMED);
break;
case RELAX_LITERAL_NR:
if (use_literal_section)
{
/* This should have been handled during relaxation. When
relaxing a code segment, literals sometimes need to be
added to the corresponding literal segment. If that
literal segment has already been relaxed, then we end up
in this situation. Marking the literal segments as data
would make this happen less often (since GAS always relaxes
code before data), but we could still get into trouble if
there are instructions in a segment that is not marked as
containing code. Until we can implement a better solution,
cheat and adjust the addresses of all the following frags.
This could break subsequent alignments, but the linker's
literal coalescing will do that anyway. */
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);
}
void
convert_frag_align_next_opcode (fragp)
fragS *fragp;
{
char *nop_buf; /* Location for Writing. */
size_t i;
bfd_boolean use_no_density = fragp->tc_frag_data.is_no_density;
addressT aligned_address;
size_t fill_size, 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 (i = 0; i < nop_count; i++)
{
size_t nop_size;
nop_size = get_text_align_nth_nop_size (fill_size, i, 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 (fragP)
fragS *fragP;
{
static xtensa_insnbuf insnbuf = NULL;
TInsn t_insn, single_target;
int size, old_size, diff, error_val;
offsetT frag_offset;
if (fragP->tc_frag_data.text_expansion == 0)
{
/* No conversion. */
fragP->fr_var = 0;
return;
}
assert (fragP->fr_opcode != NULL);
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_from_chars (&t_insn, fragP->fr_opcode);
tinsn_immed_from_frag (&t_insn, fragP);
/* Just convert it to a wide form.... */
size = 0;
old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
tinsn_init (&single_target);
frag_offset = fragP->fr_opcode - fragP->fr_literal;
error_val = xg_expand_narrow (&single_target, &t_insn);
if (error_val)
as_bad (_("unable to widen instruction"));
size = xtensa_insn_length (xtensa_default_isa, single_target.opcode);
xg_emit_insn_to_buf (&single_target, 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;
/* clean it up */
fragP->fr_var = 0;
}
static void
convert_frag_immed (segP, fragP, min_steps)
segT segP;
fragS *fragP;
int min_steps;
{
char *immed_instr = fragP->fr_opcode;
static xtensa_insnbuf insnbuf = NULL;
TInsn orig_t_insn;
bfd_boolean expanded = FALSE;
char *fr_opcode = fragP->fr_opcode;
bfd_boolean branch_jmp_to_next = FALSE;
int size;
assert (fragP->fr_opcode != NULL);
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
tinsn_from_chars (&orig_t_insn, fragP->fr_opcode);
tinsn_immed_from_frag (&orig_t_insn, fragP);
/* Here is the fun stuff: Get the immediate field from this
instruction. If it fits, we're done. If not, find the next
instruction sequence that fits. */
if (software_avoid_b_j_loop_end)
branch_jmp_to_next = is_branch_jmp_to_next (&orig_t_insn, fragP);
if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
{
/* Conversion just inserts a NOP and marks the fix as completed. */
size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
assemble_nop (size, fragP->fr_opcode);
fragP->fr_var = 0;
}
else
{
IStack istack;
int i;
symbolS *lit_sym = NULL;
int total_size = 0;
int old_size;
int diff;
symbolS *gen_label = NULL;
offsetT frag_offset;
/* It does not fit. Find something that does and
convert immediately. */
frag_offset = fragP->fr_opcode - fragP->fr_literal;
istack_init (&istack);
xg_assembly_relax (&istack, &orig_t_insn,
segP, fragP, frag_offset, min_steps, 0);
old_size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
/* Assemble this right inline. */
/* First, create the mapping from a label name to the REAL label. */
total_size = 0;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *t_insn = &istack.insn[i];
int size = 0;
fragS *lit_frag;
switch (t_insn->insn_type)
{
case ITYPE_LITERAL:
if (lit_sym != NULL)
as_bad (_("multiple literals in expansion"));
/* First find the appropriate space in the literal pool. */
lit_frag = fragP->tc_frag_data.literal_frag;
if (lit_frag == NULL)
as_bad (_("no registered fragment for literal"));
if (t_insn->ntok != 1)
as_bad (_("number of literal tokens != 1"));
/* Set the literal symbol and add a fixup. */
lit_sym = lit_frag->fr_symbol;
break;
case ITYPE_LABEL:
assert (gen_label == NULL);
gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
fragP->fr_opcode - fragP->fr_literal +
total_size, fragP);
break;
case ITYPE_INSN:
size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
total_size += size;
break;
}
}
total_size = 0;
for (i = 0; i < istack.ninsn; i++)
{
TInsn *t_insn = &istack.insn[i];
fragS *lit_frag;
int size;
segT target_seg;
switch (t_insn->insn_type)
{
case ITYPE_LITERAL:
lit_frag = fragP->tc_frag_data.literal_frag;
/* already checked */
assert (lit_frag != NULL);
assert (lit_sym != NULL);
assert (t_insn->ntok == 1);
/* add a fixup */
target_seg = S_GET_SEGMENT (lit_sym);
assert (target_seg);
fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
&t_insn->tok[0], FALSE, BFD_RELOC_32);
break;
case ITYPE_LABEL:
break;
case ITYPE_INSN:
xg_resolve_labels (t_insn, gen_label);
xg_resolve_literals (t_insn, lit_sym);
size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
total_size += size;
xg_emit_insn_to_buf (t_insn, immed_instr, fragP,
immed_instr - fragP->fr_literal, TRUE);
immed_instr += 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;
}
/* Clean it up. */
fragP->fr_var = 0;
/* Check for undefined immediates in LOOP instructions. */
if (is_loop_opcode (orig_t_insn.opcode))
{
symbolS *sym;
sym = orig_t_insn.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_t_insn.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 && is_loop_opcode (orig_t_insn.opcode))
convert_frag_immed_finish_loop (segP, fragP, &orig_t_insn);
if (expanded && is_direct_call_opcode (orig_t_insn.opcode))
{
/* Add an expansion note on the expanded instruction. */
fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
&orig_t_insn.tok[0], TRUE,
BFD_RELOC_XTENSA_ASM_EXPAND);
}
}
/* Add a new fix expression into the desired segment. We have to
switch to that segment to do this. */
static fixS *
fix_new_exp_in_seg (new_seg, new_subseg,
frag, where, size, exp, pcrel, r_type)
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);
if (r_type == BFD_RELOC_32
&& exp->X_add_symbol
&& symbol_get_tc (exp->X_add_symbol)->plt == 1)
{
r_type = BFD_RELOC_XTENSA_PLT;
}
new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
subseg_set (seg, subseg);
return new_fix;
}
/* Relax a loop instruction so that it can span loop >256 bytes. */
/*
loop as, .L1
.L0:
rsr as, LEND
wsr as, LBEG
addi as, as, lo8(label-.L1)
addmi as, as, mid8(label-.L1)
wsr as, LEND
isync
rsr as, LCOUNT
addi as, as, 1
.L1:
<<body>>
label: */
static void
convert_frag_immed_finish_loop (segP, fragP, t_insn)
segT segP;
fragS *fragP;
TInsn *t_insn;
{
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;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
/* Get the loop offset. */
loop_offset = get_expanded_loop_offset (t_insn->opcode);
/* Validate that there really is a LOOP at the loop_offset. */
tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset);
if (!is_loop_opcode (loop_insn.opcode))
{
as_bad_where (fragP->fr_file, fragP->fr_line,
_("loop relaxation specification does not correspond"));
assert (0);
}
addi_offset += loop_offset;
addmi_offset += loop_offset;
assert (t_insn->ntok == 2);
target = get_expression_value (segP, &t_insn->tok[1]);
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;
}
/* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
32512. If the loop is larger than that, then we just fail. */
if (loop_length_hi > 32512)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("loop too long for LOOP instruction"));
tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset);
assert (addi_insn.opcode == xtensa_addi_opcode);
tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset);
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, fragP->fr_opcode + addi_offset);
set_expr_const (&addmi_insn.tok[2], loop_length_hi);
tinsn_to_insnbuf (&addmi_insn, insnbuf);
xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addmi_offset);
}
static offsetT
get_expression_value (segP, exp)
segT segP;
expressionS *exp;
{
if (exp->X_op == O_constant)
return exp->X_add_number;
if (exp->X_op == O_symbol)
{
/* Find the fragment. */
symbolS *sym = exp->X_add_symbol;
assert (S_GET_SEGMENT (sym) == segP
|| S_GET_SEGMENT (sym) == absolute_section);
return (S_GET_VALUE (sym) + exp->X_add_number);
}
as_bad (_("invalid expression evaluation type %d"), exp->X_op);
return 0;
}
/* A map that keeps information on a per-subsegment basis. This is
maintained during initial assembly, but is invalid once the
subsegments are smashed together. I.E., it cannot be used during
the relaxation. */
typedef struct subseg_map_struct
{
/* the key */
segT seg;
subsegT subseg;
/* the data */
unsigned flags;
struct subseg_map_struct *next;
} subseg_map;
static subseg_map *sseg_map = NULL;
static unsigned
get_last_insn_flags (seg, subseg)
segT seg;
subsegT subseg;
{
subseg_map *subseg_e;
for (subseg_e = sseg_map; subseg_e != NULL; subseg_e = subseg_e->next)
if (seg == subseg_e->seg && subseg == subseg_e->subseg)
return subseg_e->flags;
return 0;
}
static void
set_last_insn_flags (seg, subseg, fl, val)
segT seg;
subsegT subseg;
unsigned fl;
bfd_boolean val;
{
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;
if (!subseg_e)
{
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->next = sseg_map;
sseg_map = subseg_e;
}
if (val)
subseg_e->flags |= fl;
else
subseg_e->flags &= ~fl;
}
/* Segment Lists and emit_state Stuff. */
/* Remove the segment from the global sections list. */
static void
xtensa_remove_section (sec)
segT sec;
{
/* Handle brain-dead bfd_section_list_remove macro, which
expect the address of the prior section's "next" field, not
just the address of the section to remove. */
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 (after_sec, sec)
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 (head)
seg_list *head;
{
head = head->next;
while (head)
{
segT literal_section = head->seg;
/* Move the literal section to the front of the section list. */
assert (literal_section);
xtensa_remove_section (literal_section);
xtensa_insert_section (NULL, literal_section);
head = head->next;
}
}
void
xtensa_move_literals ()
{
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);
/* Make sure that all the frags in this series are closed, and
that there is at least one left over of zero-size. This
prevents us from making a segment with an frchain without any
frags in it. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
last_frag = frag_now;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
while (search_frag != frag_now)
{
next_frag = search_frag->fr_next;
/* First, move the frag out of the literal section and
to the appropriate place. */
if (search_frag->tc_frag_data.literal_frag)
{
literal_pool = search_frag->tc_frag_data.literal_frag;
assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
/* Note that we set this fr_var to be a fix
chain when we created the literal pool location
as RELAX_LITERAL_POOL_BEGIN. */
frchain_to = (frchainS *) literal_pool->fr_var;
}
insert_after = literal_pool;
while (insert_after->fr_next->fr_subtype != RELAX_LITERAL_POOL_END)
insert_after = insert_after->fr_next;
dest_seg = (segT) insert_after->fr_next->fr_var;
*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;
/* Now move any fixups associated with this frag to the
right section. */
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;
}
/* Now fix up the SEGMENT value for all the literal symbols. */
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;
S_SET_SEGMENT (lit_sym, dest_seg);
}
}
/* Walk over all the frags for segments in a list and mark them as
containing literals. As clunky as this is, we can't rely on frag_var
and frag_variant to get called in all situations. */
static void
mark_literal_frags (segment)
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 (head, after)
seg_list *head;
segT after;
{
/* Move all of the sections in the section list to come
after "after" in the gnu segment list. */
head = head->next;
while (head)
{
segT literal_section = head->seg;
/* Move the literal section after "after". */
assert (literal_section);
if (literal_section != after)
{
xtensa_remove_section (literal_section);
xtensa_insert_section (after, literal_section);
}
head = head->next;
}
}
/* Push all the literal segments to the end of the gnu list. */
void
xtensa_reorder_segments ()
{
segT sec;
segT last_sec;
int old_count = 0;
int new_count = 0;
for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
old_count++;
/* Now that we have the last section, push all the literal
sections to the end. */
last_sec = get_last_sec ();
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);
/* Now perform the final error check. */
for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
new_count++;
assert (new_count == old_count);
}
segT
get_last_sec ()
{
segT last_sec = stdoutput->sections;
while (last_sec->next != NULL)
last_sec = last_sec->next;
return last_sec;
}
/* Change the emit state (seg, subseg, and frag related stuff) to the
correct location. Return a emit_state which can be passed to
xtensa_restore_emit_state to return to current fragment. */
void
xtensa_switch_to_literal_fragment (result)
emit_state *result;
{
/* When we mark a literal pool location, we want to put a frag in
the literal pool that points to it. But to do that, we want to
switch_to_literal_fragment. But literal sections don't have
literal pools, so their location is always null, so we would
recurse forever. This is kind of hacky, but it works. */
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_warn (_("inlining literal pool; "
"specify location with .literal_position."));
recursive = TRUE;
xtensa_mark_literal_pool_location ();
recursive = FALSE;
}
/* Special case: If we are in the ".fini" or ".init" section, then
we will ALWAYS be generating to the ".fini.literal" and
".init.literal" sections. */
if (is_init)
{
cache_literal_section (init_literal_head,
default_lit_sections.init_lit_seg_name,
&default_lit_sections.init_lit_seg);
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);
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);
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)
{
/* Close whatever frag is there. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
frag_now->tc_frag_data.literal_frag = pool_location;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
}
/* Do a 4 byte align here. */
frag_align (2, 0, 0);
}
/* Call this function before emitting data into the literal section.
This is a helper function for xtensa_switch_to_literal_fragment.
This is similar to a .section new_now_seg subseg. */
void
xtensa_switch_section_emit_state (state, new_now_seg, new_now_subseg)
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_new (segment_name (new_now_seg), new_now_subseg);
}
/* Use to restore the emitting into the normal place. */
void
xtensa_restore_emit_state (state)
emit_state *state;
{
generating_literals = state->generating_literals;
subseg_new (state->name, state->now_subseg);
}
/* Get a segment of a given name. If the segment is already
present, return it; otherwise, create a new one. */
static void
cache_literal_section (head, name, seg)
seg_list *head;
const char *name;
segT *seg;
{
segT current_section = now_seg;
int current_subsec = now_subseg;
if (*seg != 0)
return;
*seg = retrieve_literal_seg (head, name);
subseg_set (current_section, current_subsec);
}
/* Get a segment of a given name. If the segment is already
present, return it; otherwise, create a new one. */
static segT
retrieve_literal_seg (head, name)
seg_list *head;
const char *name;
{
segT ret = 0;
assert (head);
ret = seg_present (name);
if (!ret)
{
ret = subseg_new (name, (subsegT) 0);
add_seg_list (head, ret);
bfd_set_section_flags (stdoutput, ret, SEC_HAS_CONTENTS |
SEC_READONLY | SEC_ALLOC | SEC_LOAD | SEC_CODE);
bfd_set_section_alignment (stdoutput, ret, 2);
}
return ret;
}
/* Return a segment of a given name if it is present. */
static segT
seg_present (name)
const char *name;
{
segT seg;
seg = stdoutput->sections;
while (seg)
{
if (!strcmp (segment_name (seg), name))
return seg;
seg = seg->next;
}
return 0;
}
/* Add a segment to a segment list. */
static void
add_seg_list (head, seg)
seg_list *head;
segT seg;
{
seg_list *n;
n = (seg_list *) xmalloc (sizeof (seg_list));
assert (n);
n->seg = seg;
n->next = head->next;
head->next = n;
}
/* Set up Property Tables after Relaxation. */
#define XTENSA_INSN_SEC_NAME ".xt.insn"
#define XTENSA_LIT_SEC_NAME ".xt.lit"
void
xtensa_post_relax_hook ()
{
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_create_property_segments (get_frag_is_insn,
XTENSA_INSN_SEC_NAME,
xt_insn_sec);
xtensa_create_property_segments (get_frag_is_literal,
XTENSA_LIT_SEC_NAME,
xt_literal_sec);
}
static bfd_boolean
get_frag_is_literal (fragP)
const fragS *fragP;
{
assert (fragP != NULL);
return (fragP->tc_frag_data.is_literal);
}
static bfd_boolean
get_frag_is_insn (fragP)
const fragS *fragP;
{
assert (fragP != NULL);
return (fragP->tc_frag_data.is_insn);
}
static void
xtensa_create_property_segments (property_function, section_name_base,
sec_type)
frag_predicate property_function;
const char * section_name_base;
xt_section_type sec_type;
{
segT *seclist;
/* Walk over all of the current segments.
Walk over each fragment
For each fragment that has instructions
Build an instruction record (append where possible). */
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segT sec = *seclist;
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];
/* Walk over all of the frchains here and add new sections. */
add_xt_block_frags (sec, insn_sec, xt_blocks, property_function);
}
}
/* Now we fill them out.... */
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;
/* This is a section with some data. */
size_t num_recs = 0;
size_t 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);
/* In order to make this work with the assembler, we have to
build some frags and then build the "fixups" for it. It
would be easier to just set the contents then set the
arlents. */
if (num_recs)
{
/* Allocate a fragment and leak it. */
fragS *fragP;
size_t frag_size;
fixS *fixes;
frchainS *frchainP;
size_t 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;
/* the rest are zeros */
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);
/* Write the fixup. */
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;
/* Write the length. */
md_number_to_chars (&frag_data[4 + 8 * i],
cur_block->size, 4);
cur_block = cur_block->next;
}
}
}
}
}
segment_info_type *
retrieve_segment_info (seg)
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 ((PTR) seginfo, 0, sizeof (*seginfo));
seginfo->fix_root = NULL;
seginfo->fix_tail = NULL;
seginfo->bfd_section = seg;
seginfo->sym = 0;
/* We will not be dealing with these, only our special ones. */
#if 0
if (seg == bfd_abs_section_ptr)
abs_seg_info = seginfo;
else if (seg == bfd_und_section_ptr)
und_seg_info = seginfo;
else
#endif
bfd_set_section_userdata (stdoutput, seg, (PTR) seginfo);
#if 0
seg_fix_rootP = &segment_info[seg].fix_root;
seg_fix_tailP = &segment_info[seg].fix_tail;
#endif
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;
/* Do not init the objstack. */
/* obstack_begin (&frchainP->frch_obstack, chunksize); */
/* frchainP->frch_frag_now = fragP; */
frchainP->frch_frag_now = NULL;
seginfo->frchainP = frchainP;
}
return seginfo;
}
segT
retrieve_xtensa_section (sec_name)
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;
}
bfd_boolean
section_has_property (sec, property_function)
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;
}
/* Two types of block sections exist right now: literal and insns. */
void
add_xt_block_frags (sec, xt_block_sec, xt_block, property_function)
segT sec;
segT xt_block_sec;
xtensa_block_info **xt_block;
frag_predicate 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);
/* Build it if needed. */
while (*xt_block != NULL)
xt_block = &(*xt_block)->next;
/* We are either at NULL at the beginning or at the end. */
/* Walk through the frags. */
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;
*xt_block = new_block;
}
}
}
}
}
/* Instruction Stack Functions (from "xtensa-istack.h"). */
void
istack_init (stack)
IStack *stack;
{
memset (stack, 0, sizeof (IStack));
stack->ninsn = 0;
}
bfd_boolean
istack_empty (stack)
IStack *stack;
{
return (stack->ninsn == 0);
}
bfd_boolean
istack_full (stack)
IStack *stack;
{
return (stack->ninsn == MAX_ISTACK);
}
/* Return a pointer to the top IStack entry.
It is an error to call this if istack_empty () is true. */
TInsn *
istack_top (stack)
IStack *stack;
{
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
return &stack->insn[rec];
}
/* Add a new TInsn to an IStack.
It is an error to call this if istack_full () is true. */
void
istack_push (stack, insn)
IStack *stack;
TInsn *insn;
{
int rec = stack->ninsn;
assert (!istack_full (stack));
tinsn_copy (&stack->insn[rec], insn);
stack->ninsn++;
}
/* Clear space for the next TInsn on the IStack and return a pointer
to it. It is an error to call this if istack_full () is true. */
TInsn *
istack_push_space (stack)
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;
}
/* Remove the last pushed instruction. It is an error to call this if
istack_empty () returns true. */
void
istack_pop (stack)
IStack *stack;
{
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
stack->ninsn--;
memset (&stack->insn[rec], 0, sizeof (TInsn));
}
/* TInsn functions. */
void
tinsn_init (dst)
TInsn *dst;
{
memset (dst, 0, sizeof (TInsn));
}
void
tinsn_copy (dst, src)
TInsn *dst;
const TInsn *src;
{
tinsn_init (dst);
memcpy (dst, src, sizeof (TInsn));
}
/* Get the ``num''th token of the TInsn.
It is illegal to call this if num > insn->ntoks. */
expressionS *
tinsn_get_tok (insn, num)
TInsn *insn;
int num;
{
assert (num < insn->ntok);
return &insn->tok[num];
}
/* Return true if ANY of the operands in the insn are symbolic. */
static bfd_boolean
tinsn_has_symbolic_operands (insn)
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 (insn)
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:
if (i == get_relaxable_immed (insn->opcode))
break;
as_bad (_("invalid symbolic operand %d on '%s'"),
i, xtensa_opcode_name (xtensa_default_isa, insn->opcode));
return TRUE;
}
}
return FALSE;
}
/* For assembly code with complex expressions (e.g. subtraction),
we have to build them in the literal pool so that
their results are calculated correctly after relaxation.
The relaxation only handles expressions that
boil down to SYMBOL + OFFSET. */
static bfd_boolean
tinsn_has_complex_operands (insn)
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:
break;
default:
return TRUE;
}
}
return FALSE;
}
/* Convert the constant operands in the t_insn to insnbuf.
Return true if there is a symbol in the immediate field.
Before this is called,
1) the number of operands are correct
2) the t_insn is a ITYPE_INSN
3) ONLY the relaxable_ is built
4) All operands are O_constant, O_symbol. All constants fit
The return value tells whether there are any remaining O_symbols. */
static bfd_boolean
tinsn_to_insnbuf (t_insn, insnbuf)
TInsn *t_insn;
xtensa_insnbuf insnbuf;
{
xtensa_isa isa = xtensa_default_isa;
xtensa_opcode opcode = t_insn->opcode;
bfd_boolean has_fixup = FALSE;
int noperands = xtensa_num_operands (isa, opcode);
int i;
uint32 opnd_value;
char *file_name;
int line;
assert (t_insn->insn_type == ITYPE_INSN);
if (noperands != t_insn->ntok)
as_fatal (_("operand number mismatch"));
xtensa_encode_insn (isa, opcode, insnbuf);
for (i = 0; i < noperands; ++i)
{
expressionS *expr = &t_insn->tok[i];
xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
switch (expr->X_op)
{
case O_register:
/* The register number has already been checked in
expression_maybe_register, so we don't need to check here. */
opnd_value = expr->X_add_number;
(void) xtensa_operand_encode (operand, &opnd_value);
xtensa_operand_set_field (operand, insnbuf, opnd_value);
break;
case O_constant:
as_where (&file_name, &line);
/* It is a constant and we called this function,
then we have to try to fit it. */
xtensa_insnbuf_set_operand (insnbuf, opcode, operand,
expr->X_add_number, file_name, line);
break;
case O_symbol:
default:
has_fixup = TRUE;
break;
}
}
return has_fixup;
}
/* Check the instruction arguments. Return true on failure. */
bfd_boolean
tinsn_check_arguments (insn)
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_num_operands (isa, opcode) > insn->ntok)
{
as_bad (_("too few operands"));
return TRUE;
}
if (xtensa_num_operands (isa, opcode) < insn->ntok)
{
as_bad (_("too many operands"));
return TRUE;
}
return FALSE;
}
/* Load an instruction from its encoded form. */
static void
tinsn_from_chars (t_insn, f)
TInsn *t_insn;
char *f;
{
static xtensa_insnbuf insnbuf = NULL;
int i;
xtensa_opcode opcode;
xtensa_isa isa = xtensa_default_isa;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
xtensa_insnbuf_from_chars (isa, insnbuf, f);
opcode = xtensa_decode_insn (isa, insnbuf);
/* Find the immed. */
tinsn_init (t_insn);
t_insn->insn_type = ITYPE_INSN;
t_insn->is_specific_opcode = FALSE; /* Must not be specific. */
t_insn->opcode = opcode;
t_insn->ntok = xtensa_num_operands (isa, opcode);
for (i = 0; i < t_insn->ntok; i++)
{
set_expr_const (&t_insn->tok[i],
xtensa_insnbuf_get_operand (insnbuf, opcode, i));
}
}
/* Read the value of the relaxable immed from the fr_symbol and fr_offset. */
static void
tinsn_immed_from_frag (t_insn, fragP)
TInsn *t_insn;
fragS *fragP;
{
xtensa_opcode opcode = t_insn->opcode;
int opnum;
if (fragP->fr_symbol)
{
opnum = get_relaxable_immed (opcode);
set_expr_symbol_offset (&t_insn->tok[opnum],
fragP->fr_symbol, fragP->fr_offset);
}
}
static int
get_num_stack_text_bytes (istack)
IStack *istack;
{
int i;
int text_bytes = 0;
for (i = 0; i < istack->ninsn; i++)
{
TInsn *t_insn = &istack->insn[i];
if (t_insn->insn_type == ITYPE_INSN)
text_bytes += xg_get_insn_size (t_insn);
}
return text_bytes;
}
static int
get_num_stack_literal_bytes (istack)
IStack *istack;
{
int i;
int lit_bytes = 0;
for (i = 0; i < istack->ninsn; i++)
{
TInsn *t_insn = &istack->insn[i];
if (t_insn->insn_type == ITYPE_LITERAL && t_insn->ntok == 1)
lit_bytes += 4;
}
return lit_bytes;
}
/* Expression utilities. */
/* Return true if the expression is an integer constant. */
bfd_boolean
expr_is_const (s)
const expressionS *s;
{
return (s->X_op == O_constant);
}
/* Get the expression constant.
Calling this is illegal if expr_is_const () returns true. */
offsetT
get_expr_const (s)
const expressionS *s;
{
assert (expr_is_const (s));
return s->X_add_number;
}
/* Set the expression to a constant value. */
void
set_expr_const (s, val)
expressionS *s;
offsetT val;
{
s->X_op = O_constant;
s->X_add_number = val;
s->X_add_symbol = NULL;
s->X_op_symbol = NULL;
}
/* Set the expression to a symbol + constant offset. */
void
set_expr_symbol_offset (s, sym, offset)
expressionS *s;
symbolS *sym;
offsetT offset;
{
s->X_op = O_symbol;
s->X_add_symbol = sym;
s->X_op_symbol = NULL; /* unused */
s->X_add_number = offset;
}
bfd_boolean
expr_is_equal (s1, s2)
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 (dst, src)
expressionS *dst;
const expressionS *src;
{
memcpy (dst, src, sizeof (expressionS));
}
/* Support for Tensilica's "--rename-section" option. */
#ifdef XTENSA_SECTION_RENAME
struct rename_section_struct
{
char *old_name;
char *new_name;
struct rename_section_struct *next;
};
static struct rename_section_struct *section_rename;
/* Parse the string oldname=new_name:oldname2=new_name2
and call add_section_rename. */
void
build_section_rename (arg)
const char *arg;
{
char *this_arg = NULL;
char *next_arg = NULL;
for (this_arg = strdup (arg); this_arg != NULL; this_arg = next_arg)
{
if (this_arg)
{
next_arg = strchr (this_arg, ':');
if (next_arg)
{
*next_arg = '\0';
next_arg++;
}
}
{
char *old_name = this_arg;
char *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++;
add_section_rename (old_name, new_name);
}
}
}
static void
add_section_rename (old_name, new_name)
char *old_name;
char *new_name;
{
struct rename_section_struct *r = section_rename;
/* Check for invalid section renaming. */
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);
}
/* Now add it. */
r = (struct rename_section_struct *)
xmalloc (sizeof (struct rename_section_struct));
r->old_name = strdup (old_name);
r->new_name = strdup (new_name);
r->next = section_rename;
section_rename = r;
}
const char *
xtensa_section_rename (name)
const 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;
}
#endif /* XTENSA_SECTION_RENAME */