/* tc-xtensa.c -- Assemble Xtensa instructions.
- Copyright 2003 Free Software Foundation, Inc.
+ Copyright 2003, 2004, 2005 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
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,
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA. */
#include <string.h>
+#include <limits.h>
#include "as.h"
#include "sb.h"
#include "safe-ctype.h"
/* 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
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. */
+/* Flags to indicate whether the hardware supports the density and
+ absolute literals options. */
-#if STATIC_LIBISA
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
-#else
-bfd_boolean density_supported = TRUE;
-#endif
+bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
+
+/* Maximum width we would pad an unreachable frag to get alignment. */
+#define UNREACHABLE_MAX_WIDTH 8
-#define XTENSA_FETCH_WIDTH 4
+static vliw_insn cur_vinsn;
+
+unsigned xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
+
+static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
+
+/* Some functions are only valid in the front end. This variable
+ allows us to assert that we haven't crossed over into the
+ back end. */
+static bfd_boolean past_xtensa_end = FALSE;
/* Flags for properties of the last instruction in a segment. */
#define FLAG_IS_A0_WRITER 0x1
their own special .fini.literal and .init.literal sections. */
#define LITERAL_SECTION_NAME xtensa_section_rename (".literal")
+#define LIT4_SECTION_NAME xtensa_section_rename (".lit4")
#define FINI_SECTION_NAME xtensa_section_rename (".fini")
#define INIT_SECTION_NAME xtensa_section_rename (".init")
#define FINI_LITERAL_SECTION_NAME xtensa_section_rename (".fini.literal")
#define INIT_LITERAL_SECTION_NAME xtensa_section_rename (".init.literal")
-/* This type is used for the directive_stack to keep track of the
+/* 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 *lit4_seg_name;
const char *init_lit_seg_name;
const char *fini_lit_seg_name;
segT lit_seg;
+ segT lit4_seg;
segT init_lit_seg;
segT fini_lit_seg;
} lit_state;
static sym_list *literal_syms;
+/* Flags to determine whether to prefer const16 or l32r
+ if both options are available. */
+int prefer_const16 = 0;
+int prefer_l32r = 0;
+
/* Global flag to indicate when we are emitting literals. */
int generating_literals = 0;
+/* The following PROPERTY table definitions are copied from
+ <elf/xtensa.h> and must be kept in sync with the code there. */
+
+/* Flags in the property tables to specify whether blocks of memory
+ are literals, instructions, data, or unreachable. For
+ instructions, blocks that begin loop targets and branch targets are
+ designated. Blocks that do not allow density, instruction
+ reordering or transformation are also specified. Finally, for
+ branch targets, branch target alignment priority is included.
+ Alignment of the next block is specified in the current block
+ and the size of the current block does not include any fill required
+ to align to the next block. */
+
+#define XTENSA_PROP_LITERAL 0x00000001
+#define XTENSA_PROP_INSN 0x00000002
+#define XTENSA_PROP_DATA 0x00000004
+#define XTENSA_PROP_UNREACHABLE 0x00000008
+/* Instruction only properties at beginning of code. */
+#define XTENSA_PROP_INSN_LOOP_TARGET 0x00000010
+#define XTENSA_PROP_INSN_BRANCH_TARGET 0x00000020
+/* Instruction only properties about code. */
+#define XTENSA_PROP_INSN_NO_DENSITY 0x00000040
+#define XTENSA_PROP_INSN_NO_REORDER 0x00000080
+#define XTENSA_PROP_INSN_NO_TRANSFORM 0x00000100
+
+/* Branch target alignment information. This transmits information
+ to the linker optimization about the priority of aligning a
+ particular block for branch target alignment: None, low priority,
+ high priority, or required. These only need to be checked in
+ instruction blocks marked as XTENSA_PROP_INSN_BRANCH_TARGET.
+ Common usage is
+
+ switch (GET_XTENSA_PROP_BT_ALIGN (flags))
+ case XTENSA_PROP_BT_ALIGN_NONE:
+ case XTENSA_PROP_BT_ALIGN_LOW:
+ case XTENSA_PROP_BT_ALIGN_HIGH:
+ case XTENSA_PROP_BT_ALIGN_REQUIRE:
+*/
+#define XTENSA_PROP_BT_ALIGN_MASK 0x00000600
+
+/* No branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_NONE 0x0
+/* Low priority branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_LOW 0x1
+/* High priority branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_HIGH 0x2
+/* Required branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_REQUIRE 0x3
+
+#define GET_XTENSA_PROP_BT_ALIGN(flag) \
+ (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
+#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
+ (((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
+ (((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
+
+
+/* Alignment is specified in the block BEFORE the one that needs
+ alignment. Up to 5 bits. Use GET_XTENSA_PROP_ALIGNMENT(flags) to
+ get the required alignment specified as a power of 2. Use
+ SET_XTENSA_PROP_ALIGNMENT(flags, pow2) to set the required
+ alignment. Be careful of side effects since the SET will evaluate
+ flags twice. Also, note that the SIZE of a block in the property
+ table does not include the alignment size, so the alignment fill
+ must be calculated to determine if two blocks are contiguous.
+ TEXT_ALIGN is not currently implemented but is a placeholder for a
+ possible future implementation. */
+
+#define XTENSA_PROP_ALIGN 0x00000800
+
+#define XTENSA_PROP_ALIGNMENT_MASK 0x0001f000
+
+#define GET_XTENSA_PROP_ALIGNMENT(flag) \
+ (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
+#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
+ (((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
+ (((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
+
+#define XTENSA_PROP_INSN_ABSLIT 0x00020000
+
+
+/* Structure for saving instruction and alignment per-fragment data
+ that will be written to the object file. This structure is
+ equivalent to the actual data that will be written out to the file
+ but is easier to use. We provide a conversion to file flags
+ in frag_flags_to_number. */
+
+typedef struct frag_flags_struct frag_flags;
+
+struct frag_flags_struct
+{
+ /* is_literal should only be used after xtensa_move_literals.
+ If you need to check if you are generating a literal fragment,
+ then use the generating_literals global. */
+
+ unsigned is_literal : 1;
+ unsigned is_insn : 1;
+ unsigned is_data : 1;
+ unsigned is_unreachable : 1;
+
+ struct
+ {
+ unsigned is_loop_target : 1;
+ unsigned is_branch_target : 1; /* Branch targets have a priority. */
+ unsigned bt_align_priority : 2;
+
+ unsigned is_no_density : 1;
+ /* no_longcalls flag does not need to be placed in the object file. */
+ /* is_specific_opcode implies no_transform. */
+ unsigned is_no_transform : 1;
+
+ unsigned is_no_reorder : 1;
+
+ /* Uses absolute literal addressing for l32r. */
+ unsigned is_abslit : 1;
+ } insn;
+ unsigned is_align : 1;
+ unsigned alignment : 5;
+};
+
+
+/* Structure for saving information about a block of property data
+ for frags that have the same flags. */
+struct xtensa_block_info_struct
+{
+ segT sec;
+ bfd_vma offset;
+ size_t size;
+ frag_flags flags;
+ struct xtensa_block_info_struct *next;
+};
+
/* Structure for saving the current state before emitting literals. */
typedef struct emit_state_struct
} emit_state;
+/* Opcode placement information */
+
+typedef unsigned long long bitfield;
+#define bit_is_set(bit, bf) ((bf) & (0x01ll << (bit)))
+#define set_bit(bit, bf) ((bf) |= (0x01ll << (bit)))
+#define clear_bit(bit, bf) ((bf) &= ~(0x01ll << (bit)))
+
+#define MAX_FORMATS 32
+
+typedef struct op_placement_info_struct
+{
+ int num_formats;
+ /* A number describing how restrictive the issue is for this
+ opcode. For example, an opcode that fits lots of different
+ formats has a high freedom, as does an opcode that fits
+ only one format but many slots in that format. The most
+ restrictive is the opcode that fits only one slot in one
+ format. */
+ int issuef;
+ /* The single format (i.e., if the op can live in a bundle by itself),
+ narrowest format, and widest format the op can be bundled in
+ and their sizes: */
+ xtensa_format single;
+ xtensa_format narrowest;
+ xtensa_format widest;
+ char narrowest_size;
+ char widest_size;
+ char single_size;
+
+ /* formats is a bitfield with the Nth bit set
+ if the opcode fits in the Nth xtensa_format. */
+ bitfield formats;
+
+ /* slots[N]'s Mth bit is set if the op fits in the
+ Mth slot of the Nth xtensa_format. */
+ bitfield slots[MAX_FORMATS];
+
+ /* A count of the number of slots in a given format
+ an op can fit (i.e., the bitcount of the slot field above). */
+ char slots_in_format[MAX_FORMATS];
+
+} op_placement_info, *op_placement_info_table;
+
+op_placement_info_table op_placement_table;
+
+
+/* Extra expression types. */
+
+#define O_pltrel O_md1 /* like O_symbol but use a PLT reloc */
+#define O_hi16 O_md2 /* use high 16 bits of symbolic value */
+#define O_lo16 O_md3 /* use low 16 bits of symbolic value */
+
+
/* Directives. */
typedef enum
directive_none = 0,
directive_literal,
directive_density,
- directive_generics,
- directive_relax,
+ directive_transform,
directive_freeregs,
directive_longcalls,
- directive_literal_prefix
+ directive_literal_prefix,
+ directive_schedule,
+ directive_absolute_literals,
+ directive_last_directive
} directiveE;
typedef struct
const directive_infoS directive_info[] =
{
- {"none", FALSE},
- {"literal", FALSE},
- {"density", TRUE},
- {"generics", TRUE},
- {"relax", TRUE},
- {"freeregs", FALSE},
- {"longcalls", TRUE},
- {"literal_prefix", FALSE}
+ { "none", FALSE },
+ { "literal", FALSE },
+ { "density", TRUE },
+ { "transform", TRUE },
+ { "freeregs", FALSE },
+ { "longcalls", TRUE },
+ { "literal_prefix", FALSE },
+ { "schedule", TRUE },
+ { "absolute-literals", TRUE }
};
bfd_boolean directive_state[] =
{
FALSE, /* none */
FALSE, /* literal */
-#if STATIC_LIBISA && !XCHAL_HAVE_DENSITY
+#if !XCHAL_HAVE_DENSITY
FALSE, /* density */
#else
TRUE, /* density */
#endif
- TRUE, /* generics */
- TRUE, /* relax */
+ TRUE, /* transform */
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. */
+ FALSE, /* literal_prefix */
+ TRUE, /* schedule */
+#if XSHAL_USE_ABSOLUTE_LITERALS
+ TRUE /* absolute_literals */
+#else
+ FALSE /* absolute_literals */
+#endif
};
-/* 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 **));
+static void xtensa_begin_directive (int);
+static void xtensa_end_directive (int);
+static void xtensa_dwarf2_directive_loc (int);
+static void xtensa_literal_prefix (char const *, int);
+static void xtensa_literal_position (int);
+static void xtensa_literal_pseudo (int);
+static void xtensa_frequency_pseudo (int);
+static void xtensa_elf_cons (int);
-/* Functions for dealing with the Xtensa ISA. */
+/* Parsing and Idiom Translation. */
-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 *));
+static bfd_reloc_code_real_type xtensa_elf_suffix (char **, expressionS *);
/* 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 *));
+static void xtensa_mark_literal_pool_location (void);
+static addressT get_expanded_loop_offset (xtensa_opcode);
+static fragS *get_literal_pool_location (segT);
+static void set_literal_pool_location (segT, fragS *);
+static void xtensa_set_frag_assembly_state (fragS *);
+static void finish_vinsn (vliw_insn *);
+static bfd_boolean emit_single_op (TInsn *);
+static int total_frag_text_expansion (fragS *);
/* 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));
+static int get_text_align_power (unsigned);
+static int get_text_align_max_fill_size (int, bfd_boolean, bfd_boolean);
+static int branch_align_power (segT);
/* 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 *));
+static long relax_frag_add_nop (fragS *);
-/* Flags for the Last Instruction in Each Subsegment. */
+/* Accessors for additional per-subsegment information. */
-static unsigned get_last_insn_flags
- PARAMS ((segT, subsegT));
-static void set_last_insn_flags
- PARAMS ((segT, subsegT, unsigned, bfd_boolean));
+static unsigned get_last_insn_flags (segT, subsegT);
+static void set_last_insn_flags (segT, subsegT, unsigned, bfd_boolean);
+static float get_subseg_total_freq (segT, subsegT);
+static float get_subseg_target_freq (segT, subsegT);
+static void set_subseg_freq (segT, subsegT, float, float);
/* 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 xtensa_move_literals (void);
+static void xtensa_reorder_segments (void);
+static void xtensa_switch_to_literal_fragment (emit_state *);
+static void xtensa_switch_to_non_abs_literal_fragment (emit_state *);
+static void xtensa_switch_section_emit_state (emit_state *, segT, subsegT);
+static void xtensa_restore_emit_state (emit_state *);
static void cache_literal_section
- 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));
+ (seg_list *, const char *, segT *, bfd_boolean);
-/* Property Table (e.g., ".xt.insn" and ".xt.lit") Functions. */
+/* Import from elf32-xtensa.c in BFD library. */
-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 *));
+extern char *xtensa_get_property_section_name (asection *, const char *);
-/* Import from elf32-xtensa.c in BFD library. */
-extern char *xtensa_get_property_section_name
- PARAMS ((asection *, const char *));
+/* op_placement_info functions. */
+
+static void init_op_placement_info_table (void);
+extern bfd_boolean opcode_fits_format_slot (xtensa_opcode, xtensa_format, int);
+static int xg_get_single_size (xtensa_opcode);
+static xtensa_format xg_get_single_format (xtensa_opcode);
/* 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 *));
+
+static bfd_boolean tinsn_has_symbolic_operands (const TInsn *);
+static bfd_boolean tinsn_has_invalid_symbolic_operands (const TInsn *);
+static bfd_boolean tinsn_has_complex_operands (const TInsn *);
+static bfd_boolean tinsn_to_insnbuf (TInsn *, xtensa_insnbuf);
+static bfd_boolean tinsn_check_arguments (const TInsn *);
+static void tinsn_from_chars (TInsn *, char *, int);
+static void tinsn_immed_from_frag (TInsn *, fragS *, int);
+static int get_num_stack_text_bytes (IStack *);
+static int get_num_stack_literal_bytes (IStack *);
+
+/* vliw_insn functions. */
+
+static void xg_init_vinsn (vliw_insn *);
+static void xg_clear_vinsn (vliw_insn *);
+static bfd_boolean vinsn_has_specific_opcodes (vliw_insn *);
+static void xg_free_vinsn (vliw_insn *);
+static bfd_boolean vinsn_to_insnbuf
+ (vliw_insn *, char *, fragS *, bfd_boolean);
+static void vinsn_from_chars (vliw_insn *, char *);
/* 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
+
+bfd_boolean expr_is_const (const expressionS *);
+offsetT get_expr_const (const expressionS *);
+void set_expr_const (expressionS *, offsetT);
+bfd_boolean expr_is_register (const expressionS *);
+offsetT get_expr_register (const expressionS *);
+void set_expr_symbol_offset (expressionS *, symbolS *, offsetT);
+static void set_expr_symbol_offset_diff
+ (expressionS *, symbolS *, symbolS *, offsetT);
+bfd_boolean expr_is_equal (expressionS *, expressionS *);
+static void copy_expr (expressionS *, const expressionS *);
+
+/* Section renaming. */
+
+static void build_section_rename (const char *);
/* ISA imported from bfd. */
static xtensa_opcode xtensa_callx4_opcode;
static xtensa_opcode xtensa_callx8_opcode;
static xtensa_opcode xtensa_callx12_opcode;
+static xtensa_opcode xtensa_const16_opcode;
static xtensa_opcode xtensa_entry_opcode;
+static xtensa_opcode xtensa_movi_opcode;
+static xtensa_opcode xtensa_movi_n_opcode;
static xtensa_opcode xtensa_isync_opcode;
-static xtensa_opcode xtensa_j_opcode;
static xtensa_opcode xtensa_jx_opcode;
+static xtensa_opcode xtensa_l32r_opcode;
static xtensa_opcode xtensa_loop_opcode;
static xtensa_opcode xtensa_loopnez_opcode;
static xtensa_opcode xtensa_loopgtz_opcode;
+static xtensa_opcode xtensa_nop_opcode;
static xtensa_opcode xtensa_nop_n_opcode;
static xtensa_opcode xtensa_or_opcode;
static xtensa_opcode xtensa_ret_opcode;
static xtensa_opcode xtensa_ret_n_opcode;
static xtensa_opcode xtensa_retw_opcode;
static xtensa_opcode xtensa_retw_n_opcode;
-static xtensa_opcode xtensa_rsr_opcode;
+static xtensa_opcode xtensa_rsr_lcount_opcode;
static xtensa_opcode xtensa_waiti_opcode;
\f
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 warn_unaligned_branch_targets = FALSE;
static bfd_boolean has_a0_b_retw = FALSE;
-static bfd_boolean workaround_a0_b_retw = TRUE;
+static bfd_boolean workaround_a0_b_retw = FALSE;
+static bfd_boolean workaround_b_j_loop_end = FALSE;
+static bfd_boolean workaround_short_loop = FALSE;
+static bfd_boolean maybe_has_short_loop = FALSE;
+static bfd_boolean workaround_close_loop_end = FALSE;
+static bfd_boolean maybe_has_close_loop_end = FALSE;
-static bfd_boolean 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;
+/* When workaround_short_loops is TRUE, all loops with early exits must
+ have at least 3 instructions. workaround_all_short_loops is a modifier
+ to the workaround_short_loop flag. In addition to the
+ workaround_short_loop actions, all straightline loopgtz and loopnez
+ must have at least 3 instructions. */
-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 workaround_all_short_loops = 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 void
+xtensa_setup_hw_workarounds (int earliest, int latest)
+{
+ if (earliest > latest)
+ as_fatal (_("illegal range of target hardware versions"));
-static bfd_boolean software_avoid_all_short_loops = TRUE;
-static bfd_boolean workaround_all_short_loops = TRUE;
+ /* Enable all workarounds for pre-T1050.0 hardware. */
+ if (earliest < 105000 || latest < 105000)
+ {
+ workaround_a0_b_retw |= TRUE;
+ workaround_b_j_loop_end |= TRUE;
+ workaround_short_loop |= TRUE;
+ workaround_close_loop_end |= TRUE;
+ workaround_all_short_loops |= TRUE;
+ }
+}
-/* This is on a per-instruction basis. */
-static bfd_boolean specific_opcode = FALSE;
enum
{
option_relax,
option_no_relax,
+ option_link_relax,
+ option_no_link_relax,
+
option_generics,
option_no_generics,
+ option_transform,
+ option_no_transform,
+
option_text_section_literals,
option_no_text_section_literals,
+ option_absolute_literals,
+ option_no_absolute_literals,
+
option_align_targets,
option_no_align_targets,
- option_align_only_targets,
- option_no_align_only_targets,
+ option_warn_unaligned_targets,
option_longcalls,
option_no_longcalls,
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
+ option_prefer_l32r,
+ option_prefer_const16,
+
+ option_target_hardware
};
const char *md_shortopts = "";
struct option md_longopts[] =
{
- {"density", no_argument, NULL, option_density},
- {"no-density", no_argument, NULL, option_no_density},
- /* 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},
+ { "density", no_argument, NULL, option_density },
+ { "no-density", no_argument, NULL, option_no_density },
+
+ /* Both "relax" and "generics" are deprecated and treated as equivalent
+ to the "transform" option. */
+ { "relax", no_argument, NULL, option_relax },
+ { "no-relax", no_argument, NULL, option_no_relax },
+ { "generics", no_argument, NULL, option_generics },
+ { "no-generics", no_argument, NULL, option_no_generics },
+
+ { "transform", no_argument, NULL, option_transform },
+ { "no-transform", no_argument, NULL, option_no_transform },
+ { "text-section-literals", no_argument, NULL, option_text_section_literals },
+ { "no-text-section-literals", no_argument, NULL,
+ option_no_text_section_literals },
+ { "absolute-literals", no_argument, NULL, option_absolute_literals },
+ { "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
/* 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}
+ { "target-align", no_argument, NULL, option_align_targets },
+ { "no-target-align", no_argument, NULL, option_no_align_targets },
+ { "warn-unaligned-targets", no_argument, NULL,
+ option_warn_unaligned_targets },
+ { "longcalls", no_argument, NULL, option_longcalls },
+ { "no-longcalls", no_argument, NULL, option_no_longcalls },
+
+ { "no-workaround-a0-b-retw", no_argument, NULL,
+ option_no_workaround_a0_b_retw },
+ { "workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw },
+
+ { "no-workaround-b-j-loop-end", no_argument, NULL,
+ option_no_workaround_b_j_loop_end },
+ { "workaround-b-j-loop-end", no_argument, NULL,
+ option_workaround_b_j_loop_end },
+
+ { "no-workaround-short-loops", no_argument, NULL,
+ option_no_workaround_short_loop },
+ { "workaround-short-loops", no_argument, NULL,
+ option_workaround_short_loop },
+
+ { "no-workaround-all-short-loops", no_argument, NULL,
+ option_no_workaround_all_short_loops },
+ { "workaround-all-short-loop", no_argument, NULL,
+ option_workaround_all_short_loops },
+
+ { "prefer-l32r", no_argument, NULL, option_prefer_l32r },
+ { "prefer-const16", no_argument, NULL, option_prefer_const16 },
+
+ { "no-workarounds", no_argument, NULL, option_no_workarounds },
+
+ { "no-workaround-close-loop-end", no_argument, NULL,
+ option_no_workaround_close_loop_end },
+ { "workaround-close-loop-end", no_argument, NULL,
+ option_workaround_close_loop_end },
+
+ { "rename-section", required_argument, NULL, option_rename_section_name },
+
+ { "link-relax", no_argument, NULL, option_link_relax },
+ { "no-link-relax", no_argument, NULL, option_no_link_relax },
+
+ { "target-hardware", required_argument, NULL, option_target_hardware },
+
+ { NULL, no_argument, NULL, 0 }
};
size_t md_longopts_size = sizeof md_longopts;
int
-md_parse_option (c, arg)
- int c;
- char *arg;
+md_parse_option (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;
+ as_warn (_("--density option is ignored"));
return 1;
case option_no_density:
- directive_state[directive_density] = FALSE;
+ as_warn (_("--no-density option is ignored"));
return 1;
- case option_generics:
- directive_state[directive_generics] = TRUE;
+ case option_link_relax:
+ linkrelax = 1;
return 1;
- case option_no_generics:
- directive_state[directive_generics] = FALSE;
+ case option_no_link_relax:
+ linkrelax = 0;
return 1;
+ case option_generics:
+ as_warn (_("--generics is deprecated; use --transform instead"));
+ return md_parse_option (option_transform, arg);
+ case option_no_generics:
+ as_warn (_("--no-generics is deprecated; use --no-transform instead"));
+ return md_parse_option (option_no_transform, arg);
+ case option_relax:
+ as_warn (_("--relax is deprecated; use --transform instead"));
+ return md_parse_option (option_transform, arg);
+ case option_no_relax:
+ as_warn (_("--no-relax is deprecated; use --no-transform instead"));
+ return md_parse_option (option_no_transform, arg);
case option_longcalls:
directive_state[directive_longcalls] = TRUE;
return 1;
case option_no_text_section_literals:
use_literal_section = TRUE;
return 1;
+ case option_absolute_literals:
+ if (!absolute_literals_supported)
+ {
+ as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
+ return 0;
+ }
+ directive_state[directive_absolute_literals] = TRUE;
+ return 1;
+ case option_no_absolute_literals:
+ directive_state[directive_absolute_literals] = FALSE;
+ return 1;
+
case option_workaround_a0_b_retw:
workaround_a0_b_retw = TRUE;
- 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;
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;
+ case option_warn_unaligned_targets:
+ warn_unaligned_branch_targets = TRUE;
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'"));
+ case option_rename_section_name:
+ build_section_rename (arg);
return 1;
- case option_text_section_name:
- add_section_rename (".text", arg);
- as_warn (_("'--text-section-name' is deprecated; "
- "use '--rename-section .text=NEWNAME'"));
+ case 'Q':
+ /* -Qy, -Qn: SVR4 arguments controlling whether a .comment section
+ should be emitted or not. FIXME: Not implemented. */
return 1;
-
- case option_data_section_name:
- add_section_rename (".data", arg);
- as_warn (_("'--data-section-name' is deprecated; "
- "use '--rename-section .data=NEWNAME'"));
+
+ case option_prefer_l32r:
+ if (prefer_const16)
+ as_fatal (_("prefer-l32r conflicts with prefer-const16"));
+ prefer_l32r = 1;
return 1;
- case option_bss_section_name:
- add_section_rename (".bss", arg);
- as_warn (_("'--bss-section-name' is deprecated; "
- "use '--rename-section .bss=NEWNAME'"));
+ case option_prefer_const16:
+ if (prefer_l32r)
+ as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
+ prefer_const16 = 1;
return 1;
- case option_rename_section_name:
- build_section_rename (arg);
+ case option_target_hardware:
+ {
+ int earliest, latest = 0;
+ if (*arg == 0 || *arg == '-')
+ as_fatal (_("invalid target hardware version"));
+
+ earliest = strtol (arg, &arg, 0);
+
+ if (*arg == 0)
+ latest = earliest;
+ else if (*arg == '-')
+ {
+ if (*++arg == 0)
+ as_fatal (_("invalid target hardware version"));
+ latest = strtol (arg, &arg, 0);
+ }
+ if (*arg != 0)
+ as_fatal (_("invalid target hardware version"));
+
+ xtensa_setup_hw_workarounds (earliest, latest);
+ return 1;
+ }
+
+ case option_transform:
+ /* This option has no affect other than to use the defaults,
+ which are already set. */
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. */
+ case option_no_transform:
+ /* This option turns off all transformations of any kind.
+ However, because we want to preserve the state of other
+ directives, we only change its own field. Thus, before
+ you perform any transformation, always check if transform
+ is available. If you use the functions we provide for this
+ purpose, you will be ok. */
+ directive_state[directive_transform] = FALSE;
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);
+md_show_usage (FILE *stream)
+{
+ fputs ("\n\
+Xtensa options:\n\
+ --[no-]text-section-literals\n\
+ [Do not] put literals in the text section\n\
+ --[no-]absolute-literals\n\
+ [Do not] default to use non-PC-relative literals\n\
+ --[no-]target-align [Do not] try to align branch targets\n\
+ --[no-]longcalls [Do not] emit 32-bit call sequences\n\
+ --[no-]transform [Do not] transform instructions\n\
+ --rename-section old=new Rename section 'old' to 'new'\n", stream);
+}
+
+\f
+/* Functions related to the list of current label symbols. */
+
+static void
+xtensa_add_insn_label (symbolS *sym)
+{
+ sym_list *l;
+
+ if (!free_insn_labels)
+ l = (sym_list *) xmalloc (sizeof (sym_list));
+ else
+ {
+ l = free_insn_labels;
+ free_insn_labels = l->next;
+ }
+
+ l->sym = sym;
+ l->next = insn_labels;
+ insn_labels = l;
+}
+
+
+static void
+xtensa_clear_insn_labels (void)
+{
+ sym_list **pl;
+
+ for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
+ ;
+ *pl = insn_labels;
+ insn_labels = NULL;
+}
+
+
+/* The "loops_ok" argument is provided to allow ignoring labels that
+ define loop ends. This fixes a bug where the NOPs to align a
+ loop opcode were included in a previous zero-cost loop:
+
+ loop a0, loopend
+ <loop1 body>
+ loopend:
+
+ loop a2, loopend2
+ <loop2 body>
+
+ would become:
+
+ loop a0, loopend
+ <loop1 body>
+ nop.n <===== bad!
+ loopend:
+
+ loop a2, loopend2
+ <loop2 body>
+
+ This argument is used to prevent moving the NOP to before the
+ loop-end label, which is what you want in this special case. */
+
+static void
+xtensa_move_labels (fragS *new_frag, valueT new_offset, bfd_boolean loops_ok)
+{
+ sym_list *lit;
+
+ for (lit = insn_labels; lit; lit = lit->next)
+ {
+ symbolS *lit_sym = lit->sym;
+ if (loops_ok || ! symbol_get_tc (lit_sym)->is_loop_target)
+ {
+ S_SET_VALUE (lit_sym, new_offset);
+ symbol_set_frag (lit_sym, new_frag);
+ }
+ }
}
\f
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},
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0). */
+ { "literal_position", xtensa_literal_position, 0 },
+ { "frame", s_ignore, 0 }, /* Formerly used for STABS debugging. */
+ { "long", xtensa_elf_cons, 4 },
+ { "word", xtensa_elf_cons, 4 },
+ { "short", xtensa_elf_cons, 2 },
+ { "begin", xtensa_begin_directive, 0 },
+ { "end", xtensa_end_directive, 0 },
+ { "loc", xtensa_dwarf2_directive_loc, 0 },
+ { "literal", xtensa_literal_pseudo, 0 },
+ { "frequency", xtensa_frequency_pseudo, 0 },
+ { NULL, 0, 0 },
};
-bfd_boolean
-use_generics ()
-{
- return directive_state[directive_generics];
-}
-
-
-bfd_boolean
-use_longcalls ()
-{
- return directive_state[directive_longcalls];
-}
-
-
-bfd_boolean
-code_density_available ()
+static bfd_boolean
+use_transform (void)
{
- return directive_state[directive_density];
+ /* After md_end, you should be checking frag by frag, rather
+ than state directives. */
+ assert (!past_xtensa_end);
+ return directive_state[directive_transform];
}
-bfd_boolean
-can_relax ()
+static bfd_boolean
+do_align_targets (void)
{
- return use_generics ();
+ /* Do not use this function after md_end; just look at align_targets
+ instead. There is no target-align directive, so alignment is either
+ enabled for all frags or not done at all. */
+ assert (!past_xtensa_end);
+ return align_targets && use_transform ();
}
static void
-directive_push (directive, negated, datum)
- directiveE directive;
- bfd_boolean negated;
- const void *datum;
+directive_push (directiveE directive, bfd_boolean negated, const void *datum)
{
char *file;
unsigned int line;
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;
+directive_pop (directiveE *directive,
+ bfd_boolean *negated,
+ const char **file,
+ unsigned int *line,
+ const void **datum)
{
state_stackS *top = directive_state_stack;
static void
-directive_balance ()
+directive_balance (void)
{
while (directive_state_stack)
{
static bfd_boolean
-inside_directive (dir)
- directiveE dir;
+inside_directive (directiveE dir)
{
state_stackS *top = directive_state_stack;
static void
-get_directive (directive, negated)
- directiveE *directive;
- bfd_boolean *negated;
+get_directive (directiveE *directive, bfd_boolean *negated)
{
int len;
unsigned i;
+ char *directive_string;
if (strncmp (input_line_pointer, "no-", 3) != 0)
*negated = FALSE;
}
len = strspn (input_line_pointer,
- "abcdefghijklmnopqrstuvwxyz_/0123456789.");
+ "abcdefghijklmnopqrstuvwxyz_-/0123456789.");
+
+ /* This code is a hack to make .begin [no-][generics|relax] exactly
+ equivalent to .begin [no-]transform. We should remove it when
+ we stop accepting those options. */
+
+ if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
+ {
+ as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
+ directive_string = "transform";
+ }
+ else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
+ {
+ as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
+ directive_string = "transform";
+ }
+ else
+ directive_string = input_line_pointer;
for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
{
- if (strncmp (input_line_pointer, directive_info[i].name, len) == 0)
+ if (strncmp (directive_string, directive_info[i].name, len) == 0)
{
input_line_pointer += len;
*directive = (directiveE) i;
if (*negated && !directive_info[i].can_be_negated)
- as_bad (_("directive %s can't be negated"),
+ as_bad (_("directive %s cannot be negated"),
directive_info[i].name);
return;
}
static void
-xtensa_begin_directive (ignore)
- int ignore ATTRIBUTE_UNUSED;
+xtensa_begin_directive (int ignore ATTRIBUTE_UNUSED)
{
directiveE directive;
bfd_boolean negated;
int len;
lit_state *ls;
- md_flush_pending_output ();
-
get_directive (&directive, &negated);
if (directive == (directiveE) XTENSA_UNDEFINED)
{
return;
}
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
switch (directive)
{
case directive_literal:
saved_insn_labels = insn_labels;
insn_labels = NULL;
}
+ as_warn (_(".begin literal is deprecated; use .literal instead"));
state = (emit_state *) xmalloc (sizeof (emit_state));
xtensa_switch_to_literal_fragment (state);
directive_push (directive_literal, negated, state);
break;
case directive_literal_prefix:
+ /* Have to flush pending output because a movi relaxed to an l32r
+ might produce a literal. */
+ md_flush_pending_output ();
/* 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)
+ if (generating_literals)
{
as_bad (_("cannot set literal_prefix inside literal fragment"));
return;
directive_push (directive_freeregs, negated, 0);
break;
+ case directive_schedule:
+ md_flush_pending_output ();
+ frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ directive_push (directive_schedule, negated, 0);
+ xtensa_set_frag_assembly_state (frag_now);
+ break;
+
case directive_density:
- if (!density_supported && !negated)
+ as_warn (_(".begin [no-]density is ignored"));
+ break;
+
+ case directive_absolute_literals:
+ md_flush_pending_output ();
+ if (!absolute_literals_supported && !negated)
{
- as_warn (_("Xtensa density option not supported; ignored"));
+ as_warn (_("Xtensa absolute literals option not supported; ignored"));
break;
}
- /* fall through */
+ xtensa_set_frag_assembly_state (frag_now);
+ directive_push (directive, negated, 0);
+ break;
default:
+ md_flush_pending_output ();
+ xtensa_set_frag_assembly_state (frag_now);
directive_push (directive, negated, 0);
break;
}
static void
-xtensa_end_directive (ignore)
- int ignore ATTRIBUTE_UNUSED;
+xtensa_end_directive (int ignore ATTRIBUTE_UNUSED)
{
directiveE begin_directive, end_directive;
bfd_boolean begin_negated, end_negated;
const char *file;
unsigned int line;
emit_state *state;
+ emit_state **state_ptr;
lit_state *s;
- md_flush_pending_output ();
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
get_directive (&end_directive, &end_negated);
- if (end_directive == (directiveE) XTENSA_UNDEFINED)
+
+ md_flush_pending_output ();
+
+ switch (end_directive)
{
+ case (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"));
+ case directive_density:
+ as_warn (_(".end [no-]density is ignored"));
demand_empty_rest_of_line ();
- return;
+ break;
+
+ case directive_absolute_literals:
+ if (!absolute_literals_supported && !end_negated)
+ {
+ as_warn (_("Xtensa absolute literals option not supported; ignored"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ break;
+
+ default:
+ break;
}
+ state_ptr = &state; /* use state_ptr to avoid type-punning warning */
directive_pop (&begin_directive, &begin_negated, &file, &line,
- (const void **) &state);
+ (const void **) state_ptr);
if (begin_directive != directive_none)
{
case directive_literal:
frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_restore_emit_state (state);
+ xtensa_set_frag_assembly_state (frag_now);
free (state);
if (!inside_directive (directive_literal))
{
}
break;
- case directive_freeregs:
- break;
-
case directive_literal_prefix:
/* Restore the default collection sections from saved state. */
s = (lit_state *) state;
free (s);
break;
+ case directive_schedule:
+ case directive_freeregs:
+ break;
+
default:
+ xtensa_set_frag_assembly_state (frag_now);
break;
}
}
}
-/* Place an aligned literal fragment at the current location. */
+/* Wrap dwarf2 functions so that we correctly support the .loc directive. */
+
+static bfd_boolean xtensa_loc_directive_seen = FALSE;
static void
-xtensa_literal_position (ignore)
- int ignore ATTRIBUTE_UNUSED;
+xtensa_dwarf2_directive_loc (int x)
{
- 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 ();
+ xtensa_loc_directive_seen = TRUE;
+ dwarf2_directive_loc (x);
}
-/* Support .literal label, value@plt + offset. */
+static void
+xtensa_dwarf2_emit_insn (int size, struct dwarf2_line_info *loc)
+{
+ if (debug_type != DEBUG_DWARF2 && ! xtensa_loc_directive_seen)
+ return;
+ xtensa_loc_directive_seen = FALSE;
+ dwarf2_gen_line_info (frag_now_fix () - size, loc);
+}
+
+
+/* Place an aligned literal fragment at the current location. */
+
+static void
+xtensa_literal_position (int ignore ATTRIBUTE_UNUSED)
+{
+ md_flush_pending_output ();
+
+ if (inside_directive (directive_literal))
+ as_warn (_(".literal_position inside literal directive; ignoring"));
+ xtensa_mark_literal_pool_location ();
+
+ demand_empty_rest_of_line ();
+ xtensa_clear_insn_labels ();
+}
+
+
+/* Support .literal label, expr, ... */
static void
-xtensa_literal_pseudo (ignored)
- int ignored ATTRIBUTE_UNUSED;
+xtensa_literal_pseudo (int ignored ATTRIBUTE_UNUSED)
{
emit_state state;
char *p, *base_name;
char c;
- expressionS expP;
segT dest_seg;
if (inside_directive (directive_literal))
return;
}
+ md_flush_pending_output ();
+
/* Previous labels go with whatever follows this directive, not with
the literal, so save them now. */
saved_insn_labels = insn_labels;
xtensa_switch_to_literal_fragment (&state);
- /* ...but if we aren't using text-section-literals, then we
+ /* ...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)
+ if (use_literal_section || directive_state[directive_absolute_literals])
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); */
+ /* All literals are aligned to four-byte boundaries. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
c = get_symbol_end ();
/* Just after name is now '\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;
+ input_line_pointer++; /* skip ',' or ':' */
- demand_empty_rest_of_line ();
+ xtensa_elf_cons (4);
xtensa_restore_emit_state (&state);
static void
-xtensa_literal_prefix (start, len)
- char const *start;
- int len;
+xtensa_literal_prefix (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;
+ char *name, *linkonce_suffix;
+ char *newname, *newname4;
+ size_t linkonce_len;
/* Get a null-terminated copy of the name. */
name = xmalloc (len + 1);
/* 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");
+ newname4 = xmalloc (len + strlen (".lit4") + 1);
+
+ linkonce_len = sizeof (".gnu.linkonce.") - 1;
+ if (strncmp (name, ".gnu.linkonce.", linkonce_len) == 0
+ && (linkonce_suffix = strchr (name + linkonce_len, '.')) != 0)
+ {
+ strcpy (newname, ".gnu.linkonce.literal");
+ strcpy (newname4, ".gnu.linkonce.lit4");
+
+ strcat (newname, linkonce_suffix);
+ strcat (newname4, linkonce_suffix);
+ }
+ else
+ {
+ int suffix_pos = len;
- /* Note that retrieve_literal_seg does not create a segment if
+ /* If the section name ends with ".text", then replace that suffix
+ instead of appending an additional suffix. */
+ if (len >= 5 && strcmp (name + len - 5, ".text") == 0)
+ suffix_pos -= 5;
+
+ strcpy (newname, name);
+ strcpy (newname4, name);
+
+ strcpy (newname + suffix_pos, ".literal");
+ strcpy (newname4 + suffix_pos, ".lit4");
+ }
+
+ /* Note that cache_literal_section does not create a segment if
it already exists. */
- default_lit_sections.lit_seg = NULL; /* retrieved on demand */
+ default_lit_sections.lit_seg = NULL;
+ default_lit_sections.lit4_seg = NULL;
/* Canonicalizing section names allows renaming literal
sections to occur correctly. */
- default_lit_sections.lit_seg_name =
- tc_canonicalize_symbol_name (newname);
+ default_lit_sections.lit_seg_name = tc_canonicalize_symbol_name (newname);
+ default_lit_sections.lit4_seg_name = tc_canonicalize_symbol_name (newname4);
free (name);
+}
+
+
+/* Support ".frequency branch_target_frequency fall_through_frequency". */
+
+static void
+xtensa_frequency_pseudo (int ignored ATTRIBUTE_UNUSED)
+{
+ float fall_through_f, target_f;
+
+ fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
+ if (fall_through_f < 0)
+ {
+ as_bad (_("fall through frequency must be greater than 0"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ target_f = (float) strtod (input_line_pointer, &input_line_pointer);
+ if (target_f < 0)
+ {
+ as_bad (_("branch target frequency must be greater than 0"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ set_subseg_freq (now_seg, now_subseg, target_f + fall_through_f, target_f);
+
+ demand_empty_rest_of_line ();
+}
- /* Restore the current section and subsection and set the
- generation into the old segment. */
- subseg_set (s_now, ss_now);
+
+/* Like normal .long/.short/.word, except support @plt, etc.
+ Clobbers input_line_pointer, checks end-of-line. */
+
+static void
+xtensa_elf_cons (int nbytes)
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ md_flush_pending_output ();
+
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
+ != BFD_RELOC_NONE))
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput, reloc);
+
+ if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
+ as_bad (_("unsupported relocation"));
+ else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
+ || (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
+ as_bad (_("opcode-specific %s relocation used outside "
+ "an instruction"), reloc_howto->name);
+ else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
+ as_bad (_("%s relocations do not fit in %d bytes"),
+ reloc_howto->name, nbytes);
+ else
+ {
+ char *p = frag_more ((int) nbytes);
+ xtensa_set_frag_assembly_state (frag_now);
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ nbytes, &exp, 0, reloc);
+ }
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
}
\f
/* Parsing and Idiom Translation. */
+/* Parse @plt, etc. and return the desired relocation. */
+static bfd_reloc_code_real_type
+xtensa_elf_suffix (char **str_p, expressionS *exp_p)
+{
+ struct map_bfd
+ {
+ char *string;
+ int length;
+ bfd_reloc_code_real_type reloc;
+ };
+
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ struct map_bfd *ptr;
+
+#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
+
+ static struct map_bfd mapping[] =
+ {
+ MAP ("l", BFD_RELOC_LO16),
+ MAP ("h", BFD_RELOC_HI16),
+ MAP ("plt", BFD_RELOC_XTENSA_PLT),
+ { (char *) 0, 0, BFD_RELOC_UNUSED }
+ };
+
+ if (*str++ != '@')
+ return BFD_RELOC_NONE;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->string[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->string, ptr->length) == 0)
+ {
+ /* Now check for "identifier@suffix+constant". */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+
+ *str_p = str;
+ return ptr->reloc;
+ }
+
+ return BFD_RELOC_UNUSED;
+}
+
+
static const char *
-expression_end (name)
- const char *name;
+expression_end (const char *name)
{
while (1)
{
switch (*name)
{
+ case '}':
case ';':
case '\0':
case ',':
+ case ':':
return name;
case ' ':
case '\t':
#define ERROR_REG_NUM ((unsigned) -1)
static unsigned
-tc_get_register (prefix)
- const char *prefix;
+tc_get_register (const char *prefix)
{
unsigned reg;
const char *next_expr;
}
-#define PLT_SUFFIX "@PLT"
-#define plt_suffix "@plt"
-
static void
-expression_maybe_register (opnd, tok)
- xtensa_operand opnd;
- expressionS *tok;
+expression_maybe_register (xtensa_opcode opc, int opnd, expressionS *tok)
{
- char *kind = xtensa_operand_kind (opnd);
+ xtensa_isa isa = xtensa_default_isa;
- if ((strlen (kind) == 1)
- && (*kind == 'l' || *kind == 'L' || *kind == 'i' || *kind == 'r'))
+ /* Check if this is an immediate operand. */
+ if (xtensa_operand_is_register (isa, opc, opnd) == 0)
{
+ bfd_reloc_code_real_type reloc;
segT t = expression (tok);
- if (t == absolute_section && operand_is_pcrel_label (opnd))
+ if (t == absolute_section
+ && xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
{
assert (tok->X_op == O_constant);
tok->X_op = O_symbol;
tok->X_add_symbol = &abs_symbol;
}
- if (tok->X_op == O_symbol
- && (!strncmp (input_line_pointer, PLT_SUFFIX,
- strlen (PLT_SUFFIX) - 1)
- || !strncmp (input_line_pointer, plt_suffix,
- strlen (plt_suffix) - 1)))
+
+ if ((tok->X_op == O_constant || tok->X_op == O_symbol)
+ && (reloc = xtensa_elf_suffix (&input_line_pointer, tok))
+ && (reloc != BFD_RELOC_NONE))
{
- symbol_get_tc (tok->X_add_symbol)->plt = 1;
- input_line_pointer += strlen (plt_suffix);
+ switch (reloc)
+ {
+ default:
+ case BFD_RELOC_UNUSED:
+ as_bad (_("unsupported relocation"));
+ break;
+
+ case BFD_RELOC_XTENSA_PLT:
+ tok->X_op = O_pltrel;
+ break;
+
+ case BFD_RELOC_LO16:
+ if (tok->X_op == O_constant)
+ tok->X_add_number &= 0xffff;
+ else
+ tok->X_op = O_lo16;
+ break;
+
+ case BFD_RELOC_HI16:
+ if (tok->X_op == O_constant)
+ tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
+ else
+ tok->X_op = O_hi16;
+ break;
+ }
}
}
else
{
- unsigned reg = tc_get_register (kind);
+ xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
+ unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
if (reg != ERROR_REG_NUM) /* Already errored */
{
uint32 buf = reg;
- if ((xtensa_operand_encode (opnd, &buf) != xtensa_encode_result_ok)
- || (reg != xtensa_operand_decode (opnd, buf)))
+ if (xtensa_operand_encode (isa, opc, opnd, &buf))
as_bad (_("register number out of range"));
}
/* Split up the arguments for an opcode or pseudo-op. */
static int
-tokenize_arguments (args, str)
- char **args;
- char *str;
+tokenize_arguments (char **args, char *str)
{
char *old_input_line_pointer;
bfd_boolean saw_comma = FALSE;
bfd_boolean saw_arg = FALSE;
+ bfd_boolean saw_colon = FALSE;
int num_args = 0;
char *arg_end, *arg;
int arg_len;
-
- /* Save and restore input_line_pointer around this function. */
+
+ /* Save and restore input_line_pointer around this function. */
old_input_line_pointer = input_line_pointer;
input_line_pointer = str;
switch (*input_line_pointer)
{
case '\0':
+ case '}':
goto fini;
+ case ':':
+ input_line_pointer++;
+ if (saw_comma || saw_colon || !saw_arg)
+ goto err;
+ saw_colon = TRUE;
+ break;
+
case ',':
input_line_pointer++;
- if (saw_comma || !saw_arg)
+ if (saw_comma || saw_colon || !saw_arg)
goto err;
saw_comma = TRUE;
break;
default:
- if (!saw_comma && saw_arg)
+ if (!saw_comma && !saw_colon && saw_arg)
goto err;
arg_end = input_line_pointer + 1;
while (!expression_end (arg_end))
arg_end += 1;
-
+
arg_len = arg_end - input_line_pointer;
- arg = (char *) xmalloc (arg_len + 1);
+ arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
args[num_args] = arg;
+ if (saw_colon)
+ *arg++ = ':';
strncpy (arg, input_line_pointer, arg_len);
arg[arg_len] = '\0';
-
+
input_line_pointer = arg_end;
num_args += 1;
saw_comma = FALSE;
+ saw_colon = FALSE;
saw_arg = TRUE;
break;
}
}
fini:
- if (saw_comma)
+ if (saw_comma || saw_colon)
goto err;
input_line_pointer = old_input_line_pointer;
return num_args;
err:
+ if (saw_comma)
+ as_bad (_("extra comma"));
+ else if (saw_colon)
+ as_bad (_("extra colon"));
+ else if (!saw_arg)
+ as_bad (_("missing argument"));
+ else
+ as_bad (_("missing comma or colon"));
input_line_pointer = old_input_line_pointer;
return -1;
}
-/* Parse the arguments to an opcode. Return true on error. */
+/* 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;
+parse_arguments (TInsn *insn, int num_args, char **arg_strings)
{
- expressionS *tok = insn->tok;
+ expressionS *tok, *last_tok;
xtensa_opcode opcode = insn->opcode;
bfd_boolean had_error = TRUE;
- xtensa_isa isa = xtensa_default_isa;
- int n;
+ xtensa_isa isa = xtensa_default_isa;
+ int n, num_regs = 0;
int opcode_operand_count;
- int actual_operand_count = 0;
- xtensa_operand opnd = NULL;
+ int opnd_cnt, last_opnd_cnt;
+ unsigned int next_reg = 0;
char *old_input_line_pointer;
if (insn->insn_type == ITYPE_LITERAL)
opcode_operand_count = 1;
else
- opcode_operand_count = xtensa_num_operands (isa, opcode);
+ opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
+ tok = insn->tok;
memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
/* Save and restore input_line_pointer around this function. */
- old_input_line_pointer = input_line_pointer;
+ old_input_line_pointer = input_line_pointer;
+
+ last_tok = 0;
+ last_opnd_cnt = -1;
+ opnd_cnt = 0;
+
+ /* Skip invisible operands. */
+ while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
+ {
+ opnd_cnt += 1;
+ tok++;
+ }
for (n = 0; n < num_args; n++)
- {
+ {
input_line_pointer = arg_strings[n];
+ if (*input_line_pointer == ':')
+ {
+ xtensa_regfile opnd_rf;
+ input_line_pointer++;
+ if (num_regs == 0)
+ goto err;
+ assert (opnd_cnt > 0);
+ num_regs--;
+ opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
+ if (next_reg
+ != tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
+ as_warn (_("incorrect register number, ignoring"));
+ next_reg++;
+ }
+ else
+ {
+ if (opnd_cnt >= opcode_operand_count)
+ {
+ as_warn (_("too many arguments"));
+ goto err;
+ }
+ assert (opnd_cnt < MAX_INSN_ARGS);
+
+ expression_maybe_register (opcode, opnd_cnt, tok);
+ next_reg = tok->X_add_number + 1;
- if (actual_operand_count >= opcode_operand_count)
- {
- as_warn (_("too many arguments"));
- goto err;
- }
- assert (actual_operand_count < MAX_INSN_ARGS);
+ if (tok->X_op == O_illegal || tok->X_op == O_absent)
+ goto err;
+ if (xtensa_operand_is_register (isa, opcode, opnd_cnt) == 1)
+ {
+ num_regs = xtensa_operand_num_regs (isa, opcode, opnd_cnt) - 1;
+ /* minus 1 because we are seeing one right now */
+ }
+ else
+ num_regs = 0;
- opnd = xtensa_get_operand (isa, opcode, actual_operand_count);
- expression_maybe_register (opnd, tok);
+ last_tok = tok;
+ last_opnd_cnt = opnd_cnt;
+
+ do
+ {
+ opnd_cnt += 1;
+ tok++;
+ }
+ while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
+ }
+ }
- if (tok->X_op == O_illegal || tok->X_op == O_absent)
- goto err;
- actual_operand_count++;
- tok++;
- }
+ if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
+ goto err;
insn->ntok = tok - insn->tok;
had_error = FALSE;
err:
- input_line_pointer = old_input_line_pointer;
+ input_line_pointer = old_input_line_pointer;
return had_error;
}
+static int
+get_invisible_operands (TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_format fmt;
+ xtensa_opcode opc = insn->opcode;
+ int slot, opnd, fmt_found;
+ unsigned val;
+
+ if (!slotbuf)
+ slotbuf = xtensa_insnbuf_alloc (isa);
+
+ /* Find format/slot where this can be encoded. */
+ fmt_found = 0;
+ slot = 0;
+ for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+ {
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
+ {
+ fmt_found = 1;
+ break;
+ }
+ }
+ if (fmt_found) break;
+ }
+
+ if (!fmt_found)
+ {
+ as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
+ return -1;
+ }
+
+ /* First encode all the visible operands
+ (to deal with shared field operands). */
+ for (opnd = 0; opnd < insn->ntok; opnd++)
+ {
+ if (xtensa_operand_is_visible (isa, opc, opnd) == 1
+ && (insn->tok[opnd].X_op == O_register
+ || insn->tok[opnd].X_op == O_constant))
+ {
+ val = insn->tok[opnd].X_add_number;
+ xtensa_operand_encode (isa, opc, opnd, &val);
+ xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
+ }
+ }
+
+ /* Then pull out the values for the invisible ones. */
+ for (opnd = 0; opnd < insn->ntok; opnd++)
+ {
+ if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
+ {
+ xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
+ xtensa_operand_decode (isa, opc, opnd, &val);
+ insn->tok[opnd].X_add_number = val;
+ if (xtensa_operand_is_register (isa, opc, opnd) == 1)
+ insn->tok[opnd].X_op = O_register;
+ else
+ insn->tok[opnd].X_op = O_constant;
+ }
+ }
+
+ return 0;
+}
+
+
static void
-xg_reverse_shift_count (cnt_argp)
- char **cnt_argp;
+xg_reverse_shift_count (char **cnt_argp)
{
char *cnt_arg, *new_arg;
cnt_arg = *cnt_argp;
in *valp. */
static int
-xg_arg_is_constant (arg, valp)
- char *arg;
- offsetT *valp;
+xg_arg_is_constant (char *arg, offsetT *valp)
{
expressionS exp;
char *save_ptr = input_line_pointer;
static void
-xg_replace_opname (popname, newop)
- char **popname;
- char *newop;
+xg_replace_opname (char **popname, char *newop)
{
free (*popname);
*popname = (char *) xmalloc (strlen (newop) + 1);
static int
-xg_check_num_args (pnum_args, expected_num, opname, arg_strings)
- int *pnum_args;
- int expected_num;
- char *opname;
- char **arg_strings;
+xg_check_num_args (int *pnum_args,
+ int expected_num,
+ char *opname,
+ char **arg_strings)
{
int num_args = *pnum_args;
- if (num_args < expected_num)
+ if (num_args < expected_num)
{
as_bad (_("not enough operands (%d) for '%s'; expected %d"),
num_args, opname, expected_num);
}
+/* If the register is not specified as part of the opcode,
+ then get it from the operand and move it to the opcode. */
+
static int
-xg_translate_sysreg_op (popname, pnum_args, arg_strings)
- char **popname;
- int *pnum_args;
- char **arg_strings;
+xg_translate_sysreg_op (char **popname, int *pnum_args, char **arg_strings)
{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_sysreg sr;
char *opname, *new_opname;
- offsetT val;
+ const char *sr_name;
+ int is_user, is_write;
bfd_boolean has_underbar = FALSE;
opname = *popname;
has_underbar = TRUE;
opname += 1;
}
+ is_user = (opname[1] == 'u');
+ is_write = (opname[0] == 'w');
- /* 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. */
+ /* Opname == [rw]ur or [rwx]sr... */
- if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
- return -1;
+ if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
+ return -1;
+ /* Check if the argument is a symbolic register name. */
+ sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
+ /* Handle WSR to "INTSET" as a special case. */
+ if (sr == XTENSA_UNDEFINED && is_write && !is_user
+ && !strcasecmp (arg_strings[1], "intset"))
+ sr = xtensa_sysreg_lookup_name (isa, "interrupt");
+ if (sr == XTENSA_UNDEFINED
+ || (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
+ {
+ /* Maybe it's a register number.... */
+ offsetT val;
if (!xg_arg_is_constant (arg_strings[1], &val))
{
- as_bad (_("register number for `%s' is not a constant"), opname);
+ as_bad (_("invalid register '%s' for '%s' instruction"),
+ arg_strings[1], opname);
return -1;
}
- if ((unsigned) val > 255)
+ sr = xtensa_sysreg_lookup (isa, val, is_user);
+ if (sr == XTENSA_UNDEFINED)
{
- as_bad (_("register number (%ld) for `%s' is out of range"),
+ as_bad (_("invalid register number (%ld) for '%s' instruction"),
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. */
+ sr_name = xtensa_sysreg_name (isa, sr);
+ /* Another special case for "WSR.INTSET".... */
+ if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
+ sr_name = "intset";
+ new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ sprintf (new_opname, "%s%s.%s", (has_underbar ? "_" : ""),
+ *popname, sr_name);
+ free (*popname);
+ *popname = new_opname;
+
+ return 0;
+}
+
+
+static int
+xtensa_translate_old_userreg_ops (char **popname)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_sysreg sr;
+ char *opname, *new_opname;
+ const char *sr_name;
+ bfd_boolean has_underbar = FALSE;
+
+ opname = *popname;
+ if (opname[0] == '_')
+ {
+ has_underbar = TRUE;
+ opname += 1;
+ }
+
+ sr = xtensa_sysreg_lookup_name (isa, opname + 1);
+ if (sr != XTENSA_UNDEFINED)
+ {
+ /* The new default name ("nnn") is different from the old default
+ name ("URnnn"). The old default is handled below, and we don't
+ want to recognize [RW]nnn, so do nothing if the name is the (new)
+ default. */
+ static char namebuf[10];
+ sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
+ if (strcmp (namebuf, opname + 1) == 0)
+ return 0;
+ }
+ else
+ {
+ offsetT val;
+ char *end;
+
+ /* Only continue if the reg name is "URnnn". */
+ if (opname[1] != 'u' || opname[2] != 'r')
+ return 0;
+ val = strtoul (opname + 3, &end, 10);
+ if (*end != '\0')
+ return 0;
+
+ sr = xtensa_sysreg_lookup (isa, val, 1);
+ if (sr == XTENSA_UNDEFINED)
+ {
+ as_bad (_("invalid register number (%ld) for '%s'"),
+ val, opname);
+ return -1;
+ }
+ }
+
+ /* Translate the opcode. */
+ sr_name = xtensa_sysreg_name (isa, sr);
+ new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
+ opname[0], sr_name);
+ free (*popname);
+ *popname = new_opname;
+
+ return 0;
+}
- /* 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;
+static int
+xtensa_translate_zero_immed (char *old_op,
+ char *new_op,
+ char **popname,
+ int *pnum_args,
+ char **arg_strings)
+{
+ char *opname;
+ offsetT val;
+
+ opname = *popname;
+ assert (opname[0] != '_');
+
+ if (strcmp (opname, old_op) != 0)
+ return 0;
+
+ if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
+ return -1;
+ if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
+ {
+ xg_replace_opname (popname, new_op);
+ free (arg_strings[1]);
+ arg_strings[1] = arg_strings[2];
+ arg_strings[2] = 0;
+ *pnum_args = 2;
}
return 0;
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;
+xg_translate_idioms (char **popname, int *pnum_args, char **arg_strings)
{
char *opname = *popname;
bfd_boolean has_underbar = FALSE;
+ if (cur_vinsn.inside_bundle)
+ return 0;
+
if (*opname == '_')
{
has_underbar = TRUE;
if (strcmp (opname, "mov") == 0)
{
- if (!has_underbar && code_density_available ())
+ if (use_transform () && !has_underbar && density_supported)
xg_replace_opname (popname, "mov.n");
else
{
return 0;
}
- if (strcmp (opname, "nop") == 0)
+ if (xtensa_nop_opcode == XTENSA_UNDEFINED
+ && strcmp (opname, "nop") == 0)
{
- if (!has_underbar && code_density_available ())
+ if (use_transform () && !has_underbar && density_supported)
xg_replace_opname (popname, "nop.n");
else
{
return 0;
}
- if ((opname[0] == 'r' || opname[0] == 'w')
- && opname[1] == 'u'
- && opname[2] == 'r')
+ /* Recognize [RW]UR and [RWX]SR. */
+ if ((((opname[0] == 'r' || opname[0] == 'w')
+ && (opname[1] == 'u' || opname[1] == 's'))
+ || (opname[0] == 'x' && opname[1] == 's'))
+ && opname[2] == 'r'
+ && opname[3] == '\0')
return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
+ /* Backward compatibility for RUR and WUR: Recognize [RW]UR<nnn> and
+ [RW]<name> if <name> is the non-default name of a user register. */
+ if ((opname[0] == 'r' || opname[0] == 'w')
+ && xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
+ return xtensa_translate_old_userreg_ops (popname);
- /* 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 ())
+ /* Relax branches that don't allow comparisons against an immediate value
+ of zero to the corresponding branches with implicit zero immediates. */
+ if (!has_underbar && use_transform ())
{
- 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");
+ if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
+ pnum_args, arg_strings))
+ return -1;
- else if (strcmp (opname, "movi.n") == 0)
- xg_replace_opname (popname, "movi");
+ if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
+ pnum_args, arg_strings))
+ return -1;
- 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;
- }
+ if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
+ pnum_args, arg_strings))
+ return -1;
- 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;
- }
+ if (xtensa_translate_zero_immed ("blti", "bltz", popname,
+ pnum_args, arg_strings))
+ return -1;
}
return 0;
\f
/* 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",
+ instruction may be symbolic. If there is a PC-relative operand,
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. */
+ last immediate operand, and if there are none of those, we fail and
+ return -1. */
-int
-get_relaxable_immed (opcode)
- xtensa_opcode opcode;
+static int
+get_relaxable_immed (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);
+ noperands = xtensa_opcode_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))
+ if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
+ continue;
+ if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
return opi;
- if (last_immed == -1 && operand_is_immed (operand))
+ if (last_immed == -1
+ && xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
last_immed = opi;
}
return last_immed;
}
-xtensa_opcode
-get_opcode_from_buf (buf)
- const char *buf;
+static xtensa_opcode
+get_opcode_from_buf (const char *buf, int slot)
{
static xtensa_insnbuf insnbuf = NULL;
- xtensa_opcode opcode;
+ static xtensa_insnbuf slotbuf = NULL;
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);
-}
+ xtensa_format fmt;
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
-/* 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. */
+ xtensa_insnbuf_from_chars (isa, insnbuf, (const unsigned char *) buf, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return XTENSA_UNDEFINED;
-static bfd_boolean
-is_entry_opcode (opcode)
- xtensa_opcode opcode;
-{
- if (opcode == XTENSA_UNDEFINED)
- return FALSE;
+ if (slot >= xtensa_format_num_slots (isa, fmt))
+ return XTENSA_UNDEFINED;
- return (opcode == xtensa_entry_opcode);
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
}
-/* 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. */
+#ifdef TENSILICA_DEBUG
-static bfd_boolean
-is_loop_opcode (opcode)
- xtensa_opcode opcode;
-{
- if (opcode == XTENSA_UNDEFINED)
- return FALSE;
+/* For debugging, print out the mapping of opcode numbers to opcodes. */
- return (opcode == xtensa_loop_opcode
- || opcode == xtensa_loopnez_opcode
- || opcode == xtensa_loopgtz_opcode);
-}
-
-
-static bfd_boolean
-is_the_loop_opcode (opcode)
- xtensa_opcode opcode;
+static void
+xtensa_print_insn_table (void)
{
- if (opcode == XTENSA_UNDEFINED)
- return FALSE;
+ int num_opcodes, num_operands;
+ xtensa_opcode opcode;
+ xtensa_isa isa = xtensa_default_isa;
- return (opcode == xtensa_loop_opcode);
+ num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+ for (opcode = 0; opcode < num_opcodes; opcode++)
+ {
+ int opn;
+ fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
+ num_operands = xtensa_opcode_num_operands (isa, opcode);
+ for (opn = 0; opn < num_operands; opn++)
+ {
+ if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
+ continue;
+ if (xtensa_operand_is_register (isa, opcode, opn) == 1)
+ {
+ xtensa_regfile opnd_rf =
+ xtensa_operand_regfile (isa, opcode, opn);
+ fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
+ }
+ else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
+ fputs ("[lLr] ", stderr);
+ else
+ fputs ("i ", stderr);
+ }
+ fprintf (stderr, "\n");
+ }
}
-static bfd_boolean
-is_jx_opcode (opcode)
- xtensa_opcode opcode;
+static void
+print_vliw_insn (xtensa_insnbuf vbuf)
{
- 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. */
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format f = xtensa_format_decode (isa, vbuf);
+ xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
+ int op;
-static bfd_boolean
-is_windowed_return_opcode (opcode)
- xtensa_opcode opcode;
-{
- if (opcode == XTENSA_UNDEFINED)
- return FALSE;
+ fprintf (stderr, "format = %d\n", f);
- return (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode);
+ for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
+ {
+ xtensa_opcode opcode;
+ const char *opname;
+ int operands;
+
+ xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
+ opcode = xtensa_opcode_decode (isa, f, op, sbuf);
+ opname = xtensa_opcode_name (isa, opcode);
+
+ fprintf (stderr, "op in slot %i is %s;\n", op, opname);
+ fprintf (stderr, " operands = ");
+ for (operands = 0;
+ operands < xtensa_opcode_num_operands (isa, opcode);
+ operands++)
+ {
+ unsigned int val;
+ if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
+ continue;
+ xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
+ xtensa_operand_decode (isa, opcode, operands, &val);
+ fprintf (stderr, "%d ", val);
+ }
+ fprintf (stderr, "\n");
+ }
+ xtensa_insnbuf_free (isa, sbuf);
}
+#endif /* TENSILICA_DEBUG */
-/* 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;
+is_direct_call_opcode (xtensa_opcode opcode)
{
xtensa_isa isa = xtensa_default_isa;
- int num_ops, i;
+ int n, num_operands;
- if (opcode == xtensa_j_opcode && opcode != XTENSA_UNDEFINED)
+ if (xtensa_opcode_is_call (isa, opcode) == 0)
return FALSE;
- num_ops = xtensa_num_operands (isa, opcode);
- for (i = 0; i < num_ops; i++)
+ num_operands = xtensa_opcode_num_operands (isa, opcode);
+ for (n = 0; n < num_operands; n++)
{
- xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
- if (strcmp (xtensa_operand_kind (operand), "l") == 0)
+ if (xtensa_operand_is_register (isa, opcode, n) == 0
+ && xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
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. */
+/* Convert from BFD relocation type code to slot and operand number.
+ Returns non-zero on failure. */
-bfd_boolean
-is_branch_or_jump_opcode (opcode)
- xtensa_opcode opcode;
+static int
+decode_reloc (bfd_reloc_code_real_type reloc, int *slot, bfd_boolean *is_alt)
{
- 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++)
+ if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
{
- 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;
+ *slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
+ *is_alt = FALSE;
}
- return FALSE;
+ else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
+ {
+ *slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
+ *is_alt = TRUE;
+ }
+ else
+ return -1;
+
+ return 0;
}
-/* Convert from operand numbers to BFD relocation type code.
- Return BFD_RELOC_NONE on failure. */
+/* Convert from slot number to BFD relocation type code for the
+ standard PC-relative relocations. Return BFD_RELOC_NONE on
+ failure. */
-bfd_reloc_code_real_type
-opnum_to_reloc (opnum)
- int opnum;
+static bfd_reloc_code_real_type
+encode_reloc (int slot)
{
- 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;
+ if (slot < 0 || slot > 14)
+ return BFD_RELOC_NONE;
+
+ return BFD_RELOC_XTENSA_SLOT0_OP + slot;
}
-/* Convert from BFD relocation type code to operand number.
- Return -1 on failure. */
+/* Convert from slot numbers to BFD relocation type code for the
+ "alternate" relocations. Return BFD_RELOC_NONE on failure. */
-int
-reloc_to_opnum (reloc)
- bfd_reloc_code_real_type reloc;
+static bfd_reloc_code_real_type
+encode_alt_reloc (int slot)
{
- 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;
+ if (slot < 0 || slot > 14)
+ return BFD_RELOC_NONE;
+
+ return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
}
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;
+xtensa_insnbuf_set_operand (xtensa_insnbuf slotbuf,
+ xtensa_format fmt,
+ int slot,
+ xtensa_opcode opcode,
+ int operand,
+ uint32 value,
+ const char *file,
+ unsigned int line)
+{
uint32 valbuf = value;
- encode_result = xtensa_operand_encode (operand, &valbuf);
-
- switch (encode_result)
+ if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
{
- 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 ();
+ if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
+ == 1)
+ as_bad_where ((char *) file, line,
+ _("operand %u is out of range for '%s'"), value,
+ xtensa_opcode_name (xtensa_default_isa, opcode));
+ else
+ as_bad_where ((char *) file, line,
+ _("operand %u is invalid for '%s'"), value,
+ xtensa_opcode_name (xtensa_default_isa, opcode));
+ return;
}
- xtensa_operand_set_field (operand, insnbuf, valbuf);
+ xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
+ slotbuf, valbuf);
}
static uint32
-xtensa_insnbuf_get_operand (insnbuf, opcode, opnum)
- xtensa_insnbuf insnbuf;
- xtensa_opcode opcode;
- int opnum;
+xtensa_insnbuf_get_operand (xtensa_insnbuf slotbuf,
+ xtensa_format fmt,
+ int slot,
+ 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));
+ uint32 val = 0;
+ (void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
+ fmt, slot, slotbuf, &val);
+ (void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
+ return val;
}
+\f
+/* Checks for rules from xtensa-relax tables. */
-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;
+/* The routine xg_instruction_matches_option_term must return TRUE
+ when a given option term is true. The meaning of all of the option
+ terms is given interpretation by this function. This is needed when
+ an option depends on the state of a directive, but there are no such
+ options in use right now. */
+
+static bfd_boolean
+xg_instruction_matches_option_term (TInsn *insn ATTRIBUTE_UNUSED,
+ const ReqOrOption *option)
{
- 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);
+ if (strcmp (option->option_name, "realnop") == 0
+ || strncmp (option->option_name, "IsaUse", 6) == 0)
+ {
+ /* These conditions were evaluated statically when building the
+ relaxation table. There's no need to reevaluate them now. */
+ return TRUE;
+ }
+ else
+ {
+ as_fatal (_("internal error: unknown option name '%s'"),
+ option->option_name);
+ }
}
static bfd_boolean
-is_negatable_branch (insn)
- TInsn *insn;
+xg_instruction_matches_or_options (TInsn *insn,
+ const ReqOrOptionList *or_option)
{
- xtensa_isa isa = xtensa_default_isa;
- int i;
- int num_ops = xtensa_num_operands (isa, insn->opcode);
-
- for (i = 0; i < num_ops; i++)
+ const ReqOrOption *option;
+ /* Must match each of the AND terms. */
+ for (option = or_option; option != NULL; option = option->next)
{
- xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
- char *kind = xtensa_operand_kind (opnd);
- if (strlen (kind) == 1 && *kind == 'l')
+ if (xg_instruction_matches_option_term (insn, option))
return TRUE;
}
return FALSE;
}
-\f
-/* Various Other Internal Functions. */
static bfd_boolean
-is_unique_insn_expansion (r)
- TransitionRule *r;
+xg_instruction_matches_options (TInsn *insn, const ReqOptionList *options)
{
- if (!r->to_instr || r->to_instr->next != NULL)
- return FALSE;
- if (r->to_instr->typ != INSTR_INSTR)
- return FALSE;
+ const ReqOption *req_options;
+ /* Must match each of the AND terms. */
+ for (req_options = options;
+ req_options != NULL;
+ req_options = req_options->next)
+ {
+ /* Must match one of the OR clauses. */
+ if (!xg_instruction_matches_or_options (insn,
+ req_options->or_option_terms))
+ 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);
-}
-
+/* Return the transition rule that matches or NULL if none matches. */
-static int
-xg_get_build_instr_size (insn)
- BuildInstr *insn;
+static bfd_boolean
+xg_instruction_matches_rule (TInsn *insn, TransitionRule *rule)
{
- assert (insn->typ == INSTR_INSTR);
- return xtensa_insn_length (xtensa_default_isa, insn->opcode);
-}
-
+ PreconditionList *condition_l;
-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);
+ if (rule->opcode != insn->opcode)
+ return FALSE;
- for (l = table->table[insn->opcode]; l != NULL; l = l->next)
+ for (condition_l = rule->conditions;
+ condition_l != NULL;
+ condition_l = condition_l->next)
{
- TransitionRule *rule = l->rule;
+ expressionS *exp1;
+ expressionS *exp2;
+ Precondition *cond = condition_l->precond;
- if (xg_instruction_matches_rule (insn, rule)
- && is_unique_insn_expansion (rule))
+ switch (cond->typ)
{
- /* 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))
+ 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))
{
- num_match++;
- if (num_match > 1)
- 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;
+ default:
+ 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);
+ else if (expr_is_register (exp1))
+ {
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (get_expr_register (exp1) != cond->op_data)
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (get_expr_register (exp1) == cond->op_data)
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else
+ return FALSE;
+ break;
+
+ case OP_OPERAND:
+ assert (cond->op_num < insn->ntok);
+ assert (cond->op_data < insn->ntok);
+ exp1 = &insn->tok[cond->op_num];
+ exp2 = &insn->tok[cond->op_data];
+
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (!expr_is_equal (exp1, exp2))
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (expr_is_equal (exp1, exp2))
+ return FALSE;
+ break;
+ }
+ break;
+
+ case OP_LITERAL:
+ case OP_LABEL:
+ default:
+ return FALSE;
+ }
+ }
+ if (!xg_instruction_matches_options (insn, rule->options))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static int
+transition_rule_cmp (const TransitionRule *a, const TransitionRule *b)
+{
+ bfd_boolean a_greater = FALSE;
+ bfd_boolean b_greater = FALSE;
+
+ ReqOptionList *l_a = a->options;
+ ReqOptionList *l_b = b->options;
+
+ /* We only care if they both are the same except for
+ a const16 vs. an l32r. */
+
+ while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
+ {
+ ReqOrOptionList *l_or_a = l_a->or_option_terms;
+ ReqOrOptionList *l_or_b = l_b->or_option_terms;
+ while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
+ {
+ if (l_or_a->is_true != l_or_b->is_true)
+ return 0;
+ if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
+ {
+ /* This is the case we care about. */
+ if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
+ && strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
+ {
+ if (prefer_const16)
+ a_greater = TRUE;
+ else
+ b_greater = TRUE;
+ }
+ else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
+ && strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
+ {
+ if (prefer_const16)
+ b_greater = TRUE;
+ else
+ a_greater = TRUE;
+ }
+ else
+ return 0;
+ }
+ l_or_a = l_or_a->next;
+ l_or_b = l_or_b->next;
+ }
+ if (l_or_a || l_or_b)
+ return 0;
+
+ l_a = l_a->next;
+ l_b = l_b->next;
+ }
+ if (l_a || l_b)
+ return 0;
+
+ /* Incomparable if the substitution was used differently in two cases. */
+ if (a_greater && b_greater)
+ return 0;
+
+ if (b_greater)
+ return 1;
+ if (a_greater)
+ return -1;
+
+ return 0;
+}
+
+
+static TransitionRule *
+xg_instruction_match (TInsn *insn)
+{
+ TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
+ TransitionList *l;
+ assert (insn->opcode < table->num_opcodes);
+
+ /* 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;
+}
+
+\f
+/* Various Other Internal Functions. */
+
+static bfd_boolean
+is_unique_insn_expansion (TransitionRule *r)
+{
+ if (!r->to_instr || r->to_instr->next != NULL)
+ return FALSE;
+ if (r->to_instr->typ != INSTR_INSTR)
+ return FALSE;
+ return TRUE;
+}
+
+
+static int
+xg_get_build_instr_size (BuildInstr *insn)
+{
+ assert (insn->typ == INSTR_INSTR);
+ return xg_get_single_size (insn->opcode);
+}
+
+
+static bfd_boolean
+xg_is_narrow_insn (TInsn *insn)
+{
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
+ TransitionList *l;
+ int num_match = 0;
+ assert (insn->insn_type == ITYPE_INSN);
+ assert (insn->opcode < table->num_opcodes);
for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
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))
+ /* ...and it is a larger instruction. */
+ if (xg_get_single_size (insn->opcode)
+ < xg_get_build_instr_size (rule->to_instr))
{
num_match++;
if (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;
+static bfd_boolean
+xg_is_single_relaxable_insn (TInsn *insn)
{
- /* Go ahead and compute it, but it better be 3. */
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
- int old_size = xtensa_insn_length (xtensa_default_isa, opcode);
- assert (opcode < table->num_opcodes);
+ int num_match = 0;
+ assert (insn->insn_type == ITYPE_INSN);
+ assert (insn->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)
+ for (l = table->table[insn->opcode]; l != NULL; l = l->next)
{
TransitionRule *rule = l->rule;
- /* If it only generates one instruction. */
- if (is_unique_insn_expansion (rule))
+ if (xg_instruction_matches_rule (insn, rule)
+ && is_unique_insn_expansion (rule))
{
- int new_size = xtensa_insn_length (xtensa_default_isa,
- rule->to_instr->opcode);
- if (new_size > old_size)
+ /* It only generates one instruction... */
+ assert (insn->insn_type == ITYPE_INSN);
+ /* ... and it is a larger instruction. */
+ if (xg_get_single_size (insn->opcode)
+ <= xg_get_build_instr_size (rule->to_instr))
{
- assert (new_size == 3);
- return 3;
+ num_match++;
+ if (num_match > 1)
+ return FALSE;
}
}
}
- return old_size;
+ return (num_match == 1);
}
/* Return the maximum number of bytes this opcode can expand to. */
-int
-xg_get_max_insn_widen_size (opcode)
- xtensa_opcode opcode;
+static int
+xg_get_max_insn_widen_size (xtensa_opcode opcode)
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
- int max_size = xtensa_insn_length (xtensa_default_isa, opcode);
+ int max_size = xg_get_single_size (opcode);
assert (opcode < table->num_opcodes);
switch (build_list->typ)
{
case INSTR_INSTR:
- this_size += xtensa_insn_length (xtensa_default_isa,
- build_list->opcode);
-
+ this_size += xg_get_single_size (build_list->opcode);
break;
case INSTR_LITERAL_DEF:
case INSTR_LABEL_DEF:
/* Return the maximum number of literal bytes this opcode can generate. */
-int
-xg_get_max_insn_widen_literal_size (opcode)
- xtensa_opcode opcode;
+static int
+xg_get_max_insn_widen_literal_size (xtensa_opcode opcode)
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int max_size = 0;
switch (build_list->typ)
{
case INSTR_LITERAL_DEF:
- /* hard coded 4-byte literal. */
+ /* Hard-coded 4-byte literal. */
this_size += 4;
break;
case INSTR_INSTR:
}
-bfd_boolean
-xg_is_relaxable_insn (insn, lateral_steps)
- TInsn *insn;
- int lateral_steps;
+static bfd_boolean
+xg_is_relaxable_insn (TInsn *insn, int lateral_steps)
{
int steps_taken = 0;
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
static symbolS *
-get_special_literal_symbol ()
+get_special_literal_symbol (void)
{
static symbolS *sym = NULL;
static symbolS *
-get_special_label_symbol ()
+get_special_label_symbol (void)
{
static symbolS *sym = NULL;
}
-/* Return true on success. */
-
-bfd_boolean
-xg_build_to_insn (targ, insn, bi)
- TInsn *targ;
- TInsn *insn;
- BuildInstr *bi;
+static bfd_boolean
+xg_valid_literal_expression (const expressionS *exp)
{
- BuildOp *op;
- symbolS *sym;
-
- memset (targ, 0, sizeof (TInsn));
- switch (bi->typ)
+ switch (exp->X_op)
{
- case INSTR_INSTR:
- op = bi->ops;
- targ->opcode = bi->opcode;
- targ->insn_type = ITYPE_INSN;
- targ->is_specific_opcode = FALSE;
+ case O_constant:
+ case O_symbol:
+ case O_big:
+ case O_uminus:
+ case O_subtract:
+ case O_pltrel:
+ return TRUE;
+ default:
+ return 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);
+/* This will check to see if the value can be converted into the
+ operand type. It will return TRUE if it does not fit. */
- if (targ->ntok <= op_num)
- targ->ntok = op_num + 1;
+static bfd_boolean
+xg_check_operand (int32 value, xtensa_opcode opcode, int operand)
+{
+ uint32 valbuf = value;
+ if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
+ return TRUE;
+ return FALSE;
+}
- 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);
+/* Assumes: All immeds are constants. Check that all constants fit
+ into their immeds; return FALSE if not. */
- if (targ->ntok <= op_num)
- targ->ntok = op_num + 1;
+static bfd_boolean
+xg_immeds_fit (const TInsn *insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
- 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;
+ int n = insn->ntok;
+ assert (insn->insn_type == ITYPE_INSN);
+ for (i = 0; i < n; ++i)
+ {
+ const expressionS *expr = &insn->tok[i];
+ if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+ continue;
- case INSTR_LABEL_DEF:
+ switch (expr->X_op)
+ {
+ case O_register:
+ case O_constant:
+ if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+ return FALSE;
+ break;
+
+ default:
+ /* 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 (const TInsn *insn,
+ segT pc_seg,
+ fragS *pc_frag,
+ offsetT pc_offset,
+ long stretch)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ symbolS *symbolP;
+ fragS *sym_frag;
+ offsetT target, pc;
+ uint32 new_offset;
+ int i;
+ int n = insn->ntok;
+
+ assert (insn->insn_type == ITYPE_INSN);
+
+ for (i = 0; i < n; ++i)
+ {
+ const expressionS *expr = &insn->tok[i];
+ if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
+ continue;
+
+ switch (expr->X_op)
+ {
+ case O_register:
+ case O_constant:
+ if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+ return FALSE;
+ break;
+
+ case O_lo16:
+ case O_hi16:
+ /* Check for the worst case. */
+ if (xg_check_operand (0xffff, insn->opcode, i))
+ return FALSE;
+ break;
+
+ case O_symbol:
+ /* We only allow symbols for PC-relative references.
+ If pc_frag == 0, then we don't have frag locations yet. */
+ if (pc_frag == 0
+ || xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0)
+ return FALSE;
+
+ /* If it is a weak symbol, then assume it won't reach. */
+ if (S_IS_WEAK (expr->X_add_symbol))
+ return FALSE;
+
+ if (is_direct_call_opcode (insn->opcode)
+ && ! pc_frag->tc_frag_data.use_longcalls)
+ {
+ /* If callee is undefined or in a different segment, be
+ optimistic and assume it will be in range. */
+ if (S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
+ return TRUE;
+ }
+
+ /* Only references within a segment can be known to fit in the
+ operands at assembly time. */
+ if (S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
+ return FALSE;
+
+ symbolP = expr->X_add_symbol;
+ sym_frag = symbol_get_frag (symbolP);
+ target = S_GET_VALUE (symbolP) + expr->X_add_number;
+ pc = pc_frag->fr_address + pc_offset;
+
+ /* If 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 != 0
+ && sym_frag->relax_marker != pc_frag->relax_marker
+ && S_GET_SEGMENT (symbolP) == pc_seg)
+ {
+ target += stretch;
+ }
+
+ new_offset = target;
+ xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
+ if (xg_check_operand (new_offset, insn->opcode, i))
+ return FALSE;
+ break;
+
+ default:
+ /* The symbol should have a fixup associated with it. */
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/* Return TRUE on success. */
+
+static bfd_boolean
+xg_build_to_insn (TInsn *targ, TInsn *insn, BuildInstr *bi)
+{
+ BuildOp *op;
+ symbolS *sym;
+
+ memset (targ, 0, sizeof (TInsn));
+ targ->loc = insn->loc;
+ switch (bi->typ)
+ {
+ case INSTR_INSTR:
+ op = bi->ops;
+ targ->opcode = bi->opcode;
+ targ->insn_type = ITYPE_INSN;
+ targ->is_specific_opcode = FALSE;
+
+ for (; op != NULL; op = op->next)
+ {
+ int op_num = op->op_num;
+ int op_data = op->op_data;
+
+ assert (op->op_num < MAX_INSN_ARGS);
+
+ if (targ->ntok <= op_num)
+ targ->ntok = op_num + 1;
+
+ switch (op->typ)
+ {
+ case OP_CONSTANT:
+ set_expr_const (&targ->tok[op_num], op_data);
+ break;
+ case OP_OPERAND:
+ assert (op_data < insn->ntok);
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ break;
+ case OP_LITERAL:
+ sym = get_special_literal_symbol ();
+ set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
+ break;
+ case OP_LABEL:
+ sym = get_special_label_symbol ();
+ set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
+ break;
+ case OP_OPERAND_HI16U:
+ case OP_OPERAND_LOW16U:
+ assert (op_data < insn->ntok);
+ if (expr_is_const (&insn->tok[op_data]))
+ {
+ long val;
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ val = xg_apply_userdef_op_fn (op->typ,
+ targ->tok[op_num].
+ X_add_number);
+ targ->tok[op_num].X_add_number = val;
+ }
+ else
+ {
+ /* For const16 we can create relocations for these. */
+ if (targ->opcode == XTENSA_UNDEFINED
+ || (targ->opcode != xtensa_const16_opcode))
+ return FALSE;
+ assert (op_data < insn->ntok);
+ /* Need to build a O_lo16 or O_hi16. */
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ if (targ->tok[op_num].X_op == O_symbol)
+ {
+ if (op->typ == OP_OPERAND_HI16U)
+ targ->tok[op_num].X_op = O_hi16;
+ else if (op->typ == OP_OPERAND_LOW16U)
+ targ->tok[op_num].X_op = O_lo16;
+ else
+ return FALSE;
+ }
+ }
+ break;
+ default:
+ /* 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);
+ /* We can only pass resolvable literals through. */
+ if (!xg_valid_literal_expression (&insn->tok[op_data]))
+ return FALSE;
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ break;
+ case OP_LITERAL:
+ case OP_CONSTANT:
+ case OP_LABEL:
+ default:
+ assert (0);
+ break;
+ }
+ }
+ break;
+
+ case INSTR_LABEL_DEF:
op = bi->ops;
targ->opcode = XTENSA_UNDEFINED;
targ->insn_type = ITYPE_LABEL;
targ->is_specific_opcode = FALSE;
- /* Literal with no ops. is a label? */
+ /* Literal with no ops is a label? */
assert (op == NULL);
break;
}
-/* Return true on success. */
+/* Return TRUE on success. */
-bfd_boolean
-xg_build_to_stack (istack, insn, bi)
- IStack *istack;
- TInsn *insn;
- BuildInstr *bi;
+static bfd_boolean
+xg_build_to_stack (IStack *istack, TInsn *insn, BuildInstr *bi)
{
for (; bi != NULL; bi = bi->next)
{
}
-/* Return true on valid expansion. */
+/* Return TRUE on valid expansion. */
-bfd_boolean
-xg_expand_to_stack (istack, insn, lateral_steps)
- IStack *istack;
- TInsn *insn;
- int lateral_steps;
+static bfd_boolean
+xg_expand_to_stack (IStack *istack, TInsn *insn, int lateral_steps)
{
int stack_size = istack->ninsn;
int steps_taken = 0;
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
}
-bfd_boolean
-xg_expand_narrow (targ, insn)
- TInsn *targ;
- TInsn *insn;
+static bfd_boolean
+xg_expand_narrow (TInsn *targ, TInsn *insn)
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
&& is_unique_insn_expansion (rule))
{
/* Is it a larger instruction? */
- if (xg_get_insn_size (insn)
+ if (xg_get_single_size (insn->opcode)
<= xg_get_build_instr_size (rule->to_instr))
{
xg_build_to_insn (targ, insn, rule->to_instr);
return TRUE;
}
+\f
+/* Relax the assembly instruction at least "min_steps".
+ Return the number of steps taken. */
-/* 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;
+static int
+xg_assembly_relax (IStack *istack,
+ TInsn *insn,
+ segT pc_seg,
+ fragS *pc_frag, /* if pc_frag == 0, not pc-relative */
+ offsetT pc_offset, /* offset in fragment */
+ int min_steps, /* minimum conversion steps */
+ long stretch) /* number of bytes stretched so far */
{
- int i;
+ int steps_taken = 0;
- 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;
+ /* 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. */
- 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;
- }
+ 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;
}
- return TRUE;
-}
+ current_insn = *insn;
+ /* Walk through all of the single instruction expansions. */
+ while (xg_is_single_relaxable_insn (¤t_insn))
+ {
+ int error_val = xg_expand_narrow (&single_target, ¤t_insn);
-/* 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;
-}
-
-\f
-/* 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 (¤t_insn, insn);
-
- /* Walk through all of the single instruction expansions. */
- while (xg_is_single_relaxable_insn (¤t_insn))
- {
- int error_val = xg_expand_narrow (&single_target, ¤t_insn);
-
- assert (!error_val);
+ assert (!error_val);
if (xg_symbolic_immeds_fit (&single_target, pc_seg, pc_frag, pc_offset,
stretch))
return steps_taken;
}
}
- tinsn_copy (¤t_insn, &single_target);
+ current_insn = single_target;
}
/* Now check for a multi-instruction expansion. */
static void
-xg_force_frag_space (size)
- int size;
+xg_force_frag_space (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"
}
-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;
+static void
+xg_finish_frag (char *last_insn,
+ enum xtensa_relax_statesE frag_state,
+ enum xtensa_relax_statesE slot0_state,
+ int max_growth,
+ bfd_boolean is_insn)
{
/* 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
beginning of the growth area. */
fragS *old_frag;
+
xg_force_frag_space (max_growth);
old_frag = frag_now;
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);
+ frag_state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
+
+ old_frag->tc_frag_data.slot_subtypes[0] = slot0_state;
+ xtensa_set_frag_assembly_state (frag_now);
/* Just to make sure that we did not split it up. */
assert (old_frag->fr_next == frag_now);
}
+/* Return TRUE if the target frag is one of the next non-empty frags. */
+
+static bfd_boolean
+is_next_frag_target (const fragS *fragP, const fragS *target)
+{
+ if (fragP == NULL)
+ return FALSE;
+
+ for (; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP == target)
+ return TRUE;
+ if (fragP->fr_fix != 0)
+ return FALSE;
+ if (fragP->fr_type == rs_fill && fragP->fr_offset != 0)
+ return FALSE;
+ if ((fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
+ && ((fragP->fr_address % (1 << fragP->fr_offset)) != 0))
+ return FALSE;
+ if (fragP->fr_type == rs_space)
+ return FALSE;
+ }
+ return FALSE;
+}
+
+
static bfd_boolean
-is_branch_jmp_to_next (insn, fragP)
- TInsn *insn;
- fragS *fragP;
+is_branch_jmp_to_next (TInsn *insn, fragS *fragP)
{
xtensa_isa isa = xtensa_default_isa;
int i;
- int num_ops = xtensa_num_operands (isa, insn->opcode);
+ int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
int target_op = -1;
symbolS *sym;
fragS *target_frag;
- if (is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_branch (isa, insn->opcode) == 0
+ && xtensa_opcode_is_jump (isa, insn->opcode) == 0)
return FALSE;
for (i = 0; i < num_ops; i++)
{
- xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
- char *kind = xtensa_operand_kind (opnd);
- if (strlen (kind) == 1 && *kind == 'l')
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
{
target_op = i;
break;
static void
-xg_add_branch_and_loop_targets (insn)
- TInsn *insn;
+xg_add_branch_and_loop_targets (TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
- int num_ops = xtensa_num_operands (isa, insn->opcode);
+ int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
- if (is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
{
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;
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
+ && insn->tok[i].X_op == O_symbol)
+ symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
return;
}
- /* 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)
+ if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
+ || xtensa_opcode_is_loop (isa, insn->opcode) == 1)
{
int i;
for (i = 0; i < insn->ntok && i < num_ops; i++)
{
- xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
- char *kind = xtensa_operand_kind (opnd);
- if (strlen (kind) == 1 && *kind == 'l'
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
&& insn->tok[i].X_op == O_symbol)
{
symbolS *sym = insn->tok[i].X_add_symbol;
}
-/* 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. */
+/* 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;
+static bfd_boolean
+xg_build_token_insn (BuildInstr *instr_spec, TInsn *old_insn, TInsn *new_insn)
{
int num_ops = 0;
BuildOp *b_op;
new_insn->insn_type = ITYPE_INSN;
new_insn->opcode = instr_spec->opcode;
new_insn->is_specific_opcode = FALSE;
+ new_insn->loc = old_insn->loc;
break;
case INSTR_LITERAL_DEF:
new_insn->insn_type = ITYPE_LITERAL;
new_insn->opcode = XTENSA_UNDEFINED;
new_insn->is_specific_opcode = FALSE;
+ new_insn->loc = old_insn->loc;
break;
case INSTR_LABEL_DEF:
as_bad (_("INSTR_LABEL_DEF not supported yet"));
}
-/* Return true if it was simplified. */
+/* Return TRUE if it was simplified. */
-bfd_boolean
-xg_simplify_insn (old_insn, new_insn)
- TInsn *old_insn;
- TInsn *new_insn;
+static bfd_boolean
+xg_simplify_insn (TInsn *old_insn, TInsn *new_insn)
{
- TransitionRule *rule = xg_instruction_match (old_insn);
+ TransitionRule *rule;
BuildInstr *insn_spec;
+
+ if (old_insn->is_specific_opcode || !density_supported)
+ return FALSE;
+
+ rule = xg_instruction_match (old_insn);
if (rule == NULL)
return FALSE;
/* 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. */
+ tokens into the stack or relax it and 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;
+xg_expand_assembly_insn (IStack *istack, TInsn *orig_insn)
{
int noperands;
TInsn new_insn;
+ bfd_boolean do_expand;
+
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;
- }
+ /* Narrow it if we can. xg_simplify_insn now does all the
+ appropriate checking (e.g., for the density option). */
+ if (xg_simplify_insn (orig_insn, &new_insn))
+ orig_insn = &new_insn;
- noperands = xtensa_num_operands (xtensa_default_isa, orig_insn->opcode);
+ noperands = xtensa_opcode_num_operands (xtensa_default_isa,
+ orig_insn->opcode);
if (orig_insn->ntok < noperands)
{
as_bad (_("found %d operands for '%s': Expected %d"),
xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
noperands);
- /* If there are not enough operands, we will assert above. If there
+ /* 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 the instruction will definitely need to be relaxed, it is better
+ to expand it now for better scheduling. Decide whether to expand
+ now.... */
+ do_expand = (!orig_insn->is_specific_opcode && use_transform ());
+
+ /* Calls should be expanded to longcalls only in the backend relaxation
+ so that the assembly scheduler will keep the L32R/CALLX instructions
+ adjacent. */
+ if (is_direct_call_opcode (orig_insn->opcode))
+ do_expand = FALSE;
if (tinsn_has_symbolic_operands (orig_insn))
{
- if (tinsn_has_complex_operands (orig_insn))
- 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);
+ /* The values of symbolic operands are not known yet, so only expand
+ now if an operand is "complex" (e.g., difference of symbols) and
+ will have to be stored as a literal regardless of the value. */
+ if (!tinsn_has_complex_operands (orig_insn))
+ do_expand = FALSE;
}
+ else if (xg_immeds_fit (orig_insn))
+ do_expand = FALSE;
-#if 0
- for (i = 0; i < istack->ninsn; i++)
- {
- if (xg_simplify_insn (&new_insn, &istack->insn[i]))
- istack->insn[i] = new_insn;
- }
-#endif
+ if (do_expand)
+ xg_assembly_relax (istack, orig_insn, 0, 0, 0, 0, 0);
+ else
+ istack_push (istack, orig_insn);
return FALSE;
}
-/* Currently all literals that are generated here are 32-bit L32R targets. */
+/* Return TRUE if the section flags are marked linkonce
+ or the name is .gnu.linkonce*. */
-symbolS *
-xg_assemble_literal (insn)
- /* const */ TInsn *insn;
+static bfd_boolean
+get_is_linkonce_section (bfd *abfd ATTRIBUTE_UNUSED, segT sec)
{
- emit_state state;
- symbolS *lit_sym = NULL;
+ flagword flags, link_once_flags;
- /* 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);
+ flags = bfd_get_section_flags (abfd, sec);
+ link_once_flags = (flags & SEC_LINK_ONCE);
- 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;
+ /* Flags might not be set yet. */
+ if (!link_once_flags)
+ {
+ static size_t len = sizeof ".gnu.linkonce.t.";
- /* Go back. */
- xtensa_restore_emit_state (&state);
- return lit_sym;
+ if (strncmp (segment_name (sec), ".gnu.linkonce.t.", len - 1) == 0)
+ link_once_flags = SEC_LINK_ONCE;
+ }
+ return (link_once_flags != 0);
}
static void
-xg_assemble_literal_space (size)
- /* const */ int size;
+xtensa_add_literal_sym (symbolS *sym)
{
- 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);
+ sym_list *l;
- /* Go back. */
- xtensa_restore_emit_state (&state);
- frag_now->tc_frag_data.literal_frag = lit_saved_frag;
+ l = (sym_list *) xmalloc (sizeof (sym_list));
+ l->sym = sym;
+ l->next = literal_syms;
+ literal_syms = l;
}
-symbolS *
-xtensa_create_literal_symbol (sec, frag)
- segT sec;
- fragS *frag;
+static symbolS *
+xtensa_create_literal_symbol (segT sec, fragS *frag)
{
static int lit_num = 0;
static char name[256];
}
-static void
-xtensa_add_literal_sym (sym)
- symbolS *sym;
+/* Currently all literals that are generated here are 32-bit L32R targets. */
+
+static symbolS *
+xg_assemble_literal (/* const */ TInsn *insn)
{
- sym_list *l;
+ emit_state state;
+ symbolS *lit_sym = NULL;
- l = (sym_list *) xmalloc (sizeof (sym_list));
- l->sym = sym;
- l->next = literal_syms;
- literal_syms = l;
-}
+ /* 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;
+ expressionS * emit_val;
+ set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
-static void
-xtensa_add_insn_label (sym)
- symbolS *sym;
-{
- sym_list *l;
+ assert (insn->insn_type == ITYPE_LITERAL);
+ assert (insn->ntok == 1); /* must be only one token here */
- if (!free_insn_labels)
- l = (sym_list *) xmalloc (sizeof (sym_list));
- else
+ xtensa_switch_to_literal_fragment (&state);
+
+ emit_val = &insn->tok[0];
+ if (emit_val->X_op == O_big)
{
- l = free_insn_labels;
- free_insn_labels = l->next;
+ int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
+ if (size > litsize)
+ {
+ /* This happens when someone writes a "movi a2, big_number". */
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("invalid immediate"));
+ xtensa_restore_emit_state (&state);
+ return NULL;
+ }
}
- l->sym = sym;
- l->next = insn_labels;
- insn_labels = l;
+ /* 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);
+ record_alignment (now_seg, litalign);
+
+ if (emit_val->X_op == O_pltrel)
+ {
+ char *p = frag_more (litsize);
+ xtensa_set_frag_assembly_state (frag_now);
+ if (emit_val->X_add_symbol)
+ emit_val->X_op = O_symbol;
+ else
+ emit_val->X_op = O_constant;
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ litsize, emit_val, 0, BFD_RELOC_XTENSA_PLT);
+ }
+ else
+ emit_expr (emit_val, litsize);
+
+ assert (frag_now->tc_frag_data.literal_frag == NULL);
+ frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
+ frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
+ lit_sym = frag_now->fr_symbol;
+ frag_now->tc_frag_data.is_literal = TRUE;
+
+ /* Go back. */
+ xtensa_restore_emit_state (&state);
+ return lit_sym;
}
static void
-xtensa_clear_insn_labels (void)
+xg_assemble_literal_space (/* const */ int size, int slot)
{
- sym_list **pl;
-
- for (pl = &free_insn_labels; *pl != NULL; pl = &(*pl)->next)
- ;
- *pl = insn_labels;
- insn_labels = NULL;
-}
+ 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;
+ assert (size % 4 == 0);
-/* Return true if the section flags are marked linkonce
- or the name is .gnu.linkonce*. */
+ xtensa_switch_to_literal_fragment (&state);
-bfd_boolean
-get_is_linkonce_section (abfd, sec)
- bfd *abfd ATTRIBUTE_UNUSED;
- segT sec;
-{
- flagword flags, link_once_flags;
+ /* Force a 4-byte align here. */
+ frag_align (litalign, 0, 0);
+ record_alignment (now_seg, litalign);
- flags = bfd_get_section_flags (abfd, sec);
- link_once_flags = (flags & SEC_LINK_ONCE);
+ xg_force_frag_space (size);
- /* Flags might not be set yet. */
- if (!link_once_flags)
- {
- static size_t len = sizeof ".gnu.linkonce.t.";
+ lit_saved_frag = frag_now;
+ frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
+ frag_now->tc_frag_data.is_literal = TRUE;
+ frag_now->fr_symbol = xtensa_create_literal_symbol (now_seg, frag_now);
+ xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
- if (strncmp (segment_name (sec), ".gnu.linkonce.t.", len - 1) == 0)
- link_once_flags = SEC_LINK_ONCE;
- }
- return (link_once_flags != 0);
+ /* Go back. */
+ xtensa_restore_emit_state (&state);
+ frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
}
-/* 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. */
+/* Put in a fixup record based on the opcode.
+ Return TRUE on success. */
static bfd_boolean
-xg_emit_insn (t_insn, record_fix)
- TInsn *t_insn;
- bfd_boolean record_fix;
+xg_add_opcode_fix (TInsn *tinsn,
+ int opnum,
+ xtensa_format fmt,
+ int slot,
+ expressionS *expr,
+ fragS *fragP,
+ offsetT offset)
{
- 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);
+ xtensa_opcode opcode = tinsn->opcode;
+ bfd_reloc_code_real_type reloc;
+ reloc_howto_type *howto;
+ int fmt_length;
+ fixS *the_fix;
- 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);
+ reloc = BFD_RELOC_NONE;
- /* This needs to generate a record that lists the parts that are
- instructions. */
- if (!frag_now->tc_frag_data.is_insn)
+ /* First try the special cases for "alternate" relocs. */
+ if (opcode == xtensa_l32r_opcode)
{
- /* 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;
+ if (fragP->tc_frag_data.use_absolute_literals)
+ reloc = encode_alt_reloc (slot);
+ }
+ else if (opcode == xtensa_const16_opcode)
+ {
+ if (expr->X_op == O_lo16)
+ {
+ reloc = encode_reloc (slot);
+ expr->X_op = O_symbol;
+ }
+ else if (expr->X_op == O_hi16)
+ {
+ reloc = encode_alt_reloc (slot);
+ expr->X_op = O_symbol;
+ }
}
- xtensa_insnbuf_to_chars (isa, insnbuf, f);
+ if (opnum != get_relaxable_immed (opcode))
+ {
+ as_bad (_("invalid relocation for operand %i of '%s'"),
+ opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
- dwarf2_emit_insn (byte_count);
+ /* Handle erroneous "@h" and "@l" expressions here before they propagate
+ into the symbol table where the generic portions of the assembler
+ won't know what to do with them. */
+ if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
+ {
+ as_bad (_("invalid expression for operand %i of '%s'"),
+ opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
- /* Now spit out the opcode fixup.... */
- if (!has_fixup)
- return !ok;
+ /* Next try the generic relocs. */
+ if (reloc == BFD_RELOC_NONE)
+ reloc = encode_reloc (slot);
+ if (reloc == BFD_RELOC_NONE)
+ {
+ as_bad (_("invalid relocation in instruction slot %i"), slot);
+ return FALSE;
+ }
- for (i = 0; i < noperands; ++i)
+ howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ if (!howto)
{
- 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;
+ as_bad (_("undefined symbol for opcode \"%s\""),
+ xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
- case O_constant:
- case O_register:
- break;
+ fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
+ the_fix = fix_new_exp (fragP, offset, fmt_length, expr,
+ howto->pc_relative, reloc);
+ the_fix->fx_no_overflow = 1;
- default:
- as_bad (_("invalid expression for operand %d on '%s'"),
- i, xtensa_opcode_name (isa, opcode));
- ok = FALSE;
- break;
- }
- }
+ 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 !ok;
+ the_fix->tc_fix_data.X_add_symbol = expr->X_add_symbol;
+ the_fix->tc_fix_data.X_add_number = expr->X_add_number;
+ the_fix->tc_fix_data.slot = slot;
+
+ return TRUE;
}
static bfd_boolean
-xg_emit_insn_to_buf (t_insn, buf, fragP, offset, build_fix)
- TInsn *t_insn;
- char *buf;
- fragS *fragP;
- offsetT offset;
- bfd_boolean build_fix;
+xg_emit_insn_to_buf (TInsn *tinsn,
+ xtensa_format fmt,
+ char *buf,
+ fragS *fragP,
+ offsetT offset,
+ bfd_boolean build_fix)
{
static xtensa_insnbuf insnbuf = NULL;
bfd_boolean has_symbolic_immed = FALSE;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- has_symbolic_immed = tinsn_to_insnbuf (t_insn, insnbuf);
+ has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
if (has_symbolic_immed && build_fix)
{
/* Add a fixup. */
- int opnum = get_relaxable_immed (t_insn->opcode);
- expressionS *exp = &t_insn->tok[opnum];
+ int opnum = get_relaxable_immed (tinsn->opcode);
+ expressionS *exp = &tinsn->tok[opnum];
- if (!xg_add_opcode_fix (t_insn->opcode,
- opnum, exp, fragP, offset))
+ if (!xg_add_opcode_fix (tinsn, opnum, fmt, 0, exp, fragP, offset))
ok = FALSE;
}
fragP->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
+ xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+ (unsigned char *) buf, 0);
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;
+static void
+xg_resolve_literals (TInsn *insn, symbolS *lit_sym)
{
symbolS *sym = get_special_literal_symbol ();
int i;
}
-void
-xg_resolve_labels (insn, label_sym)
- TInsn *insn;
- symbolS *label_sym;
+static void
+xg_resolve_labels (TInsn *insn, symbolS *label_sym)
{
symbolS *sym = get_special_label_symbol ();
int i;
- /* assert(!insn->is_literal); */
+ /* 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. */
+/* Return TRUE if the instruction can write to the specified
+ integer register. */
- assert (insn->insn_type == ITYPE_INSN);
+static bfd_boolean
+is_register_writer (const TInsn *insn, const char *regset, int regnum)
+{
+ int i;
+ int num_ops;
+ xtensa_isa isa = xtensa_default_isa;
+
+ num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
- if (!tinsn_has_symbolic_operands (insn))
+ for (i = 0; i < num_ops; i++)
{
- if (xg_is_narrow_insn (insn) && !insn->is_specific_opcode)
+ char inout;
+ inout = xtensa_operand_inout (isa, insn->opcode, i);
+ if ((inout == 'o' || inout == 'm')
+ && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
{
- /* 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)
+ xtensa_regfile opnd_rf =
+ xtensa_operand_regfile (isa, insn->opcode, i);
+ if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
{
if ((insn->tok[i].X_op == O_register)
&& (insn->tok[i].X_add_number == regnum))
static bfd_boolean
-is_bad_loopend_opcode (tinsn)
- const TInsn * tinsn;
+is_bad_loopend_opcode (const TInsn *tinsn)
{
xtensa_opcode opcode = tinsn->opcode;
|| opcode == xtensa_ret_n_opcode
|| opcode == xtensa_retw_opcode
|| opcode == xtensa_retw_n_opcode
- || opcode == xtensa_waiti_opcode)
+ || opcode == xtensa_waiti_opcode
+ || opcode == xtensa_rsr_lcount_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;
}
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;
+static bfd_boolean
+is_unaligned_label (symbolS *sym)
{
const char *name = S_GET_NAME (sym);
static size_t fake_size = 0;
if (fake_size == 0)
fake_size = strlen (FAKE_LABEL_NAME);
- if (name
+ if (name
&& strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
&& (name[fake_size] == 'F'
|| name[fake_size] == 'L'
}
-fragS *
-next_non_empty_frag (fragP)
- const fragS *fragP;
+static fragS *
+next_non_empty_frag (const fragS *fragP)
{
fragS *next_fragP = fragP->fr_next;
}
-xtensa_opcode
-next_frag_opcode (fragP)
- const fragS * fragP;
+static bfd_boolean
+next_frag_opcode_is_loop (const fragS *fragP, xtensa_opcode *opcode)
{
+ xtensa_opcode out_opcode;
const fragS *next_fragP = next_non_empty_frag (fragP);
+
+ if (next_fragP == NULL)
+ return FALSE;
+
+ out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
+ {
+ *opcode = out_opcode;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static int
+frag_format_size (const fragS *fragP)
+{
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt;
+ int fmt_size;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
- if (next_fragP == NULL)
+ if (fragP == NULL)
return XTENSA_UNDEFINED;
- xtensa_insnbuf_from_chars (isa, insnbuf, next_fragP->fr_literal);
- return xtensa_decode_insn (isa, insnbuf);
-}
+ xtensa_insnbuf_from_chars (isa, insnbuf,
+ (unsigned char *) fragP->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return XTENSA_UNDEFINED;
+ fmt_size = xtensa_format_length (isa, fmt);
+
+ /* If the next format won't be changing due to relaxation, just
+ return the length of the first format. */
+ if (fragP->fr_opcode != fragP->fr_literal)
+ return fmt_size;
+
+ /* If during relaxation we have to pull an instruction out of a
+ multi-slot instruction, we will return the more conservative
+ number. This works because alignment on bigger instructions
+ is more restrictive than alignment on smaller instructions.
+ This is more conservative than we would like, but it happens
+ infrequently. */
+
+ if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
+ return fmt_size;
+
+ /* If we aren't doing one of our own relaxations or it isn't
+ slot-based, then the insn size won't change. */
+ if (fragP->fr_type != rs_machine_dependent)
+ return fmt_size;
+ if (fragP->fr_subtype != RELAX_SLOTS)
+ return fmt_size;
+
+ /* If an instruction is about to grow, return the longer size. */
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
+ || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2)
+ return 3;
+
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ return 2 + fragP->tc_frag_data.text_expansion[0];
-/* Return true if the target frag is one of the next non-empty frags. */
+ return fmt_size;
+}
-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;
+static int
+next_frag_format_size (const fragS *fragP)
+{
+ const fragS *next_fragP = next_non_empty_frag (fragP);
+ return frag_format_size (next_fragP);
}
switch its state so it will instantiate a NOP. */
static void
-update_next_frag_nop_state (fragP)
- fragS *fragP;
+update_next_frag_state (fragS *fragP)
{
fragS *next_fragP = fragP->fr_next;
+ fragS *new_target = NULL;
+
+ if (align_targets)
+ {
+ /* We are guaranteed there will be one of these... */
+ while (!(next_fragP->fr_type == rs_machine_dependent
+ && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || next_fragP->fr_subtype == RELAX_UNREACHABLE)))
+ next_fragP = next_fragP->fr_next;
+
+ assert (next_fragP->fr_type == rs_machine_dependent
+ && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || next_fragP->fr_subtype == RELAX_UNREACHABLE));
+
+ /* ...and one of these. */
+ new_target = next_fragP->fr_next;
+ while (!(new_target->fr_type == rs_machine_dependent
+ && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+ || new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
+ new_target = new_target->fr_next;
+
+ assert (new_target->fr_type == rs_machine_dependent
+ && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+ || new_target->fr_subtype == RELAX_DESIRE_ALIGN));
+ }
while (next_fragP && next_fragP->fr_fix == 0)
{
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;
+next_frag_is_branch_target (const fragS *fragP)
{
- /* Sometimes an empty will end up here due storage allocation issues,
+ /* Sometimes an empty will end up here due to storage allocation issues,
so we have to skip until we find something legit. */
for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
{
static bfd_boolean
-next_frag_is_loop_target (fragP)
- const fragS *fragP;
+next_frag_is_loop_target (const fragS *fragP)
{
/* Sometimes an empty will end up here due storage allocation issues.
So we have to skip until we find something legit. */
static addressT
-next_frag_pre_opcode_bytes (fragp)
- const fragS *fragp;
+next_frag_pre_opcode_bytes (const fragS *fragp)
{
const fragS *next_fragp = fragp->fr_next;
+ xtensa_opcode next_opcode;
- xtensa_opcode next_opcode = next_frag_opcode (fragp);
- if (!is_loop_opcode (next_opcode))
+ if (!next_frag_opcode_is_loop (fragp, &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. */
+ /* Sometimes an empty will end up here due to 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;
/* 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)
+ been relaxed. Note that we can assume that the LOOP
+ instruction is in slot 0 because loops aren't bundleable. */
+ if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
return get_expanded_loop_offset (next_opcode);
return 0;
placed nearest to their use. */
static void
-xtensa_mark_literal_pool_location ()
+xtensa_mark_literal_pool_location (void)
{
/* Any labels pointing to the current location need
to be adjusted to after the literal pool. */
emit_state s;
fragS *pool_location;
+ if (use_literal_section && !directive_state[directive_absolute_literals])
+ return;
+
frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
/* We stash info in the fr_var of these frags
- so we can later move the literal's fixes into this
+ 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,
+ 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,
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_variant (rs_machine_dependent, 0, (int) now_seg,
RELAX_LITERAL_POOL_END, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
/* Now put a frag into the literal pool that points to this location. */
set_literal_pool_location (now_seg, pool_location);
- xtensa_switch_to_literal_fragment (&s);
+ xtensa_switch_to_non_abs_literal_fragment (&s);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
/* Close whatever frag is there. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.literal_frag = pool_location;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_restore_emit_state (&s);
+ xtensa_set_frag_assembly_state (frag_now);
}
-/* 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. */
+/* Build a nop of the correct size into tinsn. */
static void
-xtensa_move_labels (new_frag, new_offset, loops_ok)
- fragS *new_frag;
- valueT new_offset;
- bfd_boolean loops_ok;
+build_nop (TInsn *tinsn, int size)
{
- sym_list *lit;
-
- for (lit = insn_labels; lit; lit = lit->next)
+ tinsn_init (tinsn);
+ switch (size)
{
- symbolS *lit_sym = lit->sym;
- if (loops_ok || symbol_get_tc (lit_sym)->is_loop_target == 0)
+ case 2:
+ tinsn->opcode = xtensa_nop_n_opcode;
+ tinsn->ntok = 0;
+ if (tinsn->opcode == XTENSA_UNDEFINED)
+ as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
+ break;
+
+ case 3:
+ if (xtensa_nop_opcode == XTENSA_UNDEFINED)
{
- S_SET_VALUE (lit_sym, new_offset);
- symbol_set_frag (lit_sym, new_frag);
+ tinsn->opcode = xtensa_or_opcode;
+ set_expr_const (&tinsn->tok[0], 1);
+ set_expr_const (&tinsn->tok[1], 1);
+ set_expr_const (&tinsn->tok[2], 1);
+ tinsn->ntok = 3;
}
+ else
+ tinsn->opcode = xtensa_nop_opcode;
+
+ assert (tinsn->opcode != XTENSA_UNDEFINED);
}
}
/* 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 void
+assemble_nop (int size, char *buf)
{
static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn;
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ TInsn tinsn;
- 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;
+ build_nop (&tinsn, size);
- 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;
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- default:
- as_fatal (_("invalid %d-byte NOP requested"), size);
- }
+ tinsn_to_insnbuf (&tinsn, insnbuf);
+ xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf,
+ (unsigned char *) buf, 0);
}
opcode. */
static addressT
-get_expanded_loop_offset (opcode)
- xtensa_opcode opcode;
+get_expanded_loop_offset (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"));
+ assert (opcode != XTENSA_UNDEFINED);
if (opcode == xtensa_loop_opcode)
return 0;
if (opcode == xtensa_loopnez_opcode)
}
-fragS *
-get_literal_pool_location (seg)
- segT seg;
+static fragS *
+get_literal_pool_location (segT seg)
{
return seg_info (seg)->tc_segment_info_data.literal_pool_loc;
}
static void
-set_literal_pool_location (seg, literal_pool_loc)
- segT seg;
- fragS *literal_pool_loc;
+set_literal_pool_location (segT seg, fragS *literal_pool_loc)
{
seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
}
+
+/* Set frag assembly state should be called when a new frag is
+ opened and after a frag has been closed. */
+
+static void
+xtensa_set_frag_assembly_state (fragS *fragP)
+{
+ if (!density_supported)
+ fragP->tc_frag_data.is_no_density = TRUE;
+
+ /* This function is called from subsegs_finish, which is called
+ after xtensa_end, so we can't use "use_transform" or
+ "use_schedule" here. */
+ if (!directive_state[directive_transform])
+ fragP->tc_frag_data.is_no_transform = TRUE;
+ if (directive_state[directive_longcalls])
+ fragP->tc_frag_data.use_longcalls = TRUE;
+ fragP->tc_frag_data.use_absolute_literals =
+ directive_state[directive_absolute_literals];
+ fragP->tc_frag_data.is_assembly_state_set = TRUE;
+}
+
+
+static bfd_boolean
+relaxable_section (asection *sec)
+{
+ return (sec->flags & SEC_DEBUGGING) == 0;
+}
+
+
+static void
+xtensa_find_unmarked_state_frags (void)
+{
+ segT *seclist;
+
+ /* Walk over each fragment of all of the current segments. For each
+ unmarked fragment, mark it with the same info as the previous
+ fragment. */
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+ segment_info_type *seginfo;
+ fragS *fragP;
+ flagword flags;
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ seginfo = seg_info (sec);
+ if (seginfo && seginfo->frchainP)
+ {
+ fragS *last_fragP = 0;
+ for (fragP = seginfo->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ if (fragP->fr_fix != 0
+ && !fragP->tc_frag_data.is_assembly_state_set)
+ {
+ if (last_fragP == 0)
+ {
+ as_warn_where (fragP->fr_file, fragP->fr_line,
+ _("assembly state not set for first frag in section %s"),
+ sec->name);
+ }
+ else
+ {
+ fragP->tc_frag_data.is_assembly_state_set = TRUE;
+ fragP->tc_frag_data.is_no_density =
+ last_fragP->tc_frag_data.is_no_density;
+ fragP->tc_frag_data.is_no_transform =
+ last_fragP->tc_frag_data.is_no_transform;
+ fragP->tc_frag_data.use_longcalls =
+ last_fragP->tc_frag_data.use_longcalls;
+ fragP->tc_frag_data.use_absolute_literals =
+ last_fragP->tc_frag_data.use_absolute_literals;
+ }
+ }
+ if (fragP->tc_frag_data.is_assembly_state_set)
+ last_fragP = fragP;
+ }
+ }
+ }
+}
+
+
+static void
+xtensa_find_unaligned_branch_targets (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ flagword flags = bfd_get_section_flags (abfd, sec);
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *frag = seginfo->frchainP->frch_root;
+
+ if (flags & SEC_CODE)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+ while (frag != NULL)
+ {
+ if (frag->tc_frag_data.is_branch_target)
+ {
+ int op_size;
+ addressT branch_align, frag_addr;
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ branch_align = 1 << branch_align_power (sec);
+ frag_addr = frag->fr_address % branch_align;
+ if (frag_addr + op_size > branch_align)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned branch target: %d bytes at 0x%lx"),
+ op_size, frag->fr_address);
+ }
+ frag = frag->fr_next;
+ }
+ xtensa_insnbuf_free (isa, insnbuf);
+ }
+}
+
+
+static void
+xtensa_find_unaligned_loops (bfd *abfd ATTRIBUTE_UNUSED,
+ asection *sec,
+ void *unused ATTRIBUTE_UNUSED)
+{
+ flagword flags = bfd_get_section_flags (abfd, sec);
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *frag = seginfo->frchainP->frch_root;
+ xtensa_isa isa = xtensa_default_isa;
+
+ if (flags & SEC_CODE)
+ {
+ xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+ while (frag != NULL)
+ {
+ if (frag->tc_frag_data.is_first_loop_insn)
+ {
+ int op_size;
+ addressT frag_addr;
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ frag_addr = frag->fr_address % xtensa_fetch_width;
+
+ if (frag_addr + op_size > xtensa_fetch_width)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned loop: %d bytes at 0x%lx"),
+ op_size, frag->fr_address);
+ }
+ frag = frag->fr_next;
+ }
+ xtensa_insnbuf_free (isa, insnbuf);
+ }
+}
+
+
+static int
+xg_apply_fix_value (fixS *fixP, valueT val)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_format fmt;
+ int slot;
+ bfd_boolean alt_reloc;
+ xtensa_opcode opcode;
+ char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ (void) decode_reloc (fixP->fx_r_type, &slot, &alt_reloc);
+ if (alt_reloc)
+ as_fatal (_("unexpected fix"));
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("undecodable fix"));
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+ if (opcode == XTENSA_UNDEFINED)
+ as_fatal (_("undecodable fix"));
+
+ /* CONST16 immediates are not PC-relative, despite the fact that we
+ reuse the normal PC-relative operand relocations for the low part
+ of a CONST16 operand. */
+ if (opcode == xtensa_const16_opcode)
+ return 0;
+
+ xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
+ get_relaxable_immed (opcode), val,
+ fixP->fx_file, fixP->fx_line);
+
+ xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
+ xtensa_insnbuf_to_chars (isa, insnbuf, (unsigned char *) fixpos, 0);
+
+ return 1;
+}
+
\f
/* External Functions and Other GAS Hooks. */
const char *
-xtensa_target_format ()
+xtensa_target_format (void)
{
return (target_big_endian ? "elf32-xtensa-be" : "elf32-xtensa-le");
}
void
-xtensa_file_arch_init (abfd)
- bfd *abfd;
+xtensa_file_arch_init (bfd *abfd)
{
bfd_set_private_flags (abfd, 0x100 | 0x200);
}
void
-md_number_to_chars (buf, val, n)
- char *buf;
- valueT val;
- int n;
+md_number_to_chars (char *buf, valueT val, int n)
{
if (target_big_endian)
number_to_chars_bigendian (buf, val, n);
need. */
void
-md_begin ()
+md_begin (void)
{
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(). */
+ xtensa_default_isa = xtensa_isa_init (0, 0);
isa = xtensa_default_isa;
-#endif
- /* Set up the .literal, .fini.literal and .init.literal sections. */
+ linkrelax = 1;
+
+ /* 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;
+ default_lit_sections.lit4_seg_name = LIT4_SECTION_NAME;
subseg_set (current_section, current_subsec);
+ xg_init_vinsn (&cur_vinsn);
+
xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
+ xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
+ xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
+ xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
- xtensa_j_opcode = xtensa_opcode_lookup (isa, "j");
xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
+ xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
+ xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
- xtensa_rsr_opcode = xtensa_opcode_lookup (isa, "rsr");
+ xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
+
+ init_op_placement_info_table ();
+
+ /* Set up the assembly state. */
+ if (!frag_now->tc_frag_data.is_assembly_state_set)
+ xtensa_set_frag_assembly_state (frag_now);
+}
+
+
+/* TC_INIT_FIX_DATA hook */
+
+void
+xtensa_init_fix_data (fixS *x)
+{
+ x->tc_fix_data.slot = 0;
+ x->tc_fix_data.X_add_symbol = NULL;
+ x->tc_fix_data.X_add_number = 0;
}
/* tc_frob_label hook */
void
-xtensa_frob_label (sym)
- symbolS *sym;
+xtensa_frob_label (symbolS *sym)
{
+ float freq = get_subseg_target_freq (now_seg, now_subseg);
+
+ /* Since the label was already attached to a frag associated with the
+ previous basic block, it now needs to be reset to the current frag. */
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
if (generating_literals)
xtensa_add_literal_sym (sym);
else
xtensa_add_insn_label (sym);
- if (symbol_get_tc (sym)->is_loop_target
- && (get_last_insn_flags (now_seg, now_subseg)
+ if (symbol_get_tc (sym)->is_loop_target)
+ {
+ if ((get_last_insn_flags (now_seg, now_subseg)
& FLAG_IS_BAD_LOOPEND) != 0)
- as_bad (_("invalid last instruction for a zero-overhead loop"));
+ as_bad (_("invalid last instruction for a zero-overhead loop"));
+
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_var (rs_machine_dependent, 4, 4, RELAX_LOOP_END,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+
+ xtensa_set_frag_assembly_state (frag_now);
+ xtensa_move_labels (frag_now, 0, TRUE);
+ }
/* No target aligning in the absolute section. */
if (now_seg != absolute_section
- && align_targets
+ && do_align_targets ()
&& !is_unaligned_label (sym)
- && !frag_now->tc_frag_data.is_literal)
+ && !generating_literals)
{
- /* frag_now->tc_frag_data.is_insn = TRUE; */
- frag_var (rs_machine_dependent, 4, 4,
+ xtensa_set_frag_assembly_state (frag_now);
+
+ frag_var (rs_machine_dependent,
+ 0, (int) freq,
RELAX_DESIRE_ALIGN_IF_TARGET,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
xtensa_move_labels (frag_now, 0, TRUE);
+ }
+
+ /* We need to mark the following properties even if we aren't aligning. */
+
+ /* 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;
+}
+
+
+/* tc_unrecognized_line hook */
- /* 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;
+int
+xtensa_unrecognized_line (int ch)
+{
+ switch (ch)
+ {
+ case '{' :
+ if (cur_vinsn.inside_bundle == 0)
+ {
+ /* PR8110: Cannot emit line number info inside a FLIX bundle
+ when using --gstabs. Temporarily disable debug info. */
+ generate_lineno_debug ();
+ if (debug_type == DEBUG_STABS)
+ {
+ xt_saved_debug_type = debug_type;
+ debug_type = DEBUG_NONE;
+ }
+
+ cur_vinsn.inside_bundle = 1;
+ }
+ else
+ {
+ as_bad (_("extra opening brace"));
+ return 0;
+ }
+ break;
- /* 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;
+ case '}' :
+ if (cur_vinsn.inside_bundle)
+ finish_vinsn (&cur_vinsn);
+ else
+ {
+ as_bad (_("extra closing brace"));
+ return 0;
+ }
+ break;
+ default:
+ as_bad (_("syntax error"));
+ return 0;
}
+ return 1;
}
/* md_flush_pending_output hook */
void
-xtensa_flush_pending_output ()
+xtensa_flush_pending_output (void)
{
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("missing closing brace"));
+
/* 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);
+ xtensa_set_frag_assembly_state (frag_now);
}
frag_now->tc_frag_data.is_insn = FALSE;
}
+/* We had an error while parsing an instruction. The string might look
+ like this: "insn arg1, arg2 }". If so, we need to see the closing
+ brace and reset some fields. Otherwise, the vinsn never gets closed
+ and the num_slots field will grow past the end of the array of slots,
+ and bad things happen. */
+
+static void
+error_reset_cur_vinsn (void)
+{
+ if (cur_vinsn.inside_bundle)
+ {
+ if (*input_line_pointer == '}'
+ || *(input_line_pointer - 1) == '}'
+ || *(input_line_pointer - 2) == '}')
+ xg_clear_vinsn (&cur_vinsn);
+ }
+}
+
+
void
-md_assemble (str)
- char *str;
+md_assemble (char *str)
{
xtensa_isa isa = xtensa_default_isa;
char *opname;
unsigned opnamelen;
bfd_boolean has_underbar = FALSE;
- char *arg_strings[MAX_INSN_ARGS];
+ 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. */
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.is_specific_opcode = (has_underbar || !use_transform ());
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))
+ xtensa_format fmt = xtensa_format_lookup (isa, opname);
+ if (fmt == XTENSA_UNDEFINED)
{
- 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);
+ as_bad (_("unknown opcode or format name '%s'"), opname);
+ error_reset_cur_vinsn ();
+ return;
+ }
+ if (!cur_vinsn.inside_bundle)
+ {
+ as_bad (_("format names only valid inside bundles"));
+ error_reset_cur_vinsn ();
+ return;
}
+ if (cur_vinsn.format != XTENSA_UNDEFINED)
+ as_warn (_("multiple formats specified for one bundle; using '%s'"),
+ opname);
+ cur_vinsn.format = fmt;
+ free (has_underbar ? opname - 1 : opname);
+ error_reset_cur_vinsn ();
+ return;
}
- /* 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"));
+ error_reset_cur_vinsn ();
return;
}
while (num_args-- > 0)
free (arg_strings[num_args]);
+ /* Get expressions for invisible operands. */
+ if (get_invisible_operands (&orig_insn))
+ {
+ error_reset_cur_vinsn ();
+ return;
+ }
+
/* 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);
+ error_reset_cur_vinsn ();
+ return;
}
+ dwarf2_where (&orig_insn.loc);
+
+ xg_add_branch_and_loop_targets (&orig_insn);
+
/* Special-case for "entry" instruction. */
- if (is_entry_opcode (orig_insn.opcode))
+ if (orig_insn.opcode == xtensa_entry_opcode)
{
- /* Check that the second opcode (#1) is >= 16. */
- if (orig_insn.ntok >= 2)
+ /* Check that the third opcode (#2) is >= 16. */
+ if (orig_insn.ntok >= 3)
{
- expressionS *exp = &orig_insn.tok[1];
+ expressionS *exp = &orig_insn.tok[2];
switch (exp->X_op)
{
case O_constant:
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
+ 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++)
+ if (!cur_vinsn.inside_bundle)
+ emit_single_op (&orig_insn);
+ else /* We are inside a bundle. */
{
- 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);
- }
+ cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
+ cur_vinsn.num_slots++;
+ if (*input_line_pointer == '}'
+ || *(input_line_pointer - 1) == '}'
+ || *(input_line_pointer - 2) == '}')
+ finish_vinsn (&cur_vinsn);
}
- /* 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);
- }
+ /* We've just emitted a new instruction so clear the list of labels. */
+ xtensa_clear_insn_labels ();
}
-/* 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. */
+/* HANDLE_ALIGN hook */
-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;
+/* For a .align directive, we mark the previous block with the alignment
+ information. This will be placed in the object file in the
+ property section corresponding to this section. */
- if (*input_line_pointer == '@')
+void
+xtensa_handle_align (fragS *fragP)
+{
+ if (linkrelax
+ && ! fragP->tc_frag_data.is_literal
+ && (fragP->fr_type == rs_align
+ || fragP->fr_type == rs_align_code)
+ && fragP->fr_address + fragP->fr_fix > 0
+ && fragP->fr_offset > 0
+ && now_seg != bss_section)
{
- 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;
+ fragP->tc_frag_data.is_align = TRUE;
+ fragP->tc_frag_data.alignment = fragP->fr_offset;
}
- switch (size)
+ if (fragP->fr_type == rs_align_test)
{
- 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;
+ int count;
+ count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ if (count != 0)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("unaligned entry instruction"));
}
- fix_new_exp (frag, where, size, exp, 0, r);
}
-
+
/* TC_FRAG_INIT hook */
void
-xtensa_frag_init (frag)
- fragS *frag;
+xtensa_frag_init (fragS *frag)
{
- frag->tc_frag_data.is_no_density = !code_density_available ();
+ xtensa_set_frag_assembly_state (frag);
}
symbolS *
-md_undefined_symbol (name)
- char *name ATTRIBUTE_UNUSED;
+md_undefined_symbol (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;
+md_section_align (segT segment ATTRIBUTE_UNUSED, valueT size)
{
return size; /* Byte alignment is fine. */
}
long
-md_pcrel_from (fixP)
- fixS *fixP;
+md_pcrel_from (fixS *fixP)
{
char *insn_p;
static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
int opnum;
- xtensa_operand operand;
+ uint32 opnd_value;
xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
xtensa_isa isa = xtensa_default_isa;
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
-
- if (fixP->fx_done)
- return addr;
+ bfd_boolean alt_reloc;
if (fixP->fx_r_type == BFD_RELOC_XTENSA_ASM_EXPAND)
- return addr;
+ return 0;
if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
- xtensa_insnbuf_from_chars (isa, insnbuf, insn_p);
- opcode = xtensa_decode_insn (isa, insnbuf);
+ xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) insn_p, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
- opnum = reloc_to_opnum (fixP->fx_r_type);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("bad instruction format"));
- 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)
+ if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
+ as_fatal (_("invalid relocation"));
+
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+ /* Check for "alternate" relocations (operand not specified). None
+ of the current uses for these are really PC-relative. */
+ if (alt_reloc || opcode == xtensa_const16_opcode)
{
- 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 (opcode != xtensa_l32r_opcode
+ && opcode != xtensa_const16_opcode)
+ as_fatal (_("invalid relocation for '%s' instruction"),
+ xtensa_opcode_name (isa, opcode));
+ return 0;
}
- if (!operand_is_pcrel_label (operand))
+ opnum = get_relaxable_immed (opcode);
+ opnd_value = 0;
+ if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
+ || xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
{
as_bad_where (fixP->fx_file,
fixP->fx_line,
_("invalid relocation for operand %d of '%s'"),
opnum, xtensa_opcode_name (isa, opcode));
- return addr;
+ return 0;
}
- if (!xtensa_operand_isPCRelative (operand))
+ return 0 - opnd_value;
+}
+
+
+/* TC_FORCE_RELOCATION hook */
+
+int
+xtensa_force_relocation (fixS *fix)
+{
+ switch (fix->fx_r_type)
{
- 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;
+ case BFD_RELOC_XTENSA_ASM_EXPAND:
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ return 1;
+ default:
+ break;
}
- return 0 - xtensa_operand_do_reloc (operand, 0, addr);
+ if (linkrelax && fix->fx_addsy
+ && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+
+/* TC_VALIDATE_FIX_SUB hook */
+
+int
+xtensa_validate_fix_sub (fixS *fix)
+{
+ segT add_symbol_segment, sub_symbol_segment;
+
+ /* The difference of two symbols should be resolved by the assembler when
+ linkrelax is not set. If the linker may relax the section containing
+ the symbols, then an Xtensa DIFF relocation must be generated so that
+ the linker knows to adjust the difference value. */
+ if (!linkrelax || fix->fx_addsy == NULL)
+ return 0;
+
+ /* Make sure both symbols are in the same segment, and that segment is
+ "normal" and relaxable. If the segment is not "normal", then the
+ fix is not valid. If the segment is not "relaxable", then the fix
+ should have been handled earlier. */
+ add_symbol_segment = S_GET_SEGMENT (fix->fx_addsy);
+ if (! SEG_NORMAL (add_symbol_segment) ||
+ ! relaxable_section (add_symbol_segment))
+ return 0;
+ sub_symbol_segment = S_GET_SEGMENT (fix->fx_subsy);
+ return (sub_symbol_segment == add_symbol_segment);
+}
+
+
+/* NO_PSEUDO_DOT hook */
+
+/* This function has nothing to do with pseudo dots, but this is the
+ nearest macro to where the check needs to take place. FIXME: This
+ seems wrong. */
+
+bfd_boolean
+xtensa_check_inside_bundle (void)
+{
+ if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
+ as_bad (_("directives are not valid inside bundles"));
+
+ /* This function must always return FALSE because it is called via a
+ macro that has nothing to do with bundling. */
+ return FALSE;
}
-/* tc_symbol_new_hook */
+/* md_elf_section_change_hook */
void
-xtensa_symbol_new_hook (symbolP)
- symbolS *symbolP;
+xtensa_elf_section_change_hook (void)
{
- symbol_get_tc (symbolP)->plt = 0;
+ /* Set up the assembly state. */
+ if (!frag_now->tc_frag_data.is_assembly_state_set)
+ xtensa_set_frag_assembly_state (frag_now);
}
/* tc_fix_adjustable hook */
bfd_boolean
-xtensa_fix_adjustable (fixP)
- fixS *fixP;
-{
+xtensa_fix_adjustable (fixS *fixP)
+{
+ /* An offset is not allowed in combination with the difference of two
+ symbols, but that cannot be easily detected after a local symbol
+ has been adjusted to a (section+offset) form. Return 0 so that such
+ an fix will not be adjusted. */
+ if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
+ && relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
+ return 0;
+
/* We need the symbol name for the VTABLE entries. */
if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
void
-md_apply_fix3 (fixP, valP, seg)
- fixS *fixP;
- valueT *valP;
- segT seg ATTRIBUTE_UNUSED;
+md_apply_fix3 (fixS *fixP, valueT *valP, segT seg)
{
- 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;
+ char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+ valueT val;
- switch (fixP->fx_r_type)
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_32:
+ case BFD_RELOC_16:
+ case BFD_RELOC_8:
+ if (linkrelax && fixP->fx_subsy)
{
- 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;
+ switch (fixP->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+ break;
+ case BFD_RELOC_16:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+ break;
+ case BFD_RELOC_32:
+ fixP->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+ break;
+ default:
+ break;
+ }
- 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);
+ /* An offset is only allowed when it results from adjusting a
+ local symbol into a section-relative offset. If the offset
+ came from the original expression, tc_fix_adjustable will have
+ prevented the fix from being converted to a section-relative
+ form so that we can flag the error here. */
+ if (fixP->fx_offset != 0 && !symbol_section_p (fixP->fx_addsy))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("cannot represent subtraction with an offset"));
+
+ val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ - S_GET_VALUE (fixP->fx_subsy));
+
+ /* The difference value gets written out, and the DIFF reloc
+ identifies the address of the subtracted symbol (i.e., the one
+ with the lowest address). */
+ *valP = val;
+ fixP->fx_offset -= val;
+ fixP->fx_subsy = NULL;
+ }
+ else if (! fixP->fx_addsy)
+ {
+ val = *valP;
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"));
+ }
+ else
+ break;
+ md_number_to_chars (fixpos, val, fixP->fx_size);
+ fixP->fx_no_overflow = 0; /* Use the standard overflow check. */
+ break;
- xtensa_insnbuf_set_immediate_field (opcode, insnbuf, *valP,
- fixP->fx_file, fixP->fx_line);
+ case BFD_RELOC_XTENSA_SLOT0_OP:
+ case BFD_RELOC_XTENSA_SLOT1_OP:
+ case BFD_RELOC_XTENSA_SLOT2_OP:
+ case BFD_RELOC_XTENSA_SLOT3_OP:
+ case BFD_RELOC_XTENSA_SLOT4_OP:
+ case BFD_RELOC_XTENSA_SLOT5_OP:
+ case BFD_RELOC_XTENSA_SLOT6_OP:
+ case BFD_RELOC_XTENSA_SLOT7_OP:
+ case BFD_RELOC_XTENSA_SLOT8_OP:
+ case BFD_RELOC_XTENSA_SLOT9_OP:
+ case BFD_RELOC_XTENSA_SLOT10_OP:
+ case BFD_RELOC_XTENSA_SLOT11_OP:
+ case BFD_RELOC_XTENSA_SLOT12_OP:
+ case BFD_RELOC_XTENSA_SLOT13_OP:
+ case BFD_RELOC_XTENSA_SLOT14_OP:
+ if (linkrelax)
+ {
+ /* Write the tentative value of a PC-relative relocation to a
+ local symbol into the instruction. The value will be ignored
+ by the linker, and it makes the object file disassembly
+ readable when all branch targets are encoded in relocations. */
+
+ assert (fixP->fx_addsy);
+ if (S_GET_SEGMENT (fixP->fx_addsy) == seg && !fixP->fx_plt
+ && !S_FORCE_RELOC (fixP->fx_addsy, 1))
+ {
+ val = (S_GET_VALUE (fixP->fx_addsy) + fixP->fx_offset
+ - md_pcrel_from (fixP));
+ (void) xg_apply_fix_value (fixP, val);
+ }
+ }
+ else if (! fixP->fx_addsy)
+ {
+ val = *valP;
+ if (xg_apply_fix_value (fixP, val))
+ fixP->fx_done = 1;
+ }
+ break;
- fixP->fx_frag->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars (isa, insnbuf, fixpos);
- fixP->fx_done = 1;
- break;
+ case BFD_RELOC_XTENSA_PLT:
+ case BFD_RELOC_XTENSA_ASM_EXPAND:
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ /* These all need to be resolved at link-time. Do nothing now. */
+ break;
- case BFD_RELOC_VTABLE_INHERIT:
- case BFD_RELOC_VTABLE_ENTRY:
- fixP->fx_done = 0;
- 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));
- }
+ 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;
+md_atof (int type, char *litP, int *sizeP)
{
int prec;
LITTLENUM_TYPE words[4];
int
-md_estimate_size_before_relax (fragP, seg)
- fragS *fragP;
- segT seg ATTRIBUTE_UNUSED;
+md_estimate_size_before_relax (fragS *fragP, segT seg ATTRIBUTE_UNUSED)
{
- return fragP->tc_frag_data.text_expansion;
+ return total_frag_text_expansion (fragP);
}
format. */
arelent *
-tc_gen_reloc (section, fixp)
- asection *section ATTRIBUTE_UNUSED;
- fixS *fixp;
+tc_gen_reloc (asection *section ATTRIBUTE_UNUSED, fixS *fixp)
{
arelent *reloc;
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;
+ reloc->addend = fixp->fx_offset;
+
+ reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
+ if (reloc->howto == NULL)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent `%s' relocation in object file"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
+ return NULL;
+ }
+
+ if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+
+ return reloc;
+}
+
+\f
+/* Checks for resource conflicts between instructions. */
+
+/* The func unit stuff could be implemented as bit-vectors rather
+ than the iterative approach here. If it ends up being too
+ slow, we will switch it. */
+
+resource_table *
+new_resource_table (void *data,
+ int cycles,
+ int nu,
+ unit_num_copies_func uncf,
+ opcode_num_units_func onuf,
+ opcode_funcUnit_use_unit_func ouuf,
+ opcode_funcUnit_use_stage_func ousf)
+{
+ int i;
+ resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
+ rt->data = data;
+ rt->cycles = cycles;
+ rt->allocated_cycles = cycles;
+ rt->num_units = nu;
+ rt->unit_num_copies = uncf;
+ rt->opcode_num_units = onuf;
+ rt->opcode_unit_use = ouuf;
+ rt->opcode_unit_stage = ousf;
+
+ rt->units = (char **) xcalloc (cycles, sizeof (char *));
+ for (i = 0; i < cycles; i++)
+ rt->units[i] = (char *) xcalloc (nu, sizeof (char));
+
+ return rt;
+}
+
+
+void
+clear_resource_table (resource_table *rt)
+{
+ int i, j;
+ for (i = 0; i < rt->allocated_cycles; i++)
+ for (j = 0; j < rt->num_units; j++)
+ rt->units[i][j] = 0;
+}
+
+
+/* We never shrink it, just fake it into thinking so. */
+
+void
+resize_resource_table (resource_table *rt, int cycles)
+{
+ int i, old_cycles;
+
+ rt->cycles = cycles;
+ if (cycles <= rt->allocated_cycles)
+ return;
+
+ old_cycles = rt->allocated_cycles;
+ rt->allocated_cycles = cycles;
+
+ rt->units = xrealloc (rt->units, sizeof (char *) * rt->allocated_cycles);
+ for (i = 0; i < old_cycles; i++)
+ rt->units[i] = xrealloc (rt->units[i], sizeof (char) * rt->num_units);
+ for (i = old_cycles; i < cycles; i++)
+ rt->units[i] = xcalloc (rt->num_units, sizeof (char));
+}
+
+
+bfd_boolean
+resources_available (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ int copies_in_use = rt->units[stage + cycle][unit];
+ int copies = (rt->unit_num_copies) (rt->data, unit);
+ if (copies_in_use >= copies)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+reserve_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ /* Note that this allows resources to be oversubscribed. That's
+ essential to the way the optional scheduler works.
+ resources_available reports when a resource is over-subscribed,
+ so it's easy to tell. */
+ rt->units[stage + cycle][unit]++;
+ }
+}
+
+
+void
+release_resources (resource_table *rt, xtensa_opcode opcode, int cycle)
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ rt->units[stage + cycle][unit]--;
+ assert (rt->units[stage + cycle][unit] >= 0);
+ }
+}
+
+
+/* Wrapper functions make parameterized resource reservation
+ more convenient. */
+
+int
+opcode_funcUnit_use_unit (void *data, xtensa_opcode opcode, int idx)
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->unit;
+}
+
+
+int
+opcode_funcUnit_use_stage (void *data, xtensa_opcode opcode, int idx)
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->stage;
+}
+
+
+/* Note that this function does not check issue constraints, but
+ solely whether the hardware is available to execute the given
+ instructions together. It also doesn't check if the tinsns
+ write the same state, or access the same tieports. That is
+ checked by check_t1_t2_reads_and_writes. */
+
+static bfd_boolean
+resources_conflict (vliw_insn *vinsn)
+{
+ int i;
+ static resource_table *rt = NULL;
+
+ /* This is the most common case by far. Optimize it. */
+ if (vinsn->num_slots == 1)
+ return FALSE;
+
+ if (rt == NULL)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ rt = new_resource_table
+ (isa, xtensa_isa_num_pipe_stages (isa),
+ xtensa_isa_num_funcUnits (isa),
+ (unit_num_copies_func) xtensa_funcUnit_num_copies,
+ (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
+ opcode_funcUnit_use_unit,
+ opcode_funcUnit_use_stage);
+ }
+
+ clear_resource_table (rt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (!resources_available (rt, vinsn->slots[i].opcode, 0))
+ return TRUE;
+ reserve_resources (rt, vinsn->slots[i].opcode, 0);
+ }
+
+ return FALSE;
+}
+
+\f
+/* finish_vinsn, emit_single_op and helper functions. */
+
+static bfd_boolean find_vinsn_conflicts (vliw_insn *);
+static xtensa_format xg_find_narrowest_format (vliw_insn *);
+static void bundle_single_op (TInsn *);
+static void xg_assemble_vliw_tokens (vliw_insn *);
+
+
+/* We have reached the end of a bundle; emit into the frag. */
+
+static void
+finish_vinsn (vliw_insn *vinsn)
+{
+ IStack slotstack;
+ int i;
+ char *file_name;
+ unsigned line;
+
+ if (find_vinsn_conflicts (vinsn))
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ if (vinsn->format == XTENSA_UNDEFINED)
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line,
+ _("couldn't find a valid instruction format"));
+ fprintf (stderr, _(" ops were: "));
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, _(" %s;"),
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, _("\n"));
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (vinsn->num_slots
+ != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
+ {
+ as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
+ xtensa_format_name (xtensa_default_isa, vinsn->format),
+ xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
+ vinsn->num_slots);
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
+ {
+ symbolS *lit_sym = NULL;
+ int j;
+ bfd_boolean e = FALSE;
+ bfd_boolean saved_density = density_supported;
+
+ /* We don't want to narrow ops inside multi-slot bundles. */
+ if (vinsn->num_slots > 1)
+ density_supported = FALSE;
+
+ istack_init (&slotstack);
+ if (vinsn->slots[i].opcode == xtensa_nop_opcode)
+ {
+ vinsn->slots[i].opcode =
+ xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+ vinsn->slots[i].ntok = 0;
+ }
+
+ if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
+ {
+ e = TRUE;
+ continue;
+ }
+
+ density_supported = saved_density;
+
+ if (e)
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (j = 0; j < slotstack.ninsn; j++)
+ {
+ TInsn *insn = &slotstack.insn[j];
+ if (insn->insn_type == ITYPE_LITERAL)
+ {
+ assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ }
+ else
+ {
+ assert (insn->insn_type == ITYPE_INSN);
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (j != slotstack.ninsn - 1)
+ emit_single_op (insn);
+ }
+ }
+
+ if (vinsn->num_slots > 1)
+ {
+ if (opcode_fits_format_slot
+ (slotstack.insn[slotstack.ninsn - 1].opcode,
+ vinsn->format, i))
+ {
+ vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
+ }
+ else
+ {
+ bundle_single_op (&slotstack.insn[slotstack.ninsn - 1]);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->slots[i].opcode = xtensa_nop_opcode;
+ else
+ vinsn->slots[i].opcode
+ = xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+
+ vinsn->slots[i].ntok = 0;
+ }
+ }
+ else
+ {
+ vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
+ vinsn->format = XTENSA_UNDEFINED;
+ }
+ }
+ }
+
+ /* Now check resource conflicts on the modified bundle. */
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ xg_assemble_vliw_tokens (vinsn);
+
+ xg_clear_vinsn (vinsn);
+}
+
+
+/* Given an vliw instruction, what conflicts are there in register
+ usage and in writes to states and queues?
+
+ This function does two things:
+ 1. Reports an error when a vinsn contains illegal combinations
+ of writes to registers states or queues.
+ 2. Marks individual tinsns as not relaxable if the combination
+ contains antidependencies.
+
+ Job 2 handles things like swap semantics in instructions that need
+ to be relaxed. For example,
+
+ addi a0, a1, 100000
+
+ normally would be relaxed to
+
+ l32r a0, some_label
+ add a0, a1, a0
+
+ _but_, if the above instruction is bundled with an a0 reader, e.g.,
+
+ { addi a0, a1, 10000 ; add a2, a0, a4 ; }
+
+ then we can't relax it into
+
+ l32r a0, some_label
+ { add a0, a1, a0 ; add a2, a0, a4 ; }
+
+ because the value of a0 is trashed before the second add can read it. */
+
+static char check_t1_t2_reads_and_writes (TInsn *, TInsn *);
+
+static bfd_boolean
+find_vinsn_conflicts (vliw_insn *vinsn)
+{
+ int i, j;
+ int branches = 0;
+ xtensa_isa isa = xtensa_default_isa;
+
+ assert (!past_xtensa_end);
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+ if (op1->is_specific_opcode)
+ op1->keep_wide = TRUE;
+ else
+ op1->keep_wide = FALSE;
+ }
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+
+ if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
+ branches++;
+
+ for (j = 0; j < vinsn->num_slots; j++)
+ {
+ if (i != j)
+ {
+ TInsn *op2 = &vinsn->slots[j];
+ char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
+ switch (conflict_type)
+ {
+ case 'c':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'd':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'e':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same queue"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'f':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile queue accesses"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ default:
+ /* Everything is OK. */
+ break;
+ }
+ op2->is_specific_opcode = (op2->is_specific_opcode
+ || conflict_type == 'a');
+ }
+ }
+ }
+
+ if (branches > 1)
+ {
+ as_bad (_("multiple branches or jumps in the same bundle"));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Check how the state used by t1 and t2 relate.
+ Cases found are:
+
+ case A: t1 reads a register t2 writes (an antidependency within a bundle)
+ case B: no relationship between what is read and written (both could
+ read the same reg though)
+ case C: t1 writes a register t2 writes (a register conflict within a
+ bundle)
+ case D: t1 writes a state that t2 also writes
+ case E: t1 writes a tie queue that t2 also writes
+ case F: two volatile queue accesses
+*/
+
+static char
+check_t1_t2_reads_and_writes (TInsn *t1, TInsn *t2)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_regfile t1_regfile, t2_regfile;
+ int t1_reg, t2_reg;
+ int t1_base_reg, t1_last_reg;
+ int t2_base_reg, t2_last_reg;
+ char t1_inout, t2_inout;
+ int i, j;
+ char conflict = 'b';
+ int t1_states;
+ int t2_states;
+ int t1_interfaces;
+ int t2_interfaces;
+ bfd_boolean t1_volatile = FALSE;
+ bfd_boolean t2_volatile = FALSE;
+
+ /* Check registers. */
+ for (j = 0; j < t2->ntok; j++)
+ {
+ if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
+ continue;
+
+ t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
+ t2_base_reg = t2->tok[j].X_add_number;
+ t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
+
+ for (i = 0; i < t1->ntok; i++)
+ {
+ if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
+ continue;
+
+ t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
+
+ if (t1_regfile != t2_regfile)
+ continue;
+
+ t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
+ t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
+
+ if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
+ || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
+ {
+ if (t1_inout == 'm' || t1_inout == 'o'
+ || t2_inout == 'm' || t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+ }
+
+ t1_base_reg = t1->tok[i].X_add_number;
+ t1_last_reg = (t1_base_reg
+ + xtensa_operand_num_regs (isa, t1->opcode, i));
+
+ for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
+ {
+ for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
+ {
+ if (t1_reg != t2_reg)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'c';
+ }
+ }
+ }
+ }
+
+ /* Check states. */
+ t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
+ t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
+ for (j = 0; j < t2_states; j++)
+ {
+ xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
+ t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
+ for (i = 0; i < t1_states; i++)
+ {
+ xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
+ t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
+ if (t1_so != t2_so)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'd';
+ }
+ }
+
+ /* Check tieports. */
+ t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
+ t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
+ for (j = 0; j < t2_interfaces; j++)
+ {
+ xtensa_interface t2_int
+ = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
+ int t2_class = xtensa_interface_class_id (isa, t2_int);
+
+ t2_inout = xtensa_interface_inout (isa, j);
+ if (xtensa_interface_has_side_effect (isa, t2_int) == 1)
+ t2_volatile = TRUE;
+
+ for (i = 0; i < t1_interfaces; i++)
+ {
+ xtensa_interface t1_int
+ = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
+ int t1_class = xtensa_interface_class_id (isa, t2_int);
+
+ t1_inout = xtensa_interface_inout (isa, i);
+ if (xtensa_interface_has_side_effect (isa, t1_int) == 1)
+ t1_volatile = TRUE;
+
+ if (t1_volatile && t2_volatile && (t1_class == t2_class))
+ return 'f';
+
+ if (t1_int != t2_int)
+ continue;
+
+ if (t2_inout == 'i' && t1_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'e';
+ }
+ }
+
+ return conflict;
+}
+
+
+static xtensa_format
+xg_find_narrowest_format (vliw_insn *vinsn)
+{
+ /* Right now we assume that the ops within the vinsn are properly
+ ordered for the slots that the programmer wanted them in. In
+ other words, we don't rearrange the ops in hopes of finding a
+ better format. The scheduler handles that. */
+
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format format;
+ vliw_insn v_copy = *vinsn;
+ xtensa_opcode nop_opcode = xtensa_nop_opcode;
+
+ for (format = 0; format < xtensa_isa_num_formats (isa); format++)
+ {
+ v_copy = *vinsn;
+ if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
+ {
+ int slot;
+ int fit = 0;
+ for (slot = 0; slot < v_copy.num_slots; slot++)
+ {
+ if (v_copy.slots[slot].opcode == nop_opcode)
+ {
+ v_copy.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, format, slot);
+ v_copy.slots[slot].ntok = 0;
+ }
+
+ if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
+ format, slot))
+ fit++;
+ else if (v_copy.num_slots > 1)
+ {
+ TInsn widened;
+ /* Try the widened version. */
+ if (!v_copy.slots[slot].keep_wide
+ && !v_copy.slots[slot].is_specific_opcode
+ && xg_is_narrow_insn (&v_copy.slots[slot])
+ && !xg_expand_narrow (&widened, &v_copy.slots[slot])
+ && opcode_fits_format_slot (widened.opcode,
+ format, slot))
+ {
+ /* The xg_is_narrow clause requires some explanation:
+
+ addi can be "widened" to an addmi, which is then
+ expanded to an addmi/addi pair if the immediate
+ requires it, but here we must have a single widen
+ only.
+
+ xg_is_narrow tells us that addi isn't really
+ narrow. The widen_spec_list says that there are
+ other cases. */
+
+ v_copy.slots[slot] = widened;
+ fit++;
+ }
+ }
+ }
+ if (fit == v_copy.num_slots)
+ {
+ *vinsn = v_copy;
+ xtensa_format_encode (isa, format, vinsn->insnbuf);
+ vinsn->format = format;
+ break;
+ }
+ }
+ }
+
+ if (format == xtensa_isa_num_formats (isa))
+ return XTENSA_UNDEFINED;
+
+ return format;
+}
+
+
+/* Return the additional space needed in a frag
+ for possible relaxations of any ops in a VLIW insn.
+ Also fill out the relaxations that might be required of
+ each tinsn in the vinsn. */
+
+static int
+relaxation_requirements (vliw_insn *vinsn)
+{
+ int extra_space = 0;
+ int slot;
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ if (!tinsn_has_symbolic_operands (tinsn))
+ {
+ /* A narrow instruction could be widened later to help
+ alignment issues. */
+ if (xg_is_narrow_insn (tinsn)
+ && !tinsn->is_specific_opcode
+ && vinsn->num_slots == 1)
+ {
+ /* Difference in bytes between narrow and wide insns... */
+ extra_space += 1;
+ tinsn->subtype = RELAX_NARROW;
+ tinsn->record_fix = TRUE;
+ break;
+ }
+ else
+ {
+ tinsn->record_fix = FALSE;
+ /* No extra_space needed. */
+ }
+ }
+ else
+ {
+ if (workaround_b_j_loop_end
+ && tinsn->opcode == xtensa_jx_opcode
+ && use_transform ())
+ {
+ /* Add 2 of these. */
+ extra_space += 3; /* for the nop size */
+ tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
+ }
+
+ /* Need to assemble it with space for the relocation. */
+ if (xg_is_relaxable_insn (tinsn, 0)
+ && !tinsn->is_specific_opcode)
+ {
+ int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
+ int max_literal_size =
+ xg_get_max_insn_widen_literal_size (tinsn->opcode);
+
+ tinsn->literal_space = max_literal_size;
+
+ tinsn->subtype = RELAX_IMMED;
+ tinsn->record_fix = FALSE;
+ extra_space += max_size;
+ }
+ else
+ {
+ tinsn->record_fix = TRUE;
+ /* No extra space needed. */
+ }
+ }
+ }
+ return extra_space;
+}
+
+
+static void
+bundle_single_op (TInsn *orig_insn)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ vliw_insn v;
+ int slot;
+
+ xg_init_vinsn (&v);
+ v.format = op_placement_table[orig_insn->opcode].narrowest;
+ assert (v.format != XTENSA_UNDEFINED);
+ v.num_slots = xtensa_format_num_slots (isa, v.format);
+
+ for (slot = 0;
+ !opcode_fits_format_slot (orig_insn->opcode, v.format, slot);
+ slot++)
+ {
+ v.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, v.format, slot);
+ v.slots[slot].ntok = 0;
+ v.slots[slot].insn_type = ITYPE_INSN;
+ }
+
+ v.slots[slot] = *orig_insn;
+ slot++;
+
+ for ( ; slot < v.num_slots; slot++)
+ {
+ v.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, v.format, slot);
+ v.slots[slot].ntok = 0;
+ v.slots[slot].insn_type = ITYPE_INSN;
+ }
+
+ finish_vinsn (&v);
+ xg_free_vinsn (&v);
+}
+
+
+static bfd_boolean
+emit_single_op (TInsn *orig_insn)
+{
+ int i;
+ IStack istack; /* put instructions into here */
+ symbolS *lit_sym = NULL;
+ symbolS *label_sym = NULL;
+
+ istack_init (&istack);
+
+ /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
+ Because the scheduling and bundling characteristics of movi and
+ l32r or const16 are so different, we can do much better if we relax
+ it prior to scheduling and bundling, rather than after. */
+ if ((orig_insn->opcode == xtensa_movi_opcode
+ || orig_insn->opcode == xtensa_movi_n_opcode)
+ && !cur_vinsn.inside_bundle
+ && (orig_insn->tok[1].X_op == O_symbol
+ || orig_insn->tok[1].X_op == O_pltrel))
+ xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
+ else
+ if (xg_expand_assembly_insn (&istack, orig_insn))
+ return TRUE;
+
+ for (i = 0; i < istack.ninsn; i++)
+ {
+ TInsn *insn = &istack.insn[i];
+ switch (insn->insn_type)
+ {
+ case ITYPE_LITERAL:
+ assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ break;
+ case ITYPE_LABEL:
+ {
+ static int relaxed_sym_idx = 0;
+ char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
+ sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
+ colon (label);
+ assert (label_sym == NULL);
+ label_sym = symbol_find_or_make (label);
+ assert (label_sym);
+ free (label);
+ }
+ break;
+ case ITYPE_INSN:
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (label_sym)
+ xg_resolve_labels (insn, label_sym);
+ bundle_single_op (insn);
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+static int
+total_frag_text_expansion (fragS *fragP)
+{
+ int slot;
+ int total_expansion = 0;
+
+ for (slot = 0; slot < MAX_SLOTS; slot++)
+ total_expansion += fragP->tc_frag_data.text_expansion[slot];
+
+ return total_expansion;
+}
+
+
+/* Emit a vliw instruction to the current fragment. */
+
+static void
+xg_assemble_vliw_tokens (vliw_insn *vinsn)
+{
+ bfd_boolean finish_frag = FALSE;
+ bfd_boolean is_jump = FALSE;
+ bfd_boolean is_branch = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
+ int insn_size;
+ int extra_space;
+ char *f = NULL;
+ int slot;
+ struct dwarf2_line_info best_loc;
+
+ best_loc.line = INT_MAX;
+
+ if (generating_literals)
+ {
+ static int reported = 0;
+ if (reported < 4)
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("cannot assemble into a literal fragment"));
+ if (reported == 3)
+ as_bad (_("..."));
+ reported++;
+ return;
+ }
+
+ if (frag_now_fix () != 0
+ && (! frag_now->tc_frag_data.is_insn
+ || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ || !use_transform () != frag_now->tc_frag_data.is_no_transform
+ || (directive_state[directive_longcalls]
+ != frag_now->tc_frag_data.use_longcalls)
+ || (directive_state[directive_absolute_literals]
+ != frag_now->tc_frag_data.use_absolute_literals)))
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (workaround_a0_b_retw
+ && vinsn->num_slots == 1
+ && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
+ && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
+ && use_transform ())
+ {
+ has_a0_b_retw = TRUE;
+
+ /* 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);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ /* See if the instruction implies an aligned section. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[i].opcode) == 1)
+ record_alignment (now_seg, 2);
+
+ /* Also determine the best line number for debug info. */
+ best_loc = vinsn->slots[i].loc.line < best_loc.line
+ ? vinsn->slots[i].loc : best_loc;
+ }
+
+ /* Special cases for instructions that force an alignment... */
+ /* None of these opcodes are bundle-able. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
+ {
+ int max_fill;
+
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+
+ max_fill = get_text_align_max_fill_size
+ (get_text_align_power (xtensa_fetch_width),
+ TRUE, frag_now->tc_frag_data.is_no_density);
+
+ if (use_transform ())
+ frag_var (rs_machine_dependent, max_fill, max_fill,
+ RELAX_ALIGN_NEXT_OPCODE,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ else
+ frag_var (rs_machine_dependent, 0, 0,
+ RELAX_CHECK_ALIGN_NEXT_OPCODE, 0, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+
+ xtensa_move_labels (frag_now, 0, FALSE);
+ }
+
+ if (vinsn->slots[0].opcode == xtensa_entry_opcode
+ && !vinsn->slots[0].is_specific_opcode)
+ {
+ xtensa_mark_literal_pool_location ();
+ xtensa_move_labels (frag_now, 0, TRUE);
+ frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
+ }
+
+ if (vinsn->num_slots == 1)
+ {
+ if (workaround_a0_b_retw && use_transform ())
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
+ is_register_writer (&vinsn->slots[0], "a", 0));
+
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
+ is_bad_loopend_opcode (&vinsn->slots[0]));
+ }
+ else
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
+
+ insn_size = xtensa_format_length (isa, vinsn->format);
+
+ extra_space = relaxation_requirements (vinsn);
+
+ /* vinsn_to_insnbuf will produce the error. */
+ if (vinsn->format != XTENSA_UNDEFINED)
+ {
+ f = frag_more (insn_size + extra_space);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ }
+
+ vinsn_to_insnbuf (vinsn, f, frag_now, TRUE);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ return;
+
+ xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, (unsigned char *) f, 0);
+
+ xtensa_dwarf2_emit_insn (insn_size - extra_space, &best_loc);
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
+ frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
+ frag_now->tc_frag_data.slot_sub_symbols[slot] = tinsn->sub_symbol;
+ frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
+ frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
+ if (tinsn->literal_space != 0)
+ xg_assemble_literal_space (tinsn->literal_space, slot);
+
+ if (tinsn->subtype == RELAX_NARROW)
+ assert (vinsn->num_slots == 1);
+ if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
+ is_jump = TRUE;
+ if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
+ is_branch = TRUE;
+
+ if (tinsn->subtype || tinsn->symbol || tinsn->record_fix
+ || tinsn->offset || tinsn->literal_frag || is_jump || is_branch)
+ finish_frag = TRUE;
+ }
+
+ if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ frag_now->tc_frag_data.is_specific_opcode = TRUE;
+
+ if (finish_frag)
+ {
+ frag_variant (rs_machine_dependent,
+ extra_space, extra_space, RELAX_SLOTS,
+ frag_now->fr_symbol, frag_now->fr_offset, f);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ /* 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 (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode)
+ && !vinsn->slots[0].is_specific_opcode)
+ {
+ if (workaround_short_loop && use_transform ())
+ {
+ maybe_has_short_loop = TRUE;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ }
+
+ /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
+ loop at least 12 bytes away from another loop's end. */
+ if (workaround_close_loop_end && use_transform ())
+ {
+ maybe_has_close_loop_end = TRUE;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 12, 12,
+ RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ }
}
- if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ if (use_transform ())
{
- as_fatal (_("internal error? cannot generate `%s' relocation"),
- bfd_get_reloc_code_name (fixp->fx_r_type));
+ if (is_jump)
+ {
+ assert (finish_frag);
+ frag_var (rs_machine_dependent,
+ UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+ RELAX_UNREACHABLE,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+ else if (is_branch && do_align_targets ())
+ {
+ assert (finish_frag);
+ frag_var (rs_machine_dependent,
+ UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+ RELAX_MAYBE_UNREACHABLE,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_var (rs_machine_dependent,
+ 0, 0,
+ RELAX_MAYBE_DESIRE_ALIGN,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
}
- assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
-
- reloc->addend = fixp->fx_offset;
- switch (fixp->fx_r_type)
+ /* Now, if the original opcode was a call... */
+ if (do_align_targets ()
+ && xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
{
- 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"));
+ float freq = get_subseg_total_freq (now_seg, now_subseg);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
}
- return reloc;
+ if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
}
\f
+/* xtensa_end and helper functions. */
+
+static void xtensa_cleanup_align_frags (void);
+static void xtensa_fix_target_frags (void);
+static void xtensa_mark_narrow_branches (void);
+static void xtensa_mark_zcl_first_insns (void);
+static void xtensa_fix_a0_b_retw_frags (void);
+static void xtensa_fix_b_j_loop_end_frags (void);
+static void xtensa_fix_close_loop_end_frags (void);
+static void xtensa_fix_short_loop_frags (void);
+static void xtensa_sanity_check (void);
+
void
-xtensa_end ()
+xtensa_end (void)
{
directive_balance ();
+ xtensa_flush_pending_output ();
+
+ past_xtensa_end = TRUE;
+
xtensa_move_literals ();
xtensa_reorder_segments ();
xtensa_cleanup_align_frags ();
xtensa_fix_target_frags ();
- if (software_a0_b_retw_interlock && has_a0_b_retw)
+ if (workaround_a0_b_retw && has_a0_b_retw)
xtensa_fix_a0_b_retw_frags ();
- if (software_avoid_b_j_loop_end && maybe_has_b_j_loop_end)
+ if (workaround_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)
+ if (workaround_close_loop_end && maybe_has_close_loop_end)
xtensa_fix_close_loop_end_frags ();
- if (software_avoid_short_loop && maybe_has_short_loop)
+ if (workaround_short_loop && maybe_has_short_loop)
xtensa_fix_short_loop_frags ();
+ xtensa_mark_narrow_branches ();
+ xtensa_mark_zcl_first_insns ();
xtensa_sanity_check ();
}
static void
-xtensa_cleanup_align_frags ()
+xtensa_cleanup_align_frags (void)
{
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)
{
|| fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
&& fragP->fr_fix == 0)
{
- fragS * next = fragP->fr_next;
+ fragS *next = fragP->fr_next;
while (next
- && next->fr_type == rs_machine_dependent
- && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+ && next->fr_fix == 0
+ && next->fr_type == rs_machine_dependent
+ && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
{
frag_wane (next);
next = next->fr_next;
}
}
+ /* If we don't widen branch targets, then they
+ will be easier to align. */
+ if (fragP->tc_frag_data.is_branch_target
+ && fragP->fr_opcode == fragP->fr_literal
+ && fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ frag_wane (fragP);
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_UNREACHABLE)
+ fragP->tc_frag_data.is_unreachable = TRUE;
}
}
}
/* Re-process all of the fragments looking to convert all of the
RELAX_DESIRE_ALIGN_IF_TARGET fragments. If there is a branch
target in the next fragment, convert this to RELAX_DESIRE_ALIGN.
- 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. */
+ Otherwise, convert to a .fill 0. */
static void
-xtensa_fix_target_frags ()
+xtensa_fix_target_frags (void)
{
frchainS *frchP;
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. */
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))
+ if (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;
+static bfd_boolean is_narrow_branch_guaranteed_in_range (fragS *, TInsn *);
+
+static void
+xtensa_mark_narrow_branches (void)
{
- if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_IMMED)
+ frchainS *frchP;
+
+ for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
{
- TInsn t_insn;
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- if (is_negatable_branch (&t_insn))
- return TRUE;
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+ {
+ vliw_insn vinsn;
+ const expressionS *expr;
+ symbolS *symbolP;
+
+ vinsn_from_chars (&vinsn, fragP->fr_opcode);
+ tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
+
+ expr = &vinsn.slots[0].tok[1];
+ symbolP = expr->X_add_symbol;
+
+ if (vinsn.num_slots == 1
+ && xtensa_opcode_is_branch (xtensa_default_isa,
+ vinsn.slots[0].opcode)
+ && xg_get_single_size (vinsn.slots[0].opcode) == 2
+ && is_narrow_branch_guaranteed_in_range (fragP,
+ &vinsn.slots[0]))
+ {
+ fragP->fr_subtype = RELAX_SLOTS;
+ fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
+ }
+ }
+ }
}
+}
+
+
+/* A branch is typically widened only when its target is out of
+ range. However, we would like to widen them to align a subsequent
+ branch target when possible.
+
+ Because the branch relaxation code is so convoluted, the optimal solution
+ (combining the two cases) is difficult to get right in all circumstances.
+ We therefore go with an "almost as good" solution, where we only
+ use for alignment narrow branches that definitely will not expand to a
+ jump and a branch. These functions find and mark these cases. */
+
+/* The range in bytes of BNEZ.N and BEQZ.N. The target operand is encoded
+ as PC + 4 + imm6, where imm6 is a 6-bit immediate ranging from 0 to 63.
+ We start counting beginning with the frag after the 2-byte branch, so the
+ maximum offset is (4 - 2) + 63 = 65. */
+#define MAX_IMMED6 65
+
+static offsetT unrelaxed_frag_max_size (fragS *);
+
+static bfd_boolean
+is_narrow_branch_guaranteed_in_range (fragS *fragP, TInsn *tinsn)
+{
+ const expressionS *expr = &tinsn->tok[1];
+ symbolS *symbolP = expr->X_add_symbol;
+ fragS *target_frag = symbol_get_frag (symbolP);
+ offsetT max_distance = expr->X_add_number;
+ max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
+ if (is_branch_jmp_to_next (tinsn, fragP))
+ return FALSE;
+
+ /* The branch doesn't branch over it's own frag,
+ but over the subsequent ones. */
+ fragP = fragP->fr_next;
+ while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
+ {
+ max_distance += unrelaxed_frag_max_size (fragP);
+ fragP = fragP->fr_next;
+ }
+ if (max_distance <= MAX_IMMED6 && fragP == target_frag)
+ return TRUE;
return FALSE;
}
+static void
+xtensa_mark_zcl_first_insns (void)
+{
+ frchainS *frchP;
+
+ for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* 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_ALIGN_NEXT_OPCODE
+ || fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
+ {
+ /* Find the loop frag. */
+ fragS *targ_frag = next_non_empty_frag (fragP);
+ /* Find the first insn frag. */
+ targ_frag = next_non_empty_frag (targ_frag);
+
+ /* Of course, sometimes (mostly for toy test cases) a
+ zero-cost loop instruction is the last in a section. */
+ if (targ_frag)
+ {
+ targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
+ if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
+ frag_wane (fragP);
+ }
+ }
+ }
+ }
+}
+
+
/* 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 bfd_boolean next_instrs_are_b_retw (fragS *);
+
static void
-xtensa_fix_a0_b_retw_frags ()
+xtensa_fix_a0_b_retw_frags (void)
{
frchainS *frchP;
&& 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);
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
}
}
}
}
-bfd_boolean
-next_instrs_are_b_retw (fragP)
- fragS * fragP;
+static bfd_boolean
+next_instrs_are_b_retw (fragS *fragP)
{
xtensa_opcode opcode;
+ xtensa_format fmt;
const fragS *next_fragP = next_non_empty_frag (fragP);
static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
int offset = 0;
+ int slot;
+ bfd_boolean branch_seen = FALSE;
if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = 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);
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return FALSE;
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+ branch_seen = (branch_seen
+ || xtensa_opcode_is_branch (isa, opcode) == 1);
+ }
- if (!is_conditional_branch_opcode (opcode))
+ if (!branch_seen)
return FALSE;
- offset += xtensa_insn_length (isa, opcode);
+ offset += xtensa_format_length (isa, fmt);
if (offset == next_fragP->fr_fix)
{
next_fragP = next_non_empty_frag (next_fragP);
offset = 0;
}
+
if (next_fragP == NULL)
return FALSE;
/* Check for the retw/retw.n. */
- xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
- opcode = xtensa_decode_insn (isa, insnbuf);
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) &next_fragP->fr_literal[offset], 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ /* Because RETW[.N] is not bundleable, a VLIW bundle here means that we
+ have no problems. */
+ if (fmt == XTENSA_UNDEFINED
+ || xtensa_format_num_slots (isa, fmt) != 1)
+ return FALSE;
+
+ xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
- if (is_windowed_return_opcode (opcode))
+ if (opcode == xtensa_retw_opcode || opcode == xtensa_retw_n_opcode)
return TRUE;
+
return FALSE;
}
loop end label, convert this frag to one that will generate a NOP.
In any case close it off with a .fill 0. */
+static bfd_boolean next_instr_is_loop_end (fragS *);
+
static void
-xtensa_fix_b_j_loop_end_frags ()
+xtensa_fix_b_j_loop_end_frags (void)
{
frchainS *frchP;
&& 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);
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
}
}
}
}
-bfd_boolean
-next_instr_is_loop_end (fragP)
- fragS * fragP;
+static bfd_boolean
+next_instr_is_loop_end (fragS *fragP)
{
const fragS *next_fragP;
make it at least 12 bytes away. In any case close it off with a
.fill 0. */
+static offsetT min_bytes_to_other_loop_end
+ (fragS *, fragS *, offsetT, offsetT);
+
static void
-xtensa_fix_close_loop_end_frags ()
+xtensa_fix_close_loop_end_frags (void)
{
frchainS *frchP;
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_IMMED)
+ && ((fragP->fr_subtype == RELAX_IMMED)
+ || ((fragP->fr_subtype == RELAX_SLOTS)
+ && (fragP->tc_frag_data.slot_subtypes[0]
+ == 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, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
{
- 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)
+ if (fragP->tc_frag_data.slot_symbols[0])
{
- current_target = symbol_get_frag (fragP->fr_symbol);
+ symbolS *sym = fragP->tc_frag_data.slot_symbols[0];
+ current_target = symbol_get_frag (sym);
current_offset = fragP->fr_offset;
}
}
&& 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;
+ offsetT min_bytes;
+ int bytes_added = 0;
#define REQUIRED_LOOP_DIVIDING_BYTES 12
/* Max out at 12. */
if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
{
- while (min_bytes + bytes_added
- < REQUIRED_LOOP_DIVIDING_BYTES)
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("loop end too close to another loop end may trigger hardware errata"));
+ else
{
- int length = 3;
-
- if (fragP->fr_var < length)
- as_warn (_("fr_var %lu < length %d; ignoring"),
- fragP->fr_var, length);
- else
+ while (min_bytes + bytes_added
+ < REQUIRED_LOOP_DIVIDING_BYTES)
{
- assemble_nop (length,
- fragP->fr_literal + fragP->fr_fix);
- fragP->fr_fix += length;
- fragP->fr_var -= length;
+ int length = 3;
+
+ if (fragP->fr_var < length)
+ as_fatal (_("fr_var %lu < length %d"),
+ 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;
}
- bytes_added += length;
}
}
frag_wane (fragP);
}
+ assert (fragP->fr_type != rs_machine_dependent
+ || fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
}
}
}
-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;
+static offsetT unrelaxed_frag_min_size (fragS *);
+
+static offsetT
+min_bytes_to_other_loop_end (fragS *fragP,
+ fragS *current_target,
+ offsetT current_offset,
+ offsetT max_size)
{
- size_t offset = 0;
+ offsetT offset = 0;
fragS *current_fragP;
for (current_fragP = fragP;
}
-size_t
-unrelaxed_frag_min_size (fragP)
- fragS * fragP;
+static offsetT
+unrelaxed_frag_min_size (fragS *fragP)
{
- size_t size = fragP->fr_fix;
+ offsetT size = fragP->fr_fix;
- /* add fill size */
+ /* Add fill size. */
if (fragP->fr_type == rs_fill)
size += fragP->fr_offset;
}
+static offsetT
+unrelaxed_frag_max_size (fragS *fragP)
+{
+ offsetT size = fragP->fr_fix;
+ switch (fragP->fr_type)
+ {
+ case 0:
+ /* Empty frags created by the obstack allocation scheme
+ end up with type 0. */
+ break;
+ case rs_fill:
+ case rs_org:
+ case rs_space:
+ size += fragP->fr_offset;
+ break;
+ case rs_align:
+ case rs_align_code:
+ case rs_align_test:
+ case rs_leb128:
+ case rs_cfa:
+ case rs_dwarf2dbg:
+ /* No further adjustments needed. */
+ break;
+ case rs_machine_dependent:
+ if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
+ size += fragP->fr_var;
+ break;
+ default:
+ /* We had darn well better know how big it is. */
+ assert (0);
+ break;
+ }
+
+ return size;
+}
+
+
/* Re-process all of the fragments looking to convert all
of the RELAX_ADD_NOP_IF_SHORT_LOOP. If:
2) loop has a jump or branch in it
or B)
- 1) software_avoid_all_short_loops is true
+ 1) workaround_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 int count_insns_to_loop_end (fragS *, bfd_boolean, int);
+static bfd_boolean branch_before_loop_end (fragS *);
+
static void
-xtensa_fix_short_loop_frags ()
+xtensa_fix_short_loop_frags (void)
{
frchainS *frchP;
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
- /* check on the current loop */
+ /* Check on the current loop. */
if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_IMMED)
+ && ((fragP->fr_subtype == RELAX_IMMED)
+ || ((fragP->fr_subtype == RELAX_SLOTS)
+ && (fragP->tc_frag_data.slot_subtypes[0]
+ == RELAX_IMMED))))
{
+ TInsn t_insn;
+
/* 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_from_chars (&t_insn, fragP->fr_opcode, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
{
- 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)
+ if (fragP->tc_frag_data.slot_symbols[0])
{
- current_target = symbol_get_frag (fragP->fr_symbol);
+ symbolS *sym = fragP->tc_frag_data.slot_symbols[0];
+ current_target = symbol_get_frag (sym);
current_offset = fragP->fr_offset;
- current_opcode = opcode;
+ current_opcode = t_insn.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
+ if (count_insns_to_loop_end (fragP->fr_next, TRUE, 3) < 3
&& (branch_before_loop_end (fragP->fr_next)
- || (software_avoid_all_short_loops
+ || (workaround_all_short_loops
&& current_opcode != XTENSA_UNDEFINED
- && !is_the_loop_opcode (current_opcode))))
- relax_frag_add_nop (fragP);
- else
- frag_wane (fragP);
+ && current_opcode != xtensa_loop_opcode)))
+ {
+ if (fragP->tc_frag_data.is_no_transform)
+ as_bad (_("loop containing less than three instructions may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
}
}
}
}
-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;
+static int unrelaxed_frag_min_insn_count (fragS *);
+
+static int
+count_insns_to_loop_end (fragS *base_fragP,
+ bfd_boolean count_relax_add,
+ int max_count)
{
fragS *fragP = NULL;
- size_t insn_count = 0;
+ int insn_count = 0;
fragP = base_fragP;
}
-size_t
-unrelaxed_frag_min_insn_count (fragP)
- fragS *fragP;
+static int
+unrelaxed_frag_min_insn_count (fragS *fragP)
{
- size_t insn_count = 0;
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf insnbuf = NULL;
+ int insn_count = 0;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return insn_count;
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
/* Decode the fixed instructions. */
while (offset < fragP->fr_fix)
{
- xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
- if (opcode == XTENSA_UNDEFINED)
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ if (fmt == XTENSA_UNDEFINED)
{
as_fatal (_("undecodable instruction in instruction frag"));
return insn_count;
}
- offset += xtensa_insn_length (xtensa_default_isa, opcode);
+ offset += xtensa_format_length (isa, fmt);
insn_count++;
}
}
-bfd_boolean
-branch_before_loop_end (base_fragP)
- fragS *base_fragP;
+static bfd_boolean unrelaxed_frag_has_b_j (fragS *);
+
+static bfd_boolean
+branch_before_loop_end (fragS *base_fragP)
{
fragS *fragP;
}
-bfd_boolean
-unrelaxed_frag_has_b_j (fragP)
- fragS *fragP;
+static bfd_boolean
+unrelaxed_frag_has_b_j (fragS *fragP)
{
- size_t insn_count = 0;
+ static xtensa_insnbuf insnbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return FALSE;
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
/* Decode the fixed instructions. */
while (offset < fragP->fr_fix)
{
- xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
- if (opcode == XTENSA_UNDEFINED)
+ xtensa_format fmt;
+ int slot;
+
+ xtensa_insnbuf_from_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_literal + offset, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return FALSE;
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
{
- as_fatal (_("undecodable instruction in instruction frag"));
- return insn_count;
+ xtensa_opcode opcode =
+ get_opcode_from_buf (fragP->fr_literal + offset, slot);
+ if (xtensa_opcode_is_branch (isa, opcode) == 1
+ || xtensa_opcode_is_jump (isa, opcode) == 1)
+ return TRUE;
}
- if (is_branch_or_jump_opcode (opcode))
- return TRUE;
- offset += xtensa_insn_length (xtensa_default_isa, opcode);
+ offset += xtensa_format_length (isa, fmt);
}
return FALSE;
}
/* Checks to be made after initial assembly but before relaxation. */
+static bfd_boolean is_empty_loop (const TInsn *, fragS *);
+static bfd_boolean is_local_forward_loop (const TInsn *, fragS *);
+
static void
-xtensa_sanity_check ()
+xtensa_sanity_check (void)
{
char *file_name;
- int line;
+ unsigned line;
frchainS *frchP;
{
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
+ tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
+ tinsn_immed_from_frag (&t_insn, fragP, 0);
- if (is_loop_opcode (t_insn.opcode))
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
{
if (is_empty_loop (&t_insn, fragP))
{
#define LOOP_IMMED_OPN 1
-/* Return true if the loop target is the next non-zero fragment. */
+/* Return TRUE if the loop target is the next non-zero fragment. */
-bfd_boolean
-is_empty_loop (insn, fragP)
- const TInsn *insn;
- fragS *fragP;
+static bfd_boolean
+is_empty_loop (const TInsn *insn, fragS *fragP)
{
const expressionS *expr;
symbolS *symbolP;
if (insn->insn_type != ITYPE_INSN)
return FALSE;
- if (!is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
return FALSE;
if (insn->ntok <= LOOP_IMMED_OPN)
/* 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)
}
-bfd_boolean
-is_local_forward_loop (insn, fragP)
- const TInsn *insn;
- fragS *fragP;
+static bfd_boolean
+is_local_forward_loop (const TInsn *insn, fragS *fragP)
{
const expressionS *expr;
symbolS *symbolP;
if (insn->insn_type != ITYPE_INSN)
return FALSE;
- if (!is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) == 0)
return FALSE;
if (insn->ntok <= LOOP_IMMED_OPN)
/* 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;
+ {
+ if (next_fragP == symbol_get_frag (symbolP))
+ return TRUE;
+ }
return FALSE;
}
\f
/* Alignment Functions. */
-size_t
-get_text_align_power (target_size)
- int target_size;
+static int
+get_text_align_power (unsigned target_size)
{
- size_t i = 0;
- for (i = 0; i < sizeof (size_t); i++)
+ int i = 0;
+ unsigned power = 1;
+
+ assert (target_size <= INT_MAX);
+ while (target_size > power)
{
- if (target_size <= (1 << i))
- return i;
+ power <<= 1;
+ i += 1;
}
- as_fatal (_("get_text_align_power: argument too large"));
- return 0;
+ return i;
}
-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;
+static int
+get_text_align_max_fill_size (int align_pow,
+ bfd_boolean use_nops,
+ bfd_boolean use_no_density)
{
if (!use_nops)
return (1 << align_pow);
}
-/* get_text_align_fill_size ()
+/* Calculate the minimum bytes of fill needed at "address" to align a
+ target instruction of size "target_size" so that it does not cross a
+ power-of-two boundary specified by "align_pow". If "use_nops" is FALSE,
+ the fill can be an arbitrary number of bytes. Otherwise, the space must
+ be filled by NOP instructions. */
+
+static int
+get_text_align_fill_size (addressT address,
+ int align_pow,
+ int target_size,
+ bfd_boolean use_nops,
+ bfd_boolean use_no_density)
+{
+ addressT alignment, fill, fill_limit, fill_step;
+ bfd_boolean skip_one = FALSE;
+
+ alignment = (1 << align_pow);
+ assert (target_size > 0 && alignment >= (addressT) target_size);
- 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;
+ fill_limit = alignment;
+ fill_step = 1;
}
-
- /* This is the slightly harder case. */
- assert ((int) alignment >= target_size);
- assert (target_size > 0);
- if (!use_no_density)
+ else 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;
- }
+ /* Combine 2- and 3-byte NOPs to fill anything larger than one. */
+ fill_limit = alignment * 2;
+ fill_step = 1;
+ skip_one = TRUE;
}
else
{
- size_t i;
+ /* Fill with 3-byte NOPs -- can only fill multiples of 3. */
+ fill_limit = alignment * 3;
+ fill_step = 3;
+ }
- /* 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;
- }
+ /* Try all fill sizes until finding one that works. */
+ for (fill = 0; fill < fill_limit; fill += fill_step)
+ {
+ if (skip_one && fill == 1)
+ continue;
+ if ((address + fill) >> align_pow
+ == (address + fill + target_size - 1) >> align_pow)
+ return fill;
}
assert (0);
return 0;
}
+static int
+branch_align_power (segT sec)
+{
+ /* If the Xtensa processor has a fetch width of 8 bytes, and the section
+ is aligned to at least an 8-byte boundary, then a branch target need
+ only fit within an 8-byte aligned block of memory to avoid a stall.
+ Otherwise, try to fit branch targets within 4-byte aligned blocks
+ (which may be insufficient, e.g., if the section has no alignment, but
+ it's good enough). */
+ if (xtensa_fetch_width == 8)
+ {
+ if (get_recorded_alignment (sec) >= 3)
+ return 3;
+ }
+ else
+ assert (xtensa_fetch_width == 4);
+
+ return 2;
+}
+
+
/* 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;
+static int
+get_text_align_nop_count (offsetT fill_size, bfd_boolean use_no_density)
{
- size_t count = 0;
+ int count = 0;
+
if (use_no_density)
{
assert (fill_size % 3 == 0);
while (fill_size > 1)
{
- size_t insn_size = 3;
+ int insn_size = 3;
if (fill_size == 2 || fill_size == 4)
insn_size = 2;
fill_size -= insn_size;
}
-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;
+static int
+get_text_align_nth_nop_size (offsetT fill_size,
+ int n,
+ bfd_boolean use_no_density)
{
- size_t count = 0;
-
- assert (get_text_align_nop_count (fill_size, use_no_density) > n);
+ int count = 0;
if (use_no_density)
return 3;
+ assert (fill_size != 1); /* Bad argument. */
+
while (fill_size > 1)
{
- size_t insn_size = 3;
+ int insn_size = 3;
if (fill_size == 2 || fill_size == 4)
insn_size = 2;
fill_size -= insn_size;
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;
+get_noop_aligned_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;
+ /* 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.
+
+ Note again here that LOOP instructions are not bundleable,
+ and this relaxation only applies to LOOP opcodes. */
+
+ int fill_size = 0;
+ int first_insn_size;
+ int loop_insn_size;
+ addressT pre_opcode_bytes;
+ int align_power;
+ fragS *first_insn;
+ xtensa_opcode opcode;
+ bfd_boolean is_loop;
- if (opcode == XTENSA_UNDEFINED)
- {
- as_bad_where (fragP->fr_file, fragP->fr_line,
- _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
- as_fatal (_("cannot continue"));
- }
+ assert (fragP->fr_type == rs_machine_dependent);
+ assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
- target_insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
+ /* Find the loop frag. */
+ first_insn = next_non_empty_frag (fragP);
+ /* Now find the first insn frag. */
+ first_insn = next_non_empty_frag (first_insn);
- pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
+ is_loop = next_frag_opcode_is_loop (fragP, &opcode);
+ assert (is_loop);
+ loop_insn_size = xg_get_single_size (opcode);
- 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 = next_frag_pre_opcode_bytes (fragP);
+ pre_opcode_bytes += loop_insn_size;
- pre_opcode_bytes += target_insn_size;
+ /* For loops, the alignment depends on the size of the
+ instruction following the loop, not the LOOP instruction. */
- /* 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 (first_insn == NULL)
+ return address;
- if (target_insn_size == 2)
- target_insn_size = 3; /* ISA specifies this. */
- }
+ assert (first_insn->tc_frag_data.is_first_loop_insn);
- /* If it was 8, then we'll need a larger alignment
- for the section. */
- alignment = get_text_align_power (target_insn_size);
+ first_insn_size = frag_format_size (first_insn);
- /* Is Now_seg valid */
- record_alignment (now_seg, alignment);
- }
- else
- as_fatal (_("expected loop opcode in relax align next target"));
+ if (first_insn_size == 2 || first_insn_size == XTENSA_UNDEFINED)
+ first_insn_size = 3; /* ISA specifies this */
- 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"));
- }
+ /* If it was 8, then we'll need a larger alignment for the section. */
+ align_power = get_text_align_power (first_insn_size);
+ record_alignment (now_seg, align_power);
+
+ fill_size = get_text_align_fill_size
+ (address + pre_opcode_bytes, align_power, first_insn_size, TRUE,
+ fragP->tc_frag_data.is_no_density);
return address + fill_size;
}
-/* 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.
-
+/* 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)
+ zeros - This is easy; always insert zeros.
+ nops - 3-byte 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;
+static offsetT
+get_aligned_diff (fragS *fragP, addressT address, offsetT *max_diff)
{
- 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;
- }
+ addressT target_address, loop_insn_offset;
+ int target_size;
+ xtensa_opcode loop_opcode;
+ bfd_boolean is_loop;
+ int align_power;
+ offsetT opt_diff;
+ addressT branch_align;
- if (fragP->fr_type == rs_machine_dependent)
+ assert (fragP->fr_type == rs_machine_dependent);
+ switch (fragP->fr_subtype)
{
- 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);
+ case RELAX_DESIRE_ALIGN:
+ target_size = next_frag_format_size (fragP);
+ if (target_size == XTENSA_UNDEFINED)
+ target_size = 3;
+ align_power = branch_align_power (now_seg);
+ branch_align = 1 << align_power;
+ opt_diff = get_text_align_fill_size (address, align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = (opt_diff + branch_align
+ - (target_size + ((address + opt_diff) % branch_align)));
+ assert (*max_diff >= opt_diff);
+ return opt_diff;
- 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;
+ case RELAX_ALIGN_NEXT_OPCODE:
+ target_size = next_frag_format_size (fragP);
+ loop_insn_offset = 0;
+ is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
+ assert (is_loop);
+
+ /* If the loop has been expanded then the LOOP instruction
+ could be at an offset from this fragment. */
+ if (next_non_empty_frag(fragP)->tc_frag_data.slot_subtypes[0]
+ != RELAX_IMMED)
+ loop_insn_offset = get_expanded_loop_offset (loop_opcode);
+
+ if (target_size == 2)
+ target_size = 3; /* ISA specifies this */
+
+ /* In an ideal world, which is what we are shooting for here,
+ we wouldn't need to use any NOPs immediately prior to the
+ LOOP instruction. If this approach fails, relax_frag_loop_align
+ will call get_noop_aligned_address. */
+ target_address =
+ address + loop_insn_offset + xg_get_single_size (loop_opcode);
+ align_power = get_text_align_power (target_size),
+ opt_diff = get_text_align_fill_size (target_address, align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = xtensa_fetch_width
+ - ((target_address + opt_diff) % xtensa_fetch_width)
+ - target_size + opt_diff;
+ assert (*max_diff >= opt_diff);
+ return opt_diff;
- default:
- as_bad (_("internal error aligning"));
- return address;
- }
+ default:
+ break;
}
- as_bad (_("internal error aligning"));
- return address;
+ assert (0);
+ return 0;
}
\f
/* md_relax_frag Hook and Helper Functions. */
+static long relax_frag_loop_align (fragS *, long);
+static long relax_frag_for_align (fragS *, long);
+static long relax_frag_immed
+ (segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean);
+
+
/* 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;
+xtensa_relax_frag (fragS *fragP, long stretch, int *stretched_p)
{
+ xtensa_isa isa = xtensa_default_isa;
int unreported = fragP->tc_frag_data.unreported_expansion;
long new_stretch = 0;
char *file_name;
- int line, lit_size;
+ unsigned line;
+ int lit_size;
+ static xtensa_insnbuf vbuf = NULL;
+ int slot, num_slots;
+ xtensa_format fmt;
as_where (&file_name, &line);
new_logical_line (fragP->fr_file, fragP->fr_line);
{
case RELAX_ALIGN_NEXT_OPCODE:
/* Always convert. */
- new_stretch = relax_frag_text_align (fragP, stretch);
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch = relax_frag_loop_align (fragP, stretch);
break;
case RELAX_LOOP_END:
case RELAX_LOOP_END_ADD_NOP:
/* Add a NOP and switch to .fill 0. */
new_stretch = relax_frag_add_nop (fragP);
+ frag_wane (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
+ /* Do nothing. The narrowing before this frag will either align
it or not. */
break;
new_stretch = 4;
break;
- case RELAX_NARROW:
- new_stretch = relax_frag_narrow (fragP, stretch);
- break;
+ case RELAX_SLOTS:
+ if (vbuf == NULL)
+ vbuf = xtensa_insnbuf_alloc (isa);
+
+ xtensa_insnbuf_from_chars
+ (isa, vbuf, (unsigned char *) fragP->fr_opcode, 0);
+ fmt = xtensa_format_decode (isa, vbuf);
+ num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (slot = 0; slot < num_slots; slot++)
+ {
+ switch (fragP->tc_frag_data.slot_subtypes[slot])
+ {
+ case RELAX_NARROW:
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch += relax_frag_for_align (fragP, stretch);
+ break;
+
+ case RELAX_IMMED:
+ case RELAX_IMMED_STEP1:
+ case RELAX_IMMED_STEP2:
+ /* Place the immediate. */
+ new_stretch += relax_frag_immed
+ (now_seg, fragP, stretch,
+ fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+ fmt, slot, stretched_p, FALSE);
+ 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);
+ default:
+ /* This is OK; see the note in xg_assemble_vliw_tokens. */
+ break;
+ }
+ }
break;
case RELAX_LITERAL_POOL_BEGIN:
case RELAX_LITERAL_POOL_END:
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
/* No relaxation required. */
break;
+ case RELAX_FILL_NOP:
+ case RELAX_UNREACHABLE:
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch += relax_frag_for_align (fragP, stretch);
+ break;
+
default:
as_bad (_("bad relaxation state"));
}
+ /* Tell gas we need another relaxation pass. */
+ if (! fragP->tc_frag_data.relax_seen)
+ {
+ fragP->tc_frag_data.relax_seen = TRUE;
+ *stretched_p = 1;
+ }
+
new_logical_line (file_name, line);
return new_stretch;
}
static long
-relax_frag_text_align (fragP, stretch)
- fragS *fragP;
- long stretch;
+relax_frag_loop_align (fragS *fragP, long stretch)
{
addressT old_address, old_next_address, old_size;
addressT new_address, new_next_address, new_size;
addressT growth;
- /* 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. */
+ /* All the frags with relax_frag_for_alignment prior to this one in the
+ section have been done, hopefully eliminating the need for a NOP here.
+ But, this will put it in if necessary. */
/* 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);
+ fragP->tc_frag_data.text_expansion[0]);
old_size = old_next_address - old_address;
/* Calculate the new address of this fragment and the next fragment. */
growth = new_size - old_size;
/* Fix up the text_expansion field and return the new growth. */
- fragP->tc_frag_data.text_expansion += growth;
+ fragP->tc_frag_data.text_expansion[0] += 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. */
+/* Add a NOP instruction. */
static long
-relax_frag_add_nop (fragP)
- fragS *fragP;
+relax_frag_add_nop (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);
+ int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
+ assemble_nop (length, nop_buf);
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);
+ as_fatal (_("fr_var (%ld) < length (%d)"), fragP->fr_var, length);
return 0;
}
fragP->fr_fix += length;
fragP->fr_var -= length;
- frag_wane (fragP);
return length;
}
+static long future_alignment_required (fragS *, long);
+
static long
-relax_frag_narrow (fragP, stretch)
- fragS *fragP;
- long stretch;
+relax_frag_for_align (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. */
+ /* Overview of the relaxation procedure for alignment:
+ We can widen with NOPs or by widening instructions or by filling
+ bytes after jump instructions. Find the opportune places and widen
+ them if necessary. */
+
+ long stretch_me;
+ long diff;
- assert (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_NARROW);
+ assert (fragP->fr_subtype == RELAX_FILL_NOP
+ || fragP->fr_subtype == RELAX_UNREACHABLE
+ || (fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
- if (!future_alignment_required (fragP, 0))
+ stretch_me = future_alignment_required (fragP, stretch);
+ diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
+ if (diff == 0)
+ return 0;
+
+ if (diff < 0)
{
- /* 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)
+ /* We expanded on a previous pass. Can we shrink now? */
+ long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
+ if (shrink <= stretch && stretch > 0)
{
- if (stretch > 0)
- {
- fragP->tc_frag_data.text_expansion = 0;
- return -1;
- }
- /* Otherwise we have to live with this bad choice. */
- return 0;
+ fragP->tc_frag_data.text_expansion[0] = stretch_me;
+ return -shrink;
}
return 0;
}
- if (fragP->tc_frag_data.text_expansion == 0)
- {
- fragP->tc_frag_data.text_expansion = 1;
- return 1;
- }
+ /* Below here, diff > 0. */
+ fragP->tc_frag_data.text_expansion[0] = stretch_me;
- return 0;
+ return diff;
}
-static bfd_boolean
-future_alignment_required (fragP, stretch)
- fragS *fragP;
- long stretch;
+/* Return the address of the next frag that should be aligned.
+
+ By "address" we mean the address it _would_ be at if there
+ is no action taken to align it between here and the target frag.
+ In other words, if no narrows and no fill nops are used between
+ here and the frag to align, _even_if_ some of the frags we use
+ to align targets have already expanded on a previous relaxation
+ pass.
+
+ Also, count each frag that may be used to help align the target.
+
+ Return 0 if there are no frags left in the chain that need to be
+ aligned. */
+
+static addressT
+find_address_of_next_align_frag (fragS **fragPP,
+ int *wide_nops,
+ int *narrow_nops,
+ int *widens,
+ bfd_boolean *paddable)
{
- long address = fragP->fr_address + stretch;
- int num_widens = 0;
- addressT aligned_address;
- offsetT desired_diff;
+ fragS *fragP = *fragPP;
+ addressT address = fragP->fr_address;
+
+ /* Do not reset the counts to 0. */
while (fragP)
{
/* Limit this to a small search. */
- if (num_widens > 8)
- return FALSE;
+ if (*widens > 8)
+ {
+ *fragPP = fragP;
+ return 0;
+ }
address += fragP->fr_fix;
- switch (fragP->fr_type)
+ if (fragP->fr_type == rs_fill)
+ address += fragP->fr_offset * fragP->fr_var;
+ else if (fragP->fr_type == rs_machine_dependent)
{
- 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++;
+ case RELAX_UNREACHABLE:
+ *paddable = TRUE;
+ break;
+
+ case RELAX_FILL_NOP:
+ (*wide_nops)++;
+ if (!fragP->tc_frag_data.is_no_density)
+ (*narrow_nops)++;
+ break;
+
+ case RELAX_SLOTS:
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ {
+ (*widens)++;
+ break;
+ }
+ address += total_frag_text_expansion (fragP);;
break;
case RELAX_IMMED:
- address += (/* fragP->fr_fix + */
- fragP->tc_frag_data.text_expansion);
+ address += fragP->tc_frag_data.text_expansion[0];
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;
+ *fragPP = fragP;
+ return address;
+
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ /* Do nothing. */
break;
default:
- return FALSE;
+ /* Just punt if we don't know the type. */
+ *fragPP = fragP;
+ return 0;
}
- break;
-
- default:
- return FALSE;
+ }
+ else
+ {
+ /* Just punt if we don't know the type. */
+ *fragPP = fragP;
+ return 0;
}
fragP = fragP->fr_next;
}
- return FALSE;
+ *fragPP = fragP;
+ return 0;
}
+static long bytes_to_stretch (fragS *, int, int, int, int);
+
+/* Undefine LOOKAHEAD_ALIGNER to get the older behavior.
+ I'll leave this in until I am more confident this works. */
+
+#define LOOKAHEAD_ALIGNER 1
+
static long
-relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
- segT segP;
- fragS *fragP;
- long stretch;
- int min_steps;
- int *stretched_p;
+future_alignment_required (fragS *fragP, long stretch ATTRIBUTE_UNUSED)
{
- static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn;
+ fragS *this_frag = fragP;
+ long address;
+ int num_widens = 0;
+ int wide_nops = 0;
+ int narrow_nops = 0;
+ bfd_boolean paddable = FALSE;
+ offsetT local_opt_diff;
+ offsetT opt_diff;
+ offsetT max_diff;
+ int stretch_amount = 0;
+ int local_stretch_amount;
+ int global_stretch_amount;
+
+ address = find_address_of_next_align_frag
+ (&fragP, &wide_nops, &narrow_nops, &num_widens, &paddable);
+
+ if (address)
+ {
+ local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
+ opt_diff = local_opt_diff;
+ assert (opt_diff >= 0);
+ assert (max_diff >= opt_diff);
+ if (max_diff == 0)
+ return 0;
+#ifdef LOOKAHEAD_ALIGNER
+ if (fragP)
+ fragP = fragP->fr_next;
+
+ while (fragP && opt_diff < max_diff && address)
+ {
+ /* We only use these to determine if we can exit early
+ because there will be plenty of ways to align future
+ align frags. */
+ int glob_widens = 0;
+ int dnn = 0;
+ int dw = 0;
+ bfd_boolean glob_pad = 0;
+ address = find_address_of_next_align_frag
+ (&fragP, &glob_widens, &dnn, &dw, &glob_pad);
+ /* If there is a padable portion, then skip. */
+ if (glob_pad || glob_widens >= (1 << branch_align_power (now_seg)))
+ break;
+
+ if (address)
+ {
+ offsetT next_m_diff;
+ offsetT next_o_diff;
+
+ /* Downrange frags haven't had stretch added to them yet. */
+ address += stretch;
+
+ /* The address also includes any text expansion from this
+ frag in a previous pass, but we don't want that. */
+ address -= this_frag->tc_frag_data.text_expansion[0];
+
+ /* Assume we are going to move at least opt_diff. In
+ reality, we might not be able to, but assuming that
+ we will helps catch cases where moving opt_diff pushes
+ the next target from aligned to unaligned. */
+ address += opt_diff;
+
+ next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
+
+ /* Now cleanup for the adjustments to address. */
+ next_o_diff += opt_diff;
+ next_m_diff += opt_diff;
+ if (next_o_diff <= max_diff && next_o_diff > opt_diff)
+ opt_diff = next_o_diff;
+ if (next_m_diff < max_diff)
+ max_diff = next_m_diff;
+ fragP = fragP->fr_next;
+ }
+ }
+#endif /* LOOKAHEAD_ALIGNER */
+ /* If there are enough wideners in between, do it. */
+ if (paddable)
+ {
+ if (this_frag->fr_subtype == RELAX_UNREACHABLE)
+ {
+ assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
+ return opt_diff;
+ }
+ return 0;
+ }
+ local_stretch_amount
+ = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+ num_widens, local_opt_diff);
+#ifdef LOOKAHEAD_ALIGNER
+ global_stretch_amount
+ = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+ num_widens, opt_diff);
+ /* If the condition below is true, then the frag couldn't
+ stretch the correct amount for the global case, so we just
+ optimize locally. We'll rely on the subsequent frags to get
+ the correct alignment in the global case. */
+ if (global_stretch_amount < local_stretch_amount)
+ stretch_amount = local_stretch_amount;
+ else
+ stretch_amount = global_stretch_amount;
+#else /* ! LOOKAHEAD_ALIGNER */
+ stretch_amount = local_stretch_amount;
+#endif /* ! LOOKAHEAD_ALIGNER */
+ if (this_frag->fr_subtype == RELAX_SLOTS
+ && this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ assert (stretch_amount <= 1);
+ else if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ {
+ if (this_frag->tc_frag_data.is_no_density)
+ assert (stretch_amount == 3 || stretch_amount == 0);
+ else
+ assert (stretch_amount <= 3);
+ }
+ }
+ return stretch_amount;
+}
+
+
+/* The idea: widen everything you can to get a target or loop aligned,
+ then start using NOPs.
+
+ When we must have a NOP, here is a table of how we decide
+ (so you don't have to fight through the control flow below):
+
+ wide_nops = the number of wide NOPs available for aligning
+ narrow_nops = the number of narrow NOPs available for aligning
+ (a subset of wide_nops)
+ widens = the number of narrow instructions that should be widened
+
+ Desired wide narrow
+ Diff nop nop widens
+ 1 0 0 1
+ 2 0 1 0
+ 3a 1 0 0
+ b 0 1 1 (case 3a makes this case unnecessary)
+ 4a 1 0 1
+ b 0 2 0
+ c 0 1 2 (case 4a makes this case unnecessary)
+ 5a 1 0 2
+ b 1 1 0
+ c 0 2 1 (case 5b makes this case unnecessary)
+ 6a 2 0 0
+ b 1 0 3
+ c 0 1 4 (case 6b makes this case unneccesary)
+ d 1 1 1 (case 6a makes this case unnecessary)
+ e 0 2 2 (case 6a makes this case unnecessary)
+ f 0 3 0 (case 6a makes this case unnecessary)
+ 7a 1 0 4
+ b 2 0 1
+ c 1 1 2 (case 7b makes this case unnecessary)
+ d 0 1 5 (case 7a makes this case unnecessary)
+ e 0 2 3 (case 7b makes this case unnecessary)
+ f 0 3 1 (case 7b makes this case unnecessary)
+ g 1 2 1 (case 7b makes this case unnecessary)
+*/
+
+static long
+bytes_to_stretch (fragS *this_frag,
+ int wide_nops,
+ int narrow_nops,
+ int num_widens,
+ int desired_diff)
+{
+ int bytes_short = desired_diff - num_widens;
+
+ assert (desired_diff >= 0 && desired_diff < 8);
+ if (desired_diff == 0)
+ return 0;
+
+ assert (wide_nops > 0 || num_widens > 0);
+
+ /* Always prefer widening to NOP-filling. */
+ if (bytes_short < 0)
+ {
+ /* There are enough RELAX_NARROW frags after this one
+ to align the target without widening this frag in any way. */
+ return 0;
+ }
+
+ if (bytes_short == 0)
+ {
+ /* Widen every narrow between here and the align target
+ and the align target will be properly aligned. */
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return 0;
+ else
+ return 1;
+ }
+
+ /* From here we will need at least one NOP to get an alignment.
+ However, we may not be able to align at all, in which case,
+ don't widen. */
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ {
+ switch (desired_diff)
+ {
+ case 1:
+ return 0;
+ case 2:
+ if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
+ return 2; /* case 2 */
+ return 0;
+ case 3:
+ if (wide_nops > 1)
+ return 0;
+ else
+ return 3; /* case 3a */
+ case 4:
+ if (num_widens >= 1 && wide_nops == 1)
+ return 3; /* case 4a */
+ if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
+ return 2; /* case 4b */
+ return 0;
+ case 5:
+ if (num_widens >= 2 && wide_nops == 1)
+ return 3; /* case 5a */
+ /* We will need two nops. Are there enough nops
+ between here and the align target? */
+ if (wide_nops < 2 || narrow_nops == 0)
+ return 0;
+ /* Are there other nops closer that can serve instead? */
+ if (wide_nops > 2 && narrow_nops > 1)
+ return 0;
+ /* Take the density one first, because there might not be
+ another density one available. */
+ if (!this_frag->tc_frag_data.is_no_density)
+ return 2; /* case 5b narrow */
+ else
+ return 3; /* case 5b wide */
+ return 0;
+ case 6:
+ if (wide_nops == 2)
+ return 3; /* case 6a */
+ else if (num_widens >= 3 && wide_nops == 1)
+ return 3; /* case 6b */
+ return 0;
+ case 7:
+ if (wide_nops == 1 && num_widens >= 4)
+ return 3; /* case 7a */
+ else if (wide_nops == 2 && num_widens >= 1)
+ return 3; /* case 7b */
+ return 0;
+ default:
+ assert (0);
+ }
+ }
+ else
+ {
+ /* We will need a NOP no matter what, but should we widen
+ this instruction to help?
+
+ This is a RELAX_FRAG_NARROW frag. */
+ switch (desired_diff)
+ {
+ case 1:
+ assert (0);
+ return 0;
+ case 2:
+ case 3:
+ return 0;
+ case 4:
+ if (wide_nops >= 1 && num_widens == 1)
+ return 1; /* case 4a */
+ return 0;
+ case 5:
+ if (wide_nops >= 1 && num_widens == 2)
+ return 1; /* case 5a */
+ return 0;
+ case 6:
+ if (wide_nops >= 2)
+ return 0; /* case 6a */
+ else if (wide_nops >= 1 && num_widens == 3)
+ return 1; /* case 6b */
+ return 0;
+ case 7:
+ if (wide_nops >= 1 && num_widens == 4)
+ return 1; /* case 7a */
+ else if (wide_nops >= 2 && num_widens == 1)
+ return 1; /* case 7b */
+ return 0;
+ default:
+ assert (0);
+ return 0;
+ }
+ }
+ assert (0);
+ return 0;
+}
+
+
+static long
+relax_frag_immed (segT segP,
+ fragS *fragP,
+ long stretch,
+ int min_steps,
+ xtensa_format fmt,
+ int slot,
+ int *stretched_p,
+ bfd_boolean estimate_only)
+{
+ TInsn tinsn;
+ vliw_insn orig_vinsn;
int old_size;
bfd_boolean negatable_branch = FALSE;
bfd_boolean branch_jmp_to_next = FALSE;
+ bfd_boolean wide_insn = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
IStack istack;
offsetT frag_offset;
int num_steps;
fragS *lit_fragP;
int num_text_bytes, num_literal_bytes;
- int literal_diff, text_diff;
+ int literal_diff, total_text_diff, this_text_diff, first;
assert (fragP->fr_opcode != NULL);
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ xg_init_vinsn (&orig_vinsn);
+ vinsn_from_chars (&orig_vinsn, fragP->fr_opcode);
+ if (xtensa_format_num_slots (isa, fmt) > 1)
+ wide_insn = TRUE;
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
+ tinsn = orig_vinsn.slots[slot];
+ tinsn_immed_from_frag (&tinsn, fragP, slot);
- negatable_branch = is_negatable_branch (&t_insn);
+ if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode))
+ return 0;
+
+ if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
+ branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
- old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
+ negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
- if (software_avoid_b_j_loop_end)
- branch_jmp_to_next = is_branch_jmp_to_next (&t_insn, fragP);
+ old_size = xtensa_format_length (isa, fmt);
/* 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
frag_offset = fragP->fr_opcode - fragP->fr_literal;
istack_init (&istack);
- num_steps = xg_assembly_relax (&istack, &t_insn, segP, fragP, frag_offset,
+ num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
min_steps, stretch);
if (num_steps < min_steps)
{
return 0;
}
- fragP->fr_subtype = (int) RELAX_IMMED + num_steps;
+ fragP->tc_frag_data.slot_subtypes[slot] = (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;
+ literal_diff =
+ num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
+ first = 0;
+ while (istack.insn[first].opcode == XTENSA_UNDEFINED)
+ first++;
+ num_text_bytes = get_num_stack_text_bytes (&istack);
+ if (wide_insn)
+ {
+ num_text_bytes += old_size;
+ if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
+ num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
+ }
+ total_text_diff = num_text_bytes - old_size;
+ this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
/* It MUST get larger. If not, we could get an infinite loop. */
- know (num_text_bytes >= 0);
- know (literal_diff >= 0 && text_diff >= 0);
+ assert (num_text_bytes >= 0);
+ assert (literal_diff >= 0);
+ assert (total_text_diff >= 0);
- fragP->tc_frag_data.text_expansion = num_text_bytes;
- fragP->tc_frag_data.literal_expansion = num_literal_bytes;
+ fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
+ fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
+ assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
+ assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
/* Find the associated expandable literal for this. */
if (literal_diff != 0)
{
- lit_fragP = fragP->tc_frag_data.literal_frag;
+ lit_fragP = fragP->tc_frag_data.literal_frags[slot];
if (lit_fragP)
{
assert (literal_diff == 4);
}
}
- /* 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 && istack.ninsn > 1)
+ update_next_frag_state (fragP);
- 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;
+ return this_text_diff;
}
\f
/* md_convert_frag Hook and Helper Functions. */
+static void convert_frag_align_next_opcode (fragS *);
+static void convert_frag_narrow (segT, fragS *, xtensa_format, int);
+static void convert_frag_fill_nop (fragS *);
+static void convert_frag_immed (segT, fragS *, int, xtensa_format, int);
+
void
-md_convert_frag (abfd, sec, fragp)
- bfd *abfd ATTRIBUTE_UNUSED;
- segT sec;
- fragS *fragp;
+md_convert_frag (bfd *abfd ATTRIBUTE_UNUSED, segT sec, fragS *fragp)
{
+ static xtensa_insnbuf vbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ int slot;
+ int num_slots;
+ xtensa_format fmt;
char *file_name;
- int line;
+ unsigned line;
as_where (&file_name, &line);
new_logical_line (fragp->fr_file, fragp->fr_line);
case RELAX_LITERAL_FINAL:
break;
- case RELAX_NARROW:
- /* No conversion. */
- convert_frag_narrow (fragp);
+ case RELAX_SLOTS:
+ if (vbuf == NULL)
+ vbuf = xtensa_insnbuf_alloc (isa);
+
+ xtensa_insnbuf_from_chars
+ (isa, vbuf, (unsigned char *) fragp->fr_opcode, 0);
+ fmt = xtensa_format_decode (isa, vbuf);
+ num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (slot = 0; slot < num_slots; slot++)
+ {
+ switch (fragp->tc_frag_data.slot_subtypes[slot])
+ {
+ case RELAX_NARROW:
+ convert_frag_narrow (sec, fragp, fmt, slot);
+ break;
+
+ case RELAX_IMMED:
+ case RELAX_IMMED_STEP1:
+ case RELAX_IMMED_STEP2:
+ /* Place the immediate. */
+ convert_frag_immed
+ (sec, fragp,
+ fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+ fmt, slot);
+ break;
+
+ default:
+ /* This is OK because some slots could have
+ relaxations and others have none. */
+ break;
+ }
+ }
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);
+ case RELAX_UNREACHABLE:
+ memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
+ fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
+ fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
+ frag_wane (fragp);
+ break;
+
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ frag_wane (fragp);
+ break;
+
+ case RELAX_FILL_NOP:
+ convert_frag_fill_nop (fragp);
break;
case RELAX_LITERAL_NR:
}
-void
-convert_frag_align_next_opcode (fragp)
- fragS *fragp;
+static void
+convert_frag_align_next_opcode (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;
+ offsetT fill_size;
+ int nop, nop_count;
aligned_address = get_noop_aligned_address (fragp, 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++)
+ for (nop = 0; nop < nop_count; nop++)
{
- size_t nop_size;
- nop_size = get_text_align_nth_nop_size (fill_size, i, use_no_density);
+ int nop_size;
+ nop_size = get_text_align_nth_nop_size (fill_size, nop, use_no_density);
assemble_nop (nop_size, nop_buf);
nop_buf += nop_size;
static void
-convert_frag_narrow (fragP)
- fragS *fragP;
+convert_frag_narrow (segT segP, fragS *fragP, xtensa_format fmt, int slot)
{
- static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn, single_target;
+ TInsn tinsn, single_target;
+ xtensa_format single_fmt;
int size, old_size, diff, error_val;
offsetT frag_offset;
- if (fragP->tc_frag_data.text_expansion == 0)
+ assert (slot == 0);
+ tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
+
+ if (xtensa_opcode_is_branch (xtensa_default_isa, tinsn.opcode) == 1)
+ {
+ assert (fragP->tc_frag_data.text_expansion[0] == 1
+ || fragP->tc_frag_data.text_expansion[0] == 0);
+ convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
+ fmt, slot);
+ return;
+ }
+
+ if (fragP->tc_frag_data.text_expansion[0] == 0)
{
/* No conversion. */
fragP->fr_var = 0;
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);
+ /* Frags in this relaxation state should only contain
+ single instruction bundles. */
+ tinsn_immed_from_frag (&tinsn, fragP, 0);
/* Just convert it to a wide form.... */
size = 0;
- old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
+ old_size = xg_get_single_size (tinsn.opcode);
tinsn_init (&single_target);
frag_offset = fragP->fr_opcode - fragP->fr_literal;
- error_val = xg_expand_narrow (&single_target, &t_insn);
+ error_val = xg_expand_narrow (&single_target, &tinsn);
if (error_val)
- as_bad (_("unable to widen instruction"));
+ {
+ as_bad (_("unable to widen instruction"));
+ return;
+ }
+
+ size = xg_get_single_size (single_target.opcode);
+ single_fmt = xg_get_single_format (single_target.opcode);
- size = xtensa_insn_length (xtensa_default_isa, single_target.opcode);
- xg_emit_insn_to_buf (&single_target, fragP->fr_opcode,
+ xg_emit_insn_to_buf (&single_target, single_fmt, fragP->fr_opcode,
fragP, frag_offset, TRUE);
diff = size - old_size;
static void
-convert_frag_immed (segP, fragP, min_steps)
- segT segP;
- fragS *fragP;
- int min_steps;
+convert_frag_fill_nop (fragS *fragP)
+{
+ char *loc = &fragP->fr_literal[fragP->fr_fix];
+ int size = fragP->tc_frag_data.text_expansion[0];
+ assert ((unsigned) size == (fragP->fr_next->fr_address
+ - fragP->fr_address - fragP->fr_fix));
+ if (size == 0)
+ {
+ /* No conversion. */
+ fragP->fr_var = 0;
+ return;
+ }
+ assemble_nop (size, loc);
+ fragP->tc_frag_data.is_insn = TRUE;
+ fragP->fr_var -= size;
+ fragP->fr_fix += size;
+ frag_wane (fragP);
+}
+
+
+static fixS *fix_new_exp_in_seg
+ (segT, subsegT, fragS *, int, int, expressionS *, int,
+ bfd_reloc_code_real_type);
+static void convert_frag_immed_finish_loop (segT, fragS *, TInsn *);
+
+static void
+convert_frag_immed (segT segP,
+ fragS *fragP,
+ int min_steps,
+ xtensa_format fmt,
+ int slot)
{
char *immed_instr = fragP->fr_opcode;
- static xtensa_insnbuf insnbuf = NULL;
- TInsn orig_t_insn;
+ TInsn orig_tinsn;
bfd_boolean expanded = FALSE;
- char *fr_opcode = fragP->fr_opcode;
bfd_boolean branch_jmp_to_next = FALSE;
- int size;
+ char *fr_opcode = fragP->fr_opcode;
+ vliw_insn orig_vinsn;
+ xtensa_isa isa = xtensa_default_isa;
+ bfd_boolean wide_insn = FALSE;
+ int bytes;
+ bfd_boolean is_loop;
- assert (fragP->fr_opcode != NULL);
+ assert (fr_opcode != NULL);
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ xg_init_vinsn (&orig_vinsn);
- tinsn_from_chars (&orig_t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&orig_t_insn, fragP);
+ vinsn_from_chars (&orig_vinsn, fr_opcode);
+ if (xtensa_format_num_slots (isa, fmt) > 1)
+ wide_insn = TRUE;
- /* 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. */
+ orig_tinsn = orig_vinsn.slots[slot];
+ tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
- if (software_avoid_b_j_loop_end)
- branch_jmp_to_next = is_branch_jmp_to_next (&orig_t_insn, fragP);
+ is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
+
+ if (workaround_b_j_loop_end && ! fragP->tc_frag_data.is_no_transform)
+ branch_jmp_to_next = is_branch_jmp_to_next (&orig_tinsn, fragP);
if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
{
/* 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);
+ bytes = xtensa_format_length (isa, fmt);
+ if (bytes >= 4)
+ {
+ orig_vinsn.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, orig_vinsn.format, slot);
+ orig_vinsn.slots[slot].ntok = 0;
+ }
+ else
+ {
+ bytes += fragP->tc_frag_data.text_expansion[0];
+ assert (bytes == 2 || bytes == 3);
+ build_nop (&orig_vinsn.slots[0], bytes);
+ fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
+ }
+ vinsn_to_insnbuf (&orig_vinsn, fr_opcode, frag_now, FALSE);
+ xtensa_insnbuf_to_chars
+ (isa, orig_vinsn.insnbuf, (unsigned char *) fr_opcode, 0);
fragP->fr_var = 0;
}
else
{
+ /* 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. */
+
IStack istack;
int i;
symbolS *lit_sym = NULL;
int total_size = 0;
+ int target_offset = 0;
int old_size;
int diff;
symbolS *gen_label = NULL;
offsetT frag_offset;
+ bfd_boolean first = TRUE;
+ bfd_boolean last_is_jump;
- /* It does not fit. Find something that does and
+ /* It does not fit. Find something that does and
convert immediately. */
- frag_offset = fragP->fr_opcode - fragP->fr_literal;
+ frag_offset = fr_opcode - fragP->fr_literal;
istack_init (&istack);
- xg_assembly_relax (&istack, &orig_t_insn,
+ xg_assembly_relax (&istack, &orig_tinsn,
segP, fragP, frag_offset, min_steps, 0);
- old_size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
+ old_size = xtensa_format_length (isa, fmt);
/* Assemble this right inline. */
/* First, create the mapping from a label name to the REAL label. */
- total_size = 0;
+ target_offset = 0;
for (i = 0; i < istack.ninsn; i++)
{
- TInsn *t_insn = &istack.insn[i];
- int size = 0;
+ TInsn *tinsn = &istack.insn[i];
fragS *lit_frag;
- switch (t_insn->insn_type)
+ switch (tinsn->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;
+ lit_frag = fragP->tc_frag_data.literal_frags[slot];
if (lit_frag == NULL)
as_bad (_("no registered fragment for literal"));
- if (t_insn->ntok != 1)
+ if (tinsn->ntok != 1)
as_bad (_("number of literal tokens != 1"));
/* Set the literal symbol and add a fixup. */
break;
case ITYPE_LABEL:
+ if (align_targets && !is_loop)
+ {
+ fragS *unreach = fragP->fr_next;
+ while (!(unreach->fr_type == rs_machine_dependent
+ && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || unreach->fr_subtype == RELAX_UNREACHABLE)))
+ {
+ unreach = unreach->fr_next;
+ }
+
+ assert (unreach->fr_type == rs_machine_dependent
+ && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || unreach->fr_subtype == RELAX_UNREACHABLE));
+
+ target_offset += unreach->tc_frag_data.text_expansion[0];
+ }
assert (gen_label == NULL);
gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
- fragP->fr_opcode - fragP->fr_literal +
- total_size, fragP);
+ fr_opcode - fragP->fr_literal
+ + target_offset, fragP);
break;
case ITYPE_INSN:
- size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
- total_size += size;
+ if (first && wide_insn)
+ {
+ target_offset += xtensa_format_length (isa, fmt);
+ first = FALSE;
+ if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ target_offset += xg_get_single_size (tinsn->opcode);
+ }
+ else
+ target_offset += xg_get_single_size (tinsn->opcode);
break;
}
}
total_size = 0;
+ first = TRUE;
+ last_is_jump = FALSE;
for (i = 0; i < istack.ninsn; i++)
{
- TInsn *t_insn = &istack.insn[i];
+ TInsn *tinsn = &istack.insn[i];
fragS *lit_frag;
int size;
segT target_seg;
+ bfd_reloc_code_real_type reloc_type;
- switch (t_insn->insn_type)
+ switch (tinsn->insn_type)
{
case ITYPE_LITERAL:
- lit_frag = fragP->tc_frag_data.literal_frag;
- /* already checked */
+ lit_frag = fragP->tc_frag_data.literal_frags[slot];
+ /* Already checked. */
assert (lit_frag != NULL);
assert (lit_sym != NULL);
- assert (t_insn->ntok == 1);
- /* add a fixup */
+ assert (tinsn->ntok == 1);
+ /* Add a fixup. */
target_seg = S_GET_SEGMENT (lit_sym);
assert (target_seg);
+ if (tinsn->tok[0].X_op == O_pltrel)
+ reloc_type = BFD_RELOC_XTENSA_PLT;
+ else
+ reloc_type = BFD_RELOC_32;
fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
- &t_insn->tok[0], FALSE, BFD_RELOC_32);
+ &tinsn->tok[0], FALSE, reloc_type);
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);
+ xg_resolve_labels (tinsn, gen_label);
+ xg_resolve_literals (tinsn, lit_sym);
+ if (wide_insn && first)
+ {
+ first = FALSE;
+ if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ {
+ tinsn->record_fix = TRUE;
+ orig_vinsn.slots[slot] = *tinsn;
+ }
+ else
+ {
+ orig_vinsn.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, fmt, slot);
+ orig_vinsn.slots[slot].ntok = 0;
+ orig_vinsn.slots[slot].record_fix = FALSE;
+ }
+ vinsn_to_insnbuf (&orig_vinsn, immed_instr, fragP, TRUE);
+ xtensa_insnbuf_to_chars (isa, orig_vinsn.insnbuf,
+ (unsigned char *) immed_instr, 0);
+ fragP->tc_frag_data.is_insn = TRUE;
+ size = xtensa_format_length (isa, fmt);
+ if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ {
+ xtensa_format single_fmt =
+ xg_get_single_format (tinsn->opcode);
+
+ xg_emit_insn_to_buf
+ (tinsn, single_fmt, immed_instr + size, fragP,
+ immed_instr - fragP->fr_literal + size, TRUE);
+ size += xg_get_single_size (tinsn->opcode);
+ }
+ }
+ else
+ {
+ xtensa_format single_format;
+ size = xg_get_single_size (tinsn->opcode);
+ single_format = xg_get_single_format (tinsn->opcode);
+ xg_emit_insn_to_buf (tinsn, single_format, immed_instr,
+ fragP,
+ immed_instr - fragP->fr_literal, TRUE);
+ }
immed_instr += size;
+ total_size += size;
break;
}
}
}
/* Clean it up. */
- fragP->fr_var = 0;
+ xg_free_vinsn (&orig_vinsn);
/* Check for undefined immediates in LOOP instructions. */
- if (is_loop_opcode (orig_t_insn.opcode))
+ if (is_loop)
{
symbolS *sym;
- sym = orig_t_insn.tok[1].X_add_symbol;
+ sym = orig_tinsn.tok[1].X_add_symbol;
if (sym != NULL && !S_IS_DEFINED (sym))
{
as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
return;
}
- sym = orig_t_insn.tok[1].X_op_symbol;
+ sym = orig_tinsn.tok[1].X_op_symbol;
if (sym != NULL && !S_IS_DEFINED (sym))
{
as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
}
}
- if (expanded && is_loop_opcode (orig_t_insn.opcode))
- convert_frag_immed_finish_loop (segP, fragP, &orig_t_insn);
+ if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
+ convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
- if (expanded && is_direct_call_opcode (orig_t_insn.opcode))
+ if (expanded && is_direct_call_opcode (orig_tinsn.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,
+ &orig_tinsn.tok[0], TRUE,
BFD_RELOC_XTENSA_ASM_EXPAND);
-
}
}
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;
+fix_new_exp_in_seg (segT new_seg,
+ subsegT new_subseg,
+ fragS *frag,
+ int where,
+ int size,
+ expressionS *exp,
+ int pcrel,
+ bfd_reloc_code_real_type r_type)
{
fixS *new_fix;
segT seg = now_seg;
subsegT subseg = now_subseg;
+
assert (new_seg != 0);
subseg_set (new_seg, new_subseg);
- 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: */
+/* 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;
+convert_frag_immed_finish_loop (segT segP, fragS *fragP, TInsn *tinsn)
{
TInsn loop_insn;
TInsn addi_insn;
addressT loop_offset;
addressT addi_offset = 9;
addressT addmi_offset = 12;
+ fragS *next_fragP;
+ int target_count;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
/* Get the loop offset. */
- loop_offset = get_expanded_loop_offset (t_insn->opcode);
- /* Validate that there really is a LOOP at the loop_offset. */
- tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset);
+ loop_offset = get_expanded_loop_offset (tinsn->opcode);
- if (!is_loop_opcode (loop_insn.opcode))
- {
- as_bad_where (fragP->fr_file, fragP->fr_line,
- _("loop relaxation specification does not correspond"));
- assert (0);
- }
+ /* Validate that there really is a LOOP at the loop_offset. Because
+ loops are not bundleable, we can assume that the instruction will be
+ in slot 0. */
+ tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
+ tinsn_immed_from_frag (&loop_insn, fragP, 0);
+
+ assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
addi_offset += loop_offset;
addmi_offset += loop_offset;
- assert (t_insn->ntok == 2);
- target = get_expression_value (segP, &t_insn->tok[1]);
+ assert (tinsn->ntok == 2);
+ if (tinsn->tok[1].X_op == O_constant)
+ target = tinsn->tok[1].X_add_number;
+ else if (tinsn->tok[1].X_op == O_symbol)
+ {
+ /* Find the fragment. */
+ symbolS *sym = tinsn->tok[1].X_add_symbol;
+ assert (S_GET_SEGMENT (sym) == segP
+ || S_GET_SEGMENT (sym) == absolute_section);
+ target = (S_GET_VALUE (sym) + tinsn->tok[1].X_add_number);
+ }
+ else
+ {
+ as_bad (_("invalid expression evaluation type %d"), tinsn->tok[1].X_op);
+ target = 0;
+ }
know (symbolP);
know (symbolP->sy_frag);
loop_length_hi += 256;
}
- /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
+ /* 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);
+ tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
assert (addi_insn.opcode == xtensa_addi_opcode);
- tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset);
+ tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
assert (addmi_insn.opcode == xtensa_addmi_opcode);
set_expr_const (&addi_insn.tok[2], loop_length_lo);
tinsn_to_insnbuf (&addi_insn, insnbuf);
-
+
fragP->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addi_offset);
+ xtensa_insnbuf_to_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addi_offset, 0);
set_expr_const (&addmi_insn.tok[2], loop_length_hi);
tinsn_to_insnbuf (&addmi_insn, insnbuf);
- xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addmi_offset);
-}
+ xtensa_insnbuf_to_chars
+ (isa, insnbuf, (unsigned char *) fragP->fr_opcode + addmi_offset, 0);
+ /* Walk through all of the frags from here to the loop end
+ and mark them as no_transform to keep them from being modified
+ by the linker. If we ever have a relocation for the
+ addi/addmi of the difference of two symbols we can remove this. */
-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)
+ target_count = 0;
+ for (next_fragP = fragP; next_fragP != NULL;
+ next_fragP = next_fragP->fr_next)
{
- /* 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);
+ next_fragP->tc_frag_data.is_no_transform = TRUE;
+ if (next_fragP->tc_frag_data.is_loop_target)
+ target_count++;
+ if (target_count == 2)
+ break;
}
- as_bad (_("invalid expression evaluation type %d"), exp->X_op);
- return 0;
}
\f
/* the data */
unsigned flags;
+ float total_freq; /* fall-through + branch target frequency */
+ float target_freq; /* branch target frequency alone */
struct subseg_map_struct *next;
} subseg_map;
-static subseg_map *sseg_map = NULL;
+static subseg_map *sseg_map = NULL;
-static unsigned
-get_last_insn_flags (seg, subseg)
- segT seg;
- subsegT subseg;
+static subseg_map *
+get_subseg_info (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;
+ for (subseg_e = sseg_map; subseg_e; subseg_e = subseg_e->next)
+ {
+ if (seg == subseg_e->seg && subseg == subseg_e->subseg)
+ break;
+ }
+ return subseg_e;
}
-static void
-set_last_insn_flags (seg, subseg, fl, val)
- segT seg;
- subsegT subseg;
- unsigned fl;
- bfd_boolean val;
+static subseg_map *
+add_subseg_info (segT seg, subsegT subseg)
{
- subseg_map *subseg_e;
+ subseg_map *subseg_e = (subseg_map *) xmalloc (sizeof (subseg_map));
+ memset (subseg_e, 0, sizeof (subseg_map));
+ subseg_e->seg = seg;
+ subseg_e->subseg = subseg;
+ subseg_e->flags = 0;
+ /* Start off considering every branch target very important. */
+ subseg_e->target_freq = 1.0;
+ subseg_e->total_freq = 1.0;
+ subseg_e->next = sseg_map;
+ sseg_map = subseg_e;
+ return 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;
- }
+static unsigned
+get_last_insn_flags (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (subseg_e)
+ return subseg_e->flags;
+ return 0;
+}
+
+static void
+set_last_insn_flags (segT seg,
+ subsegT subseg,
+ unsigned fl,
+ bfd_boolean val)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (! subseg_e)
+ subseg_e = add_subseg_info (seg, subseg);
if (val)
subseg_e->flags |= fl;
else
subseg_e->flags &= ~fl;
}
+
+static float
+get_subseg_total_freq (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (subseg_e)
+ return subseg_e->total_freq;
+ return 1.0;
+}
+
+
+static float
+get_subseg_target_freq (segT seg, subsegT subseg)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (subseg_e)
+ return subseg_e->target_freq;
+ return 1.0;
+}
+
+
+static void
+set_subseg_freq (segT seg, subsegT subseg, float total_f, float target_f)
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ if (! subseg_e)
+ subseg_e = add_subseg_info (seg, subseg);
+ subseg_e->total_freq = total_f;
+ subseg_e->target_freq = target_f;
+}
+
\f
/* Segment Lists and emit_state Stuff. */
/* Remove the segment from the global sections list. */
static void
-xtensa_remove_section (sec)
- segT sec;
+xtensa_remove_section (segT sec)
{
/* Handle brain-dead bfd_section_list_remove macro, which
expect the address of the prior section's "next" field, not
static void
-xtensa_insert_section (after_sec, sec)
- segT after_sec;
- segT sec;
+xtensa_insert_section (segT after_sec, segT sec)
{
segT *after_sec_next;
if (after_sec == NULL)
static void
-xtensa_move_seg_list_to_beginning (head)
- seg_list *head;
+xtensa_move_seg_list_to_beginning (seg_list *head)
{
head = head->next;
while (head)
}
-void
-xtensa_move_literals ()
+static void mark_literal_frags (seg_list *);
+
+static void
+xtensa_move_literals (void)
{
seg_list *segment;
frchainS *frchain_from, *frchain_to;
prevents us from making a segment with an frchain without any
frags in it. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
last_frag = frag_now;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
- while (search_frag != frag_now)
+ while (search_frag != frag_now)
{
next_frag = search_frag->fr_next;
- /* First, move the frag out of the literal section and
+ /* 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
+ /* 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;
{
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);
+ if (dest_seg)
+ S_SET_SEGMENT (lit_sym, dest_seg);
}
}
and frag_variant to get called in all situations. */
static void
-mark_literal_frags (segment)
- seg_list *segment;
+mark_literal_frags (seg_list *segment)
{
frchainS *frchain_from;
fragS *search_frag;
static void
-xtensa_reorder_seg_list (head, after)
- seg_list *head;
- segT after;
+xtensa_reorder_seg_list (seg_list *head, segT after)
{
/* Move all of the sections in the section list to come
after "after" in the gnu segment list. */
/* Push all the literal segments to the end of the gnu list. */
-void
-xtensa_reorder_segments ()
+static void
+xtensa_reorder_segments (void)
{
segT sec;
- segT last_sec;
+ segT last_sec = 0;
int old_count = 0;
int new_count = 0;
for (sec = stdoutput->sections; sec != NULL; sec = sec->next)
- old_count++;
+ {
+ last_sec = sec;
+ 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);
}
-segT
-get_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. */
+
+static void
+xtensa_switch_to_literal_fragment (emit_state *result)
{
- segT last_sec = stdoutput->sections;
- while (last_sec->next != NULL)
- last_sec = last_sec->next;
+ if (directive_state[directive_absolute_literals])
+ {
+ cache_literal_section (0, default_lit_sections.lit4_seg_name,
+ &default_lit_sections.lit4_seg, FALSE);
+ xtensa_switch_section_emit_state (result,
+ default_lit_sections.lit4_seg, 0);
+ }
+ else
+ xtensa_switch_to_non_abs_literal_fragment (result);
- return last_sec;
+ /* Do a 4-byte align here. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
}
-/* 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;
+static void
+xtensa_switch_to_non_abs_literal_fragment (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
bfd_boolean is_fini =
(now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME));
-
- if (pool_location == NULL
- && !use_literal_section
+ if (pool_location == NULL
+ && !use_literal_section
&& !recursive
&& !is_init && ! is_fini)
{
- as_warn (_("inlining literal pool; "
- "specify location with .literal_position."));
+ as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
recursive = TRUE;
xtensa_mark_literal_pool_location ();
recursive = FALSE;
{
cache_literal_section (init_literal_head,
default_lit_sections.init_lit_seg_name,
- &default_lit_sections.init_lit_seg);
+ &default_lit_sections.init_lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.init_lit_seg, 0);
}
{
cache_literal_section (fini_literal_head,
default_lit_sections.fini_lit_seg_name,
- &default_lit_sections.fini_lit_seg);
+ &default_lit_sections.fini_lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.fini_lit_seg, 0);
}
- else
+ else
{
cache_literal_section (literal_head,
default_lit_sections.lit_seg_name,
- &default_lit_sections.lit_seg);
+ &default_lit_sections.lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.lit_seg, 0);
}
- if (!use_literal_section &&
- !is_init && !is_fini &&
- get_literal_pool_location (now_seg) != pool_location)
+ 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);
+ xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.literal_frag = pool_location;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
}
-
- /* Do a 4 byte align here. */
- frag_align (2, 0, 0);
}
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;
+static void
+xtensa_switch_section_emit_state (emit_state *state,
+ segT new_now_seg,
+ subsegT new_now_subseg)
{
state->name = now_seg->name;
state->now_seg = now_seg;
state->now_subseg = now_subseg;
state->generating_literals = generating_literals;
generating_literals++;
- subseg_new (segment_name (new_now_seg), new_now_subseg);
+ subseg_set (new_now_seg, new_now_subseg);
}
/* Use to restore the emitting into the normal place. */
-void
-xtensa_restore_emit_state (state)
- emit_state *state;
+static void
+xtensa_restore_emit_state (emit_state *state)
{
generating_literals = state->generating_literals;
- subseg_new (state->name, state->now_subseg);
+ subseg_set (state->now_seg, state->now_subseg);
}
present, return it; otherwise, create a new one. */
static void
-cache_literal_section (head, name, seg)
- seg_list *head;
- const char *name;
- segT *seg;
+cache_literal_section (seg_list *head,
+ const char *name,
+ segT *pseg,
+ bfd_boolean is_code)
{
segT current_section = now_seg;
int current_subsec = now_subseg;
+ segT seg;
- if (*seg != 0)
+ if (*pseg != 0)
return;
- *seg = retrieve_literal_seg (head, name);
+
+ /* Check if the named section exists. */
+ for (seg = stdoutput->sections; seg; seg = seg->next)
+ {
+ if (!strcmp (segment_name (seg), name))
+ break;
+ }
+
+ if (!seg)
+ {
+ /* Create a new literal section. */
+ seg = subseg_new (name, (subsegT) 0);
+ if (head)
+ {
+ /* Add the newly created literal segment to the specified list. */
+ seg_list *n = (seg_list *) xmalloc (sizeof (seg_list));
+ n->seg = seg;
+ n->next = head->next;
+ head->next = n;
+ }
+ bfd_set_section_flags (stdoutput, seg, SEC_HAS_CONTENTS |
+ SEC_READONLY | SEC_ALLOC | SEC_LOAD
+ | (is_code ? SEC_CODE : SEC_DATA));
+ bfd_set_section_alignment (stdoutput, seg, 2);
+ }
+
+ *pseg = seg;
subseg_set (current_section, current_subsec);
}
+\f
+/* Property Tables Stuff. */
+
+#define XTENSA_INSN_SEC_NAME ".xt.insn"
+#define XTENSA_LIT_SEC_NAME ".xt.lit"
+#define XTENSA_PROP_SEC_NAME ".xt.prop"
-/* Get a segment of a given name. If the segment is already
- present, return it; otherwise, create a new one. */
+typedef bfd_boolean (*frag_predicate) (const fragS *);
+typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
-static segT
-retrieve_literal_seg (head, name)
- seg_list *head;
- const char *name;
+static bfd_boolean get_frag_is_literal (const fragS *);
+static void xtensa_create_property_segments
+ (frag_predicate, frag_predicate, const char *, xt_section_type);
+static void xtensa_create_xproperty_segments
+ (frag_flags_fn, const char *, xt_section_type);
+static segment_info_type *retrieve_segment_info (segT);
+static segT retrieve_xtensa_section (char *);
+static bfd_boolean section_has_property (segT, frag_predicate);
+static bfd_boolean section_has_xproperty (segT, frag_flags_fn);
+static void add_xt_block_frags
+ (segT, segT, xtensa_block_info **, frag_predicate, frag_predicate);
+static bfd_boolean xtensa_frag_flags_is_empty (const frag_flags *);
+static void xtensa_frag_flags_init (frag_flags *);
+static void get_frag_property_flags (const fragS *, frag_flags *);
+static bfd_vma frag_flags_to_number (const frag_flags *);
+static void add_xt_prop_frags
+ (segT, segT, xtensa_block_info **, frag_flags_fn);
+
+/* Set up property tables after relaxation. */
+
+void
+xtensa_post_relax_hook (void)
{
- segT ret = 0;
+ 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);
- assert (head);
+ xtensa_find_unmarked_state_frags ();
- 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);
- }
+ if (use_literal_section)
+ xtensa_create_property_segments (get_frag_is_literal,
+ NULL,
+ XTENSA_LIT_SEC_NAME,
+ xt_literal_sec);
+ xtensa_create_xproperty_segments (get_frag_property_flags,
+ XTENSA_PROP_SEC_NAME,
+ xt_prop_sec);
- return ret;
+ if (warn_unaligned_branch_targets)
+ bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
+ bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
}
-/* Return a segment of a given name if it is present. */
+/* This function is only meaningful after xtensa_move_literals. */
-static segT
-seg_present (name)
- const char *name;
+static bfd_boolean
+get_frag_is_literal (const fragS *fragP)
{
- segT seg;
- seg = stdoutput->sections;
+ assert (fragP != NULL);
+ return fragP->tc_frag_data.is_literal;
+}
+
+
+static void
+xtensa_create_property_segments (frag_predicate property_function,
+ frag_predicate end_property_function,
+ const char *section_name_base,
+ xt_section_type sec_type)
+{
+ segT *seclist;
+
+ /* Walk over all of the current segments.
+ Walk over each fragment
+ For each non-empty fragment,
+ Build a property record (append where possible). */
- while (seg)
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
{
- if (!strcmp (segment_name (seg), name))
- return seg;
- seg = seg->next;
+ segT sec = *seclist;
+ flagword flags;
+
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ if (section_has_property (sec, property_function))
+ {
+ char *property_section_name =
+ xtensa_get_property_section_name (sec, section_name_base);
+ segT insn_sec = retrieve_xtensa_section (property_section_name);
+ segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
+ xtensa_block_info **xt_blocks =
+ &xt_seg_info->tc_segment_info_data.blocks[sec_type];
+ /* Walk over all of the frchains here and add new sections. */
+ add_xt_block_frags (sec, insn_sec, xt_blocks, property_function,
+ end_property_function);
+ }
}
- return 0;
-}
+ /* 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];
-/* Add a segment to a segment list. */
+ if (block)
+ {
+ xtensa_block_info *cur_block;
+ /* This is a section with some data. */
+ int num_recs = 0;
+ bfd_size_type rec_size;
-static void
-add_seg_list (head, seg)
- seg_list *head;
- segT seg;
-{
- seg_list *n;
- n = (seg_list *) xmalloc (sizeof (seg_list));
- assert (n);
+ for (cur_block = block; cur_block; cur_block = cur_block->next)
+ num_recs++;
- n->seg = seg;
- n->next = head->next;
- head->next = n;
-}
+ 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;
+ bfd_size_type frag_size;
+ fixS *fixes;
+ frchainS *frchainP;
+ int i;
+ char *frag_data;
-\f
-/* Set up Property Tables after Relaxation. */
+ frag_size = sizeof (fragS) + rec_size;
+ fragP = (fragS *) xmalloc (frag_size);
-#define XTENSA_INSN_SEC_NAME ".xt.insn"
-#define XTENSA_LIT_SEC_NAME ".xt.lit"
+ 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. */
-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);
+ frchainP = seginfo->frchainP;
+ frchainP->frch_root = fragP;
+ frchainP->frch_last = fragP;
- xtensa_create_property_segments (get_frag_is_insn,
- XTENSA_INSN_SEC_NAME,
- xt_insn_sec);
- if (use_literal_section)
- xtensa_create_property_segments (get_frag_is_literal,
- XTENSA_LIT_SEC_NAME,
- xt_literal_sec);
-}
+ 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);
-static bfd_boolean
-get_frag_is_literal (fragP)
- const fragS *fragP;
-{
- assert (fragP != NULL);
- return (fragP->tc_frag_data.is_literal);
-}
-
+ /* 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;
-static bfd_boolean
-get_frag_is_insn (fragP)
- const fragS *fragP;
-{
- assert (fragP != NULL);
- return (fragP->tc_frag_data.is_insn);
+ /* Write the length. */
+ md_number_to_chars (&frag_data[4 + 8 * i],
+ cur_block->size, 4);
+ cur_block = cur_block->next;
+ }
+ }
+ }
+ }
}
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;
+xtensa_create_xproperty_segments (frag_flags_fn flag_fn,
+ 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). */
+ 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))
+ flagword flags;
+
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if ((flags & SEC_DEBUGGING)
+ || !(flags & SEC_ALLOC)
+ || (flags & SEC_MERGE))
+ continue;
+
+ if (section_has_xproperty (sec, flag_fn))
{
char *property_section_name =
xtensa_get_property_section_name (sec, section_name_base);
segT insn_sec = retrieve_xtensa_section (property_section_name);
segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
- xtensa_block_info **xt_blocks =
+ 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);
+ add_xt_prop_frags (sec, insn_sec, xt_blocks, flag_fn);
}
}
segment_info_type *seginfo;
xtensa_block_info *block;
segT sec = *seclist;
+
seginfo = seg_info (sec);
block = seginfo->tc_segment_info_data.blocks[sec_type];
{
xtensa_block_info *cur_block;
/* This is a section with some data. */
- size_t num_recs = 0;
- size_t rec_size;
+ int num_recs = 0;
+ bfd_size_type rec_size;
for (cur_block = block; cur_block; cur_block = cur_block->next)
num_recs++;
- rec_size = num_recs * 8;
+ rec_size = num_recs * (8 + 4);
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. */
+ /* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
+
+ /* In order to make this work with the assembler, we have to build
+ some frags 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. */
+ /* Allocate a fragment and (unfortunately) leak it. */
fragS *fragP;
- size_t frag_size;
+ bfd_size_type frag_size;
fixS *fixes;
frchainS *frchainP;
- size_t i;
+ int i;
char *frag_data;
frag_size = sizeof (fragS) + rec_size;
fragP->fr_fix = rec_size;
fragP->fr_var = 0;
fragP->fr_type = rs_fill;
- /* the rest are zeros */
+ /* The rest are zeros. */
frchainP = seginfo->frchainP;
frchainP->frch_root = fragP;
fix->fx_size = 4;
fix->fx_done = 0;
fix->fx_frag = fragP;
- fix->fx_where = i * 8;
+ fix->fx_where = i * (8 + 4);
fix->fx_addsy = section_symbol (cur_block->sec);
fix->fx_offset = cur_block->offset;
fix->fx_r_type = BFD_RELOC_32;
fix->fx_line = 0;
/* Write the length. */
- md_number_to_chars (&frag_data[4 + 8 * i],
+ md_number_to_chars (&frag_data[4 + (8+4) * i],
cur_block->size, 4);
+ md_number_to_chars (&frag_data[8 + (8+4) * i],
+ frag_flags_to_number (&cur_block->flags),
+ 4);
cur_block = cur_block->next;
}
}
}
-segment_info_type *
-retrieve_segment_info (seg)
- segT seg;
+static segment_info_type *
+retrieve_segment_info (segT seg)
{
segment_info_type *seginfo;
seginfo = (segment_info_type *) bfd_get_section_userdata (stdoutput, seg);
frchainS *frchainP;
seginfo = (segment_info_type *) xmalloc (sizeof (*seginfo));
- memset ((PTR) seginfo, 0, sizeof (*seginfo));
+ memset ((void *) 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
+ bfd_set_section_userdata (stdoutput, seg, (void *) seginfo);
frchainP = (frchainS *) xmalloc (sizeof (frchainS));
frchainP->frch_root = NULL;
}
-segT
-retrieve_xtensa_section (sec_name)
- char *sec_name;
+static segT
+retrieve_xtensa_section (char *sec_name)
{
bfd *abfd = stdoutput;
flagword flags, out_flags, link_once_flags;
}
-bfd_boolean
-section_has_property (sec, property_function)
- segT sec;
- frag_predicate property_function;
+static bfd_boolean
+section_has_property (segT sec, frag_predicate property_function)
{
segment_info_type *seginfo = seg_info (sec);
fragS *fragP;
}
+static bfd_boolean
+section_has_xproperty (segT sec, frag_flags_fn property_function)
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragP;
+
+ if (seginfo && seginfo->frchainP)
+ {
+ for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ frag_flags prop_flags;
+ property_function (fragP, &prop_flags);
+ if (!xtensa_frag_flags_is_empty (&prop_flags))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/* 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;
+static void
+add_xt_block_frags (segT sec,
+ segT xt_block_sec,
+ xtensa_block_info **xt_block,
+ frag_predicate property_function,
+ frag_predicate end_property_function)
{
segment_info_type *seg_info;
segment_info_type *xt_seg_info;
new_block->offset = fragP->fr_address;
new_block->size = fragP->fr_fix;
new_block->next = NULL;
+ xtensa_frag_flags_init (&new_block->flags);
+ *xt_block = new_block;
+ }
+ if (end_property_function
+ && end_property_function (fragP))
+ {
+ xt_block = &((*xt_block)->next);
+ }
+ }
+ }
+ }
+}
+
+
+/* Break the encapsulation of add_xt_prop_frags here. */
+
+static bfd_boolean
+xtensa_frag_flags_is_empty (const frag_flags *prop_flags)
+{
+ if (prop_flags->is_literal
+ || prop_flags->is_insn
+ || prop_flags->is_data
+ || prop_flags->is_unreachable)
+ return FALSE;
+ return TRUE;
+}
+
+
+static void
+xtensa_frag_flags_init (frag_flags *prop_flags)
+{
+ memset (prop_flags, 0, sizeof (frag_flags));
+}
+
+
+static void
+get_frag_property_flags (const fragS *fragP, frag_flags *prop_flags)
+{
+ xtensa_frag_flags_init (prop_flags);
+ if (fragP->tc_frag_data.is_literal)
+ prop_flags->is_literal = TRUE;
+ if (fragP->tc_frag_data.is_unreachable)
+ prop_flags->is_unreachable = TRUE;
+ else if (fragP->tc_frag_data.is_insn)
+ {
+ prop_flags->is_insn = TRUE;
+ if (fragP->tc_frag_data.is_loop_target)
+ prop_flags->insn.is_loop_target = TRUE;
+ if (fragP->tc_frag_data.is_branch_target)
+ prop_flags->insn.is_branch_target = TRUE;
+ if (fragP->tc_frag_data.is_specific_opcode
+ || fragP->tc_frag_data.is_no_transform)
+ prop_flags->insn.is_no_transform = TRUE;
+ if (fragP->tc_frag_data.is_no_density)
+ prop_flags->insn.is_no_density = TRUE;
+ if (fragP->tc_frag_data.use_absolute_literals)
+ prop_flags->insn.is_abslit = TRUE;
+ }
+ if (fragP->tc_frag_data.is_align)
+ {
+ prop_flags->is_align = TRUE;
+ prop_flags->alignment = fragP->tc_frag_data.alignment;
+ if (xtensa_frag_flags_is_empty (prop_flags))
+ prop_flags->is_data = TRUE;
+ }
+}
+
+
+static bfd_vma
+frag_flags_to_number (const frag_flags *prop_flags)
+{
+ bfd_vma num = 0;
+ if (prop_flags->is_literal)
+ num |= XTENSA_PROP_LITERAL;
+ if (prop_flags->is_insn)
+ num |= XTENSA_PROP_INSN;
+ if (prop_flags->is_data)
+ num |= XTENSA_PROP_DATA;
+ if (prop_flags->is_unreachable)
+ num |= XTENSA_PROP_UNREACHABLE;
+ if (prop_flags->insn.is_loop_target)
+ num |= XTENSA_PROP_INSN_LOOP_TARGET;
+ if (prop_flags->insn.is_branch_target)
+ {
+ num |= XTENSA_PROP_INSN_BRANCH_TARGET;
+ num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
+ }
+
+ if (prop_flags->insn.is_no_density)
+ num |= XTENSA_PROP_INSN_NO_DENSITY;
+ if (prop_flags->insn.is_no_transform)
+ num |= XTENSA_PROP_INSN_NO_TRANSFORM;
+ if (prop_flags->insn.is_no_reorder)
+ num |= XTENSA_PROP_INSN_NO_REORDER;
+ if (prop_flags->insn.is_abslit)
+ num |= XTENSA_PROP_INSN_ABSLIT;
+
+ if (prop_flags->is_align)
+ {
+ num |= XTENSA_PROP_ALIGN;
+ num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
+ }
+
+ return num;
+}
+
+
+static bfd_boolean
+xtensa_frag_flags_combinable (const frag_flags *prop_flags_1,
+ const frag_flags *prop_flags_2)
+{
+ /* Cannot combine with an end marker. */
+
+ if (prop_flags_1->is_literal != prop_flags_2->is_literal)
+ return FALSE;
+ if (prop_flags_1->is_insn != prop_flags_2->is_insn)
+ return FALSE;
+ if (prop_flags_1->is_data != prop_flags_2->is_data)
+ return FALSE;
+
+ if (prop_flags_1->is_insn)
+ {
+ /* Properties of the beginning of the frag. */
+ if (prop_flags_2->insn.is_loop_target)
+ return FALSE;
+ if (prop_flags_2->insn.is_branch_target)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_density !=
+ prop_flags_2->insn.is_no_density)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_transform !=
+ prop_flags_2->insn.is_no_transform)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_reorder !=
+ prop_flags_2->insn.is_no_reorder)
+ return FALSE;
+ if (prop_flags_1->insn.is_abslit !=
+ prop_flags_2->insn.is_abslit)
+ return FALSE;
+ }
+
+ if (prop_flags_1->is_align)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+static bfd_vma
+xt_block_aligned_size (const xtensa_block_info *xt_block)
+{
+ bfd_vma end_addr;
+ unsigned align_bits;
+
+ if (!xt_block->flags.is_align)
+ return xt_block->size;
+
+ end_addr = xt_block->offset + xt_block->size;
+ align_bits = xt_block->flags.alignment;
+ end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
+ return end_addr - xt_block->offset;
+}
+
+
+static bfd_boolean
+xtensa_xt_block_combine (xtensa_block_info *xt_block,
+ const xtensa_block_info *xt_block_2)
+{
+ if (xt_block->sec != xt_block_2->sec)
+ return FALSE;
+ if (xt_block->offset + xt_block_aligned_size (xt_block)
+ != xt_block_2->offset)
+ return FALSE;
+
+ if (xt_block_2->size == 0
+ && (!xt_block_2->flags.is_unreachable
+ || xt_block->flags.is_unreachable))
+ {
+ if (xt_block_2->flags.is_align
+ && xt_block->flags.is_align)
+ {
+ /* Nothing needed. */
+ if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
+ return TRUE;
+ }
+ else
+ {
+ if (xt_block_2->flags.is_align)
+ {
+ /* Push alignment to previous entry. */
+ xt_block->flags.is_align = xt_block_2->flags.is_align;
+ xt_block->flags.alignment = xt_block_2->flags.alignment;
+ }
+ return TRUE;
+ }
+ }
+ if (!xtensa_frag_flags_combinable (&xt_block->flags,
+ &xt_block_2->flags))
+ return FALSE;
+
+ xt_block->size += xt_block_2->size;
+
+ if (xt_block_2->flags.is_align)
+ {
+ xt_block->flags.is_align = TRUE;
+ xt_block->flags.alignment = xt_block_2->flags.alignment;
+ }
+
+ return TRUE;
+}
+
+
+static void
+add_xt_prop_frags (segT sec,
+ segT xt_block_sec,
+ xtensa_block_info **xt_block,
+ frag_flags_fn property_function)
+{
+ segment_info_type *seg_info;
+ segment_info_type *xt_seg_info;
+ bfd_vma seg_offset;
+ fragS *fragP;
+
+ xt_seg_info = retrieve_segment_info (xt_block_sec);
+ seg_info = retrieve_segment_info (sec);
+ /* 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)
+ {
+ xtensa_block_info tmp_block;
+ tmp_block.sec = sec;
+ tmp_block.offset = fragP->fr_address;
+ tmp_block.size = fragP->fr_fix;
+ tmp_block.next = NULL;
+ property_function (fragP, &tmp_block.flags);
+
+ if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
+ /* && fragP->fr_fix != 0) */
+ {
+ if ((*xt_block) == NULL
+ || !xtensa_xt_block_combine (*xt_block, &tmp_block))
+ {
+ xtensa_block_info *new_block;
+ if ((*xt_block) != NULL)
+ xt_block = &(*xt_block)->next;
+ new_block = (xtensa_block_info *)
+ xmalloc (sizeof (xtensa_block_info));
+ *new_block = tmp_block;
*xt_block = new_block;
}
}
}
}
+\f
+/* op_placement_info_table */
+
+/* op_placement_info makes it easier to determine which
+ ops can go in which slots. */
+
+static void
+init_op_placement_info_table (void)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
+ xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
+ int num_opcodes = xtensa_isa_num_opcodes (isa);
+
+ op_placement_table = (op_placement_info_table)
+ xmalloc (sizeof (op_placement_info) * num_opcodes);
+ assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
+
+ for (opcode = 0; opcode < num_opcodes; opcode++)
+ {
+ op_placement_info *opi = &op_placement_table[opcode];
+ /* FIXME: Make tinsn allocation dynamic. */
+ if (xtensa_opcode_num_operands (isa, opcode) >= MAX_INSN_ARGS)
+ as_fatal (_("too many operands in instruction"));
+ opi->single = XTENSA_UNDEFINED;
+ opi->single_size = 0;
+ opi->widest = XTENSA_UNDEFINED;
+ opi->widest_size = 0;
+ opi->narrowest = XTENSA_UNDEFINED;
+ opi->narrowest_size = 0x7F;
+ opi->formats = 0;
+ opi->num_formats = 0;
+ opi->issuef = 0;
+ for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+ {
+ opi->slots[fmt] = 0;
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
+ {
+ int fmt_length = xtensa_format_length (isa, fmt);
+ opi->issuef++;
+ set_bit (fmt, opi->formats);
+ set_bit (slot, opi->slots[fmt]);
+ /* opi->slot_count[fmt]++; */
+ if (fmt_length < opi->narrowest_size)
+ {
+ opi->narrowest = fmt;
+ opi->narrowest_size = fmt_length;
+ }
+ if (fmt_length > opi->widest_size)
+ {
+ opi->widest = fmt;
+ opi->widest_size = fmt_length;
+ }
+ if (xtensa_format_num_slots (isa, fmt) == 1)
+ {
+ if (opi->single_size == 0
+ || fmt_length < opi->single_size)
+ {
+ opi->single = fmt;
+ opi->single_size = fmt_length;
+ }
+ }
+ }
+ }
+ if (opi->formats)
+ opi->num_formats++;
+ }
+ }
+ xtensa_insnbuf_free (isa, ibuf);
+}
+
+
+bfd_boolean
+opcode_fits_format_slot (xtensa_opcode opcode, xtensa_format fmt, int slot)
+{
+ return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
+}
+
+
+/* If the opcode is available in a single slot format, return its size. */
+
+static int
+xg_get_single_size (xtensa_opcode opcode)
+{
+ assert (op_placement_table[opcode].single != XTENSA_UNDEFINED);
+ return op_placement_table[opcode].single_size;
+}
+
+
+static xtensa_format
+xg_get_single_format (xtensa_opcode opcode)
+{
+ return op_placement_table[opcode].single;
+}
+
\f
/* Instruction Stack Functions (from "xtensa-istack.h"). */
void
-istack_init (stack)
- IStack *stack;
+istack_init (IStack *stack)
{
memset (stack, 0, sizeof (IStack));
stack->ninsn = 0;
bfd_boolean
-istack_empty (stack)
- IStack *stack;
+istack_empty (IStack *stack)
{
return (stack->ninsn == 0);
}
bfd_boolean
-istack_full (stack)
- IStack *stack;
+istack_full (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. */
+ It is an error to call this if istack_empty () is TRUE. */
TInsn *
-istack_top (stack)
- IStack *stack;
+istack_top (IStack *stack)
{
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
/* Add a new TInsn to an IStack.
- It is an error to call this if istack_full () is true. */
+ It is an error to call this if istack_full () is TRUE. */
void
-istack_push (stack, insn)
- IStack *stack;
- TInsn *insn;
+istack_push (IStack *stack, TInsn *insn)
{
int rec = stack->ninsn;
assert (!istack_full (stack));
- tinsn_copy (&stack->insn[rec], insn);
+ 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. */
+ to it. It is an error to call this if istack_full () is TRUE. */
TInsn *
-istack_push_space (stack)
- IStack *stack;
+istack_push_space (IStack *stack)
{
int rec = stack->ninsn;
TInsn *insn;
/* Remove the last pushed instruction. It is an error to call this if
- istack_empty () returns true. */
+ istack_empty () returns TRUE. */
void
-istack_pop (stack)
- IStack *stack;
+istack_pop (IStack *stack)
{
int rec = stack->ninsn - 1;
assert (!istack_empty (stack));
/* TInsn functions. */
void
-tinsn_init (dst)
- TInsn *dst;
+tinsn_init (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;
+tinsn_get_tok (TInsn *insn, int num)
{
assert (num < insn->ntok);
return &insn->tok[num];
}
-/* Return true if ANY of the operands in the insn are symbolic. */
+/* Return TRUE if ANY of the operands in the insn are symbolic. */
static bfd_boolean
-tinsn_has_symbolic_operands (insn)
- const TInsn *insn;
+tinsn_has_symbolic_operands (const TInsn *insn)
{
int i;
int n = insn->ntok;
bfd_boolean
-tinsn_has_invalid_symbolic_operands (insn)
- const TInsn *insn;
+tinsn_has_invalid_symbolic_operands (const TInsn *insn)
{
+ xtensa_isa isa = xtensa_default_isa;
int i;
int n = insn->ntok;
case O_register:
case O_constant:
break;
+ case O_big:
+ case O_illegal:
+ case O_absent:
+ /* Errors for these types are caught later. */
+ break;
+ case O_hi16:
+ case O_lo16:
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;
+ /* Symbolic immediates are only allowed on the last immediate
+ operand. At this time, CONST16 is the only opcode where we
+ support non-PC-relative relocations. (It isn't necessary
+ to complain about non-PC-relative relocations here, but
+ otherwise, no error is reported until the relocations are
+ generated, and the assembler won't get that far if there
+ are any other errors. It's nice to see all the problems
+ at once.) */
+ if (i != get_relaxable_immed (insn->opcode)
+ || (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
+ && insn->opcode != xtensa_const16_opcode))
+ {
+ as_bad (_("invalid symbolic operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, insn->opcode));
+ return TRUE;
+ }
}
}
return FALSE;
boil down to SYMBOL + OFFSET. */
static bfd_boolean
-tinsn_has_complex_operands (insn)
- const TInsn *insn;
+tinsn_has_complex_operands (const TInsn *insn)
{
int i;
int n = insn->ntok;
case O_register:
case O_constant:
case O_symbol:
+ case O_lo16:
+ case O_hi16:
break;
default:
return TRUE;
}
-/* Convert the constant operands in the t_insn to insnbuf.
- Return true if there is a symbol in the immediate field.
+/* Convert the constant operands in the tinsn to insnbuf.
+ Return TRUE if there is a symbol in the immediate field.
- Before this is called,
+ Before this is called,
1) the number of operands are correct
- 2) the t_insn is a ITYPE_INSN
+ 2) the tinsn 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;
+tinsn_to_insnbuf (TInsn *tinsn, xtensa_insnbuf insnbuf)
{
+ static xtensa_insnbuf slotbuf = 0;
xtensa_isa isa = xtensa_default_isa;
- xtensa_opcode opcode = t_insn->opcode;
+ xtensa_opcode opcode = tinsn->opcode;
+ xtensa_format fmt = xg_get_single_format (opcode);
bfd_boolean has_fixup = FALSE;
- int noperands = xtensa_num_operands (isa, opcode);
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
int i;
uint32 opnd_value;
char *file_name;
- int line;
+ unsigned line;
- assert (t_insn->insn_type == ITYPE_INSN);
- if (noperands != t_insn->ntok)
+ if (!slotbuf)
+ slotbuf = xtensa_insnbuf_alloc (isa);
+
+ assert (tinsn->insn_type == ITYPE_INSN);
+ if (noperands != tinsn->ntok)
as_fatal (_("operand number mismatch"));
- xtensa_encode_insn (isa, opcode, insnbuf);
+ if (xtensa_opcode_encode (isa, fmt, 0, slotbuf, opcode))
+ as_fatal (_("cannot encode opcode"));
for (i = 0; i < noperands; ++i)
{
- expressionS *expr = &t_insn->tok[i];
- xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
+ expressionS *expr = &tinsn->tok[i];
switch (expr->X_op)
{
case O_register:
- /* The register number has already been checked in
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ /* The register number has already been checked in
expression_maybe_register, so we don't need to check here. */
opnd_value = expr->X_add_number;
- (void) xtensa_operand_encode (operand, &opnd_value);
- xtensa_operand_set_field (operand, insnbuf, opnd_value);
+ (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
+ xtensa_operand_set_field (isa, opcode, i, fmt, 0,
+ slotbuf, opnd_value);
break;
case O_constant:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
as_where (&file_name, &line);
/* It is a constant and we called this function,
then we have to try to fit it. */
- xtensa_insnbuf_set_operand (insnbuf, opcode, operand,
+ xtensa_insnbuf_set_operand (slotbuf, fmt, 0, opcode, i,
expr->X_add_number, file_name, line);
break;
- case O_symbol:
default:
has_fixup = TRUE;
break;
}
}
+
+ xtensa_format_encode (isa, fmt, insnbuf);
+ xtensa_format_set_slot (isa, fmt, 0, insnbuf, slotbuf);
+
return has_fixup;
}
-/* Check the instruction arguments. Return true on failure. */
+/* Convert the constant operands in the tinsn to slotbuf.
+ Return TRUE if there is a symbol in the immediate field.
+ (Eventually this should replace tinsn_to_insnbuf.) */
-bfd_boolean
-tinsn_check_arguments (insn)
- const TInsn *insn;
+/* Before this is called,
+ 1) the number of operands are correct
+ 2) the tinsn 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_slotbuf (xtensa_format fmt,
+ int slot,
+ TInsn *tinsn,
+ xtensa_insnbuf slotbuf)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_opcode opcode = tinsn->opcode;
+ bfd_boolean has_fixup = FALSE;
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
+ int i;
+
+ *((int *) &slotbuf[0]) = 0;
+ *((int *) &slotbuf[1]) = 0;
+ assert (tinsn->insn_type == ITYPE_INSN);
+ if (noperands != tinsn->ntok)
+ as_fatal (_("operand number mismatch"));
+
+ if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
+ {
+ as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
+ xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
+ return FALSE;
+ }
+
+ for (i = 0; i < noperands; i++)
+ {
+ expressionS *expr = &tinsn->tok[i];
+ int rc;
+ unsigned line;
+ char *file_name;
+ uint32 opnd_value;
+
+ switch (expr->X_op)
+ {
+ case O_register:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ /* 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 (isa, opcode, i, &opnd_value);
+ rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
+ opnd_value);
+ if (rc != 0)
+ as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
+ break;
+
+ case O_constant:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ as_where (&file_name, &line);
+ /* It is a constant and we called this function
+ then we have to try to fit it. */
+ xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
+ expr->X_add_number, file_name, line);
+ break;
+
+ default:
+ has_fixup = TRUE;
+ break;
+ }
+ }
+
+ return has_fixup;
+}
+
+
+/* Check the instruction arguments. Return TRUE on failure. */
+
+static bfd_boolean
+tinsn_check_arguments (const TInsn *insn)
{
xtensa_isa isa = xtensa_default_isa;
xtensa_opcode opcode = insn->opcode;
return TRUE;
}
- if (xtensa_num_operands (isa, opcode) > insn->ntok)
+ if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
{
as_bad (_("too few operands"));
return TRUE;
}
- if (xtensa_num_operands (isa, opcode) < insn->ntok)
+ if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
{
as_bad (_("too many operands"));
return TRUE;
/* Load an instruction from its encoded form. */
static void
-tinsn_from_chars (t_insn, f)
- TInsn *t_insn;
- char *f;
+tinsn_from_chars (TInsn *tinsn, char *f, int slot)
{
- static xtensa_insnbuf insnbuf = NULL;
- int i;
- xtensa_opcode opcode;
- xtensa_isa isa = xtensa_default_isa;
+ vliw_insn vinsn;
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
+ xg_init_vinsn (&vinsn);
+ vinsn_from_chars (&vinsn, f);
+
+ *tinsn = vinsn.slots[slot];
+ xg_free_vinsn (&vinsn);
+}
- xtensa_insnbuf_from_chars (isa, insnbuf, f);
- opcode = xtensa_decode_insn (isa, insnbuf);
+
+static void
+tinsn_from_insnbuf (TInsn *tinsn,
+ xtensa_insnbuf slotbuf,
+ xtensa_format fmt,
+ int slot)
+{
+ int i;
+ xtensa_isa isa = xtensa_default_isa;
/* 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++)
+ tinsn_init (tinsn);
+ tinsn->insn_type = ITYPE_INSN;
+ tinsn->is_specific_opcode = FALSE; /* must not be specific */
+ tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+ tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
+ for (i = 0; i < tinsn->ntok; i++)
{
- set_expr_const (&t_insn->tok[i],
- xtensa_insnbuf_get_operand (insnbuf, opcode, i));
+ set_expr_const (&tinsn->tok[i],
+ xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
+ tinsn->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;
+tinsn_immed_from_frag (TInsn *tinsn, fragS *fragP, int slot)
{
- xtensa_opcode opcode = t_insn->opcode;
+ xtensa_opcode opcode = tinsn->opcode;
int opnum;
- if (fragP->fr_symbol)
+ if (fragP->tc_frag_data.slot_symbols[slot])
{
opnum = get_relaxable_immed (opcode);
- set_expr_symbol_offset (&t_insn->tok[opnum],
- fragP->fr_symbol, fragP->fr_offset);
+ assert (opnum >= 0);
+ if (fragP->tc_frag_data.slot_sub_symbols[slot])
+ {
+ set_expr_symbol_offset_diff
+ (&tinsn->tok[opnum],
+ fragP->tc_frag_data.slot_symbols[slot],
+ fragP->tc_frag_data.slot_sub_symbols[slot],
+ fragP->tc_frag_data.slot_offsets[slot]);
+ }
+ else
+ {
+ set_expr_symbol_offset
+ (&tinsn->tok[opnum],
+ fragP->tc_frag_data.slot_symbols[slot],
+ fragP->tc_frag_data.slot_offsets[slot]);
+ }
}
}
static int
-get_num_stack_text_bytes (istack)
- IStack *istack;
+get_num_stack_text_bytes (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);
+ TInsn *tinsn = &istack->insn[i];
+ if (tinsn->insn_type == ITYPE_INSN)
+ text_bytes += xg_get_single_size (tinsn->opcode);
}
return text_bytes;
}
static int
-get_num_stack_literal_bytes (istack)
- IStack *istack;
+get_num_stack_literal_bytes (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)
+ TInsn *tinsn = &istack->insn[i];
+ if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
lit_bytes += 4;
}
return lit_bytes;
}
+\f
+/* vliw_insn functions. */
+
+static void
+xg_init_vinsn (vliw_insn *v)
+{
+ int i;
+ xtensa_isa isa = xtensa_default_isa;
+
+ xg_clear_vinsn (v);
+
+ v->insnbuf = xtensa_insnbuf_alloc (isa);
+ if (v->insnbuf == NULL)
+ as_fatal (_("out of memory"));
+
+ for (i = 0; i < MAX_SLOTS; i++)
+ {
+ tinsn_init (&v->slots[i]);
+ v->slots[i].opcode = XTENSA_UNDEFINED;
+ v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
+ if (v->slotbuf[i] == NULL)
+ as_fatal (_("out of memory"));
+ }
+}
+
+
+static void
+xg_clear_vinsn (vliw_insn *v)
+{
+ int i;
+ v->format = XTENSA_UNDEFINED;
+ v->num_slots = 0;
+ v->inside_bundle = FALSE;
+
+ if (xt_saved_debug_type != DEBUG_NONE)
+ debug_type = xt_saved_debug_type;
+
+ for (i = 0; i < MAX_SLOTS; i++)
+ {
+ memset (&v->slots[i], 0, sizeof (TInsn));
+ v->slots[i].opcode = XTENSA_UNDEFINED;
+ }
+}
+
+
+static bfd_boolean
+vinsn_has_specific_opcodes (vliw_insn *v)
+{
+ int i;
+
+ for (i = 0; i < v->num_slots; i++)
+ {
+ if (v->slots[i].is_specific_opcode)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static void
+xg_free_vinsn (vliw_insn *v)
+{
+ int i;
+ xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
+ for (i = 0; i < MAX_SLOTS; i++)
+ xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
+}
+
+
+/* Before this is called, we should have
+ filled out the following fields:
+
+ 1) the number of operands for each opcode are correct
+ 2) the tinsn in the slots are 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
+vinsn_to_insnbuf (vliw_insn *vinsn,
+ char *frag_offset,
+ fragS *fragP,
+ bfd_boolean record_fixup)
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt = vinsn->format;
+ xtensa_insnbuf insnbuf = vinsn->insnbuf;
+ int slot;
+ bfd_boolean has_fixup = FALSE;
+
+ xtensa_format_encode (isa, fmt, insnbuf);
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ bfd_boolean tinsn_has_fixup =
+ tinsn_to_slotbuf (vinsn->format, slot, tinsn,
+ vinsn->slotbuf[slot]);
+
+ xtensa_format_set_slot (isa, fmt, slot,
+ insnbuf, vinsn->slotbuf[slot]);
+ /* tinsn_has_fixup tracks if there is a fixup at all.
+ record_fixup controls globally. I.E., we use this
+ function from several places, some of which are after
+ fixups have already been recorded. Finally,
+ tinsn->record_fixup controls based on the individual ops,
+ which may or may not need it based on the relaxation
+ requirements. */
+ if (tinsn_has_fixup && record_fixup)
+ {
+ int i;
+ xtensa_opcode opcode = tinsn->opcode;
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
+ has_fixup = TRUE;
+
+ for (i = 0; i < noperands; i++)
+ {
+ expressionS* expr = &tinsn->tok[i];
+ switch (expr->X_op)
+ {
+ case O_symbol:
+ case O_lo16:
+ case O_hi16:
+ if (get_relaxable_immed (opcode) == i)
+ {
+ if (tinsn->record_fix || expr->X_op != O_symbol)
+ {
+ if (!xg_add_opcode_fix
+ (tinsn, i, fmt, slot, expr, fragP,
+ frag_offset - fragP->fr_literal))
+ as_bad (_("instruction with constant operands does not fit"));
+ }
+ else
+ {
+ tinsn->symbol = expr->X_add_symbol;
+ tinsn->offset = expr->X_add_number;
+ }
+ }
+ else
+ as_bad (_("invalid operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, opcode));
+ break;
+
+ case O_constant:
+ case O_register:
+ break;
+
+ case O_subtract:
+ if (get_relaxable_immed (opcode) == i)
+ {
+ if (tinsn->record_fix)
+ as_bad (_("invalid subtract operand"));
+ else
+ {
+ tinsn->symbol = expr->X_add_symbol;
+ tinsn->sub_symbol = expr->X_op_symbol;
+ tinsn->offset = expr->X_add_number;
+ }
+ }
+ else
+ as_bad (_("invalid operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, opcode));
+ break;
+
+ default:
+ as_bad (_("invalid expression for operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, opcode));
+ break;
+ }
+ }
+ }
+ }
+
+ return has_fixup;
+}
+
+
+static void
+vinsn_from_chars (vliw_insn *vinsn, char *f)
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ int i;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, (unsigned char *) f, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("cannot decode instruction format"));
+ vinsn->format = fmt;
+ vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ TInsn *tinsn = &vinsn->slots[i];
+ xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
+ tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
+ }
+}
+
\f
/* Expression utilities. */
-/* Return true if the expression is an integer constant. */
+/* Return TRUE if the expression is an integer constant. */
bfd_boolean
-expr_is_const (s)
- const expressionS *s;
+expr_is_const (const expressionS *s)
{
return (s->X_op == O_constant);
}
/* Get the expression constant.
- Calling this is illegal if expr_is_const () returns true. */
+ Calling this is illegal if expr_is_const () returns TRUE. */
offsetT
-get_expr_const (s)
- const expressionS *s;
+get_expr_const (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;
+set_expr_const (expressionS *s, offsetT val)
{
s->X_op = O_constant;
s->X_add_number = val;
}
+bfd_boolean
+expr_is_register (const expressionS *s)
+{
+ return (s->X_op == O_register);
+}
+
+
+/* Get the expression constant.
+ Calling this is illegal if expr_is_const () returns TRUE. */
+
+offsetT
+get_expr_register (const expressionS *s)
+{
+ assert (expr_is_register (s));
+ return s->X_add_number;
+}
+
+
/* Set the expression to a symbol + constant offset. */
void
-set_expr_symbol_offset (s, sym, offset)
- expressionS *s;
- symbolS *sym;
- offsetT offset;
+set_expr_symbol_offset (expressionS *s, symbolS *sym, offsetT offset)
{
s->X_op = O_symbol;
s->X_add_symbol = sym;
}
+/* Set the expression to symbol - minus_sym + offset. */
+
+static void
+set_expr_symbol_offset_diff (expressionS *s,
+ symbolS *sym,
+ symbolS *minus_sym,
+ offsetT offset)
+{
+ s->X_op = O_subtract;
+ s->X_add_symbol = sym;
+ s->X_op_symbol = minus_sym; /* unused */
+ s->X_add_number = offset;
+}
+
+
+/* Return TRUE if the two expressions are equal. */
+
bfd_boolean
-expr_is_equal (s1, s2)
- expressionS *s1;
- expressionS *s2;
+expr_is_equal (expressionS *s1, expressionS *s2)
{
if (s1->X_op != s2->X_op)
return FALSE;
static void
-copy_expr (dst, src)
- expressionS *dst;
- const expressionS *src;
+copy_expr (expressionS *dst, const expressionS *src)
{
memcpy (dst, src, sizeof (expressionS));
}
\f
-/* Support for Tensilica's "--rename-section" option. */
-
-#ifdef XTENSA_SECTION_RENAME
+/* Support for the "--rename-section" option. */
struct rename_section_struct
{
static struct rename_section_struct *section_rename;
-/* Parse the string oldname=new_name:oldname2=new_name2
- and call add_section_rename. */
+/* Parse the string "oldname=new_name(:oldname2=new_name2)*" and add
+ entries to the section_rename list. Note: Specifying multiple
+ renamings separated by colons is not documented and is retained only
+ for backward compatibility. */
-void
-build_section_rename (arg)
- const char *arg;
+static void
+build_section_rename (const char *arg)
{
+ struct rename_section_struct *r;
char *this_arg = NULL;
char *next_arg = NULL;
- for (this_arg = strdup (arg); this_arg != NULL; this_arg = next_arg)
+ for (this_arg = xstrdup (arg); this_arg != NULL; this_arg = next_arg)
{
+ char *old_name, *new_name;
+
if (this_arg)
{
next_arg = strchr (this_arg, ':');
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);
- }
- }
-}
+ old_name = this_arg;
+ new_name = strchr (this_arg, '=');
+ if (*old_name == '\0')
+ {
+ as_warn (_("ignoring extra '-rename-section' delimiter ':'"));
+ continue;
+ }
+ if (!new_name || new_name[1] == '\0')
+ {
+ as_warn (_("ignoring invalid '-rename-section' specification: '%s'"),
+ old_name);
+ continue;
+ }
+ *new_name = '\0';
+ new_name++;
-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);
+ }
- /* 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 = xstrdup (old_name);
+ r->new_name = xstrdup (new_name);
+ r->next = section_rename;
+ section_rename = r;
}
-
- /* 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;
+char *
+xtensa_section_rename (char *name)
{
struct rename_section_struct *r = section_rename;
for (r = section_rename; r != NULL; r = r->next)
- if (strcmp (r->old_name, name) == 0)
- return r->new_name;
+ {
+ if (strcmp (r->old_name, name) == 0)
+ return r->new_name;
+ }
return name;
}
-
-#endif /* XTENSA_SECTION_RENAME */