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