summaryrefslogtreecommitdiffstats
path: root/gas/config
diff options
context:
space:
mode:
authorBob Wilson <bob.wilson@acm.org>2004-10-08 00:22:15 +0000
committerBob Wilson <bob.wilson@acm.org>2004-10-08 00:22:15 +0000
commit43cd72b9aa3a95b0345587974c14b6d237f79a5f (patch)
tree5fd20b810662a22317882ba50bacc06473276256 /gas/config
parent51fea49ef7ee944234686878bab69fcc17f4b171 (diff)
downloadppe42-binutils-43cd72b9aa3a95b0345587974c14b6d237f79a5f.tar.gz
ppe42-binutils-43cd72b9aa3a95b0345587974c14b6d237f79a5f.zip
bfd ChangeLog
* elf32-xtensa.c (elf32xtensa_size_opt): New global variable. (xtensa_default_isa): Global variable moved here from xtensa-isa.c. (elf32xtensa_no_literal_movement): New global variable. (elf_howto_table): Add entries for new relocations. (elf_xtensa_reloc_type_lookup): Handle new relocations. (property_table_compare): When addresses are equal, compare sizes and various property flags. (property_table_matches): New. (xtensa_read_table_entries): Extend to read new property tables. Add output_addr parameter to indicate that output addresses should be used. Use bfd_get_section_limit. (elf_xtensa_find_property_entry): New. (elf_xtensa_in_literal_pool): Use elf_xtensa_find_property_entry. (elf_xtensa_check_relocs): Handle new relocations. (elf_xtensa_do_reloc): Use bfd_get_section_limit. Handle new relocations. Use new xtensa-isa.h functions. (build_encoding_error_message): Remove encode_result parameter. Add new target_address parameter used to detect alignment errors. (elf_xtensa_relocate_section): Use bfd_get_section_limit. Clean up error handling. Use new is_operand_relocation function. (elf_xtensa_combine_prop_entries, elf_xtensa_merge_private_bfd_data): Use underbar macro for error messages. Formatting. (get_const16_opcode): New. (get_l32r_opcode): Add a separate flag for initialization. (get_relocation_opnd): Operand number is no longer explicit in the relocation. Change to decode the opcode and analyze its operands. (get_relocation_slot): New. (get_relocation_opcode): Add bfd parameter. Use bfd_get_section_limit. Use new xtensa-isa.h functions to handle multislot instructions. (is_l32r_relocation): Add bfd parameter. Use is_operand_relocation. (get_asm_simplify_size, is_alt_relocation, is_operand_relocation, insn_decode_len, insn_decode_opcode, check_branch_target_aligned, check_loop_aligned, check_branch_target_aligned_address, narrowable, widenable, narrow_instruction, widen_instruction, op_single_fmt_table, get_single_format, init_op_single_format_table): New. (elf_xtensa_do_asm_simplify): Add error_message parameter and use it instead of calling _bfd_error_handler. Use new xtensa-isa.h functions. (contract_asm_expansion): Add error_message parameter and pass it to elf_xtensa_do_asm_simplify. Replace use of R_XTENSA_OP0 relocation with R_XTENSA_SLOT0_OP. (get_expanded_call_opcode): Extend to handle either L32R or CONST16 instructions. Use new xtensa-isa.h functions. (r_reloc struct): Add new virtual_offset field. (r_reloc_init): Add contents and content_length parameters. Set virtual_offset field to zero. Add contents to target_offset field for partial_inplace relocations. (r_reloc_is_defined): Check for null. (print_r_reloc): New debug function. (source_reloc struct): Replace xtensa_operand field with pair of the opcode and the operand position. Add is_abs_literal field. (init_source_reloc): Specify operand by opcode/position pair. Set is_abs_literal field. (source_reloc_compare): When target_offsets are equal, compare other fields to make sorting predictable. (literal_value struct): Add is_abs_literal field. (value_map_hash_table struct): Add has_last_loc and last_loc fields. (init_literal_value): New. (is_same_value): Replace with ... (literal_value_equal): ... this function. Add comparisons of virtual_offset and is_abs_literal fields. (value_map_hash_table_init): Use bfd_zmalloc. Check for allocation failure. Initialize has_last_loc field. (value_map_hash_table_delete): New. (hash_literal_value): Rename to ... (literal_value_hash): ... this. Include is_abs_literal flag and virtual_offset field in the hash value. (get_cached_value): Rename to ... (value_map_get_cached_value): ... this. Update calls to literal_value_hash and literal_value_equal. (add_value_map): Check for allocation failure. Update calls to value_map_get_cached_value and literal_value_hash. (text_action, text_action_list, text_action_t): New types. (find_fill_action, compute_removed_action_diff, adjust_fill_action, text_action_add, text_action_add_literal, offset_with_removed_text, offset_with_removed_text_before_fill, find_insn_action, print_action_list, print_removed_literals): New. (offset_with_removed_literals): Delete. (xtensa_relax_info struct): Add is_relaxable_asm_section, action_list, fix_array, fix_array_count, allocated_relocs, relocs_count, and allocated_relocs_count fields. (init_xtensa_relax_info): Initialize new fields. (reloc_bfd_fix struct): Add new translated field. (reloc_bfd_fix_init): Add translated parameter and use it to set the translated field. (fix_compare, cache_fix_array): New. (get_bfd_fix): Remove fix_list parameter and get all relax_info for the section via get_xtensa_relax_info. Use cache_fix_array to set up sorted fix_array and use bsearch instead of linear search. (section_cache_t): New struct. (init_section_cache, section_cache_section, clear_section_cache): New. (ebb_t, ebb_target_enum, proposed_action, ebb_constraint): New types. (init_ebb_constraint, free_ebb_constraint, init_ebb, extend_ebb_bounds, extend_ebb_bounds_forward, extend_ebb_bounds_backward, insn_block_decodable_len, ebb_propose_action, ebb_add_proposed_action): New. (retrieve_contents): Use bfd_get_section_limit. (elf_xtensa_relax_section): Add relocations_analyzed flag. Update call to compute_removed_literals. Free value_map_hash_table when no longer needed. (analyze_relocations): Check is_relaxable_asm_section flag. Call compute_text_actions for all sections. (find_relaxable_sections): Mark sections as relaxable if they contain ASM_EXPAND relocations that can be optimized. Adjust r_reloc_init call. Increment relax_info src_count field only for appropriate relocation types. Remove is_literal_section check. (collect_source_relocs): Use bfd_get_section_limit. Adjust calls to r_reloc_init and find_associated_l32r_irel. Check is_relaxable_asm_section flag. Handle L32R instructions with absolute literals. Pass is_abs_literal flag to init_source_reloc. (is_resolvable_asm_expansion): Use bfd_get_section_limit. Check for CONST16 instructions. Adjust calls to r_reloc_init and pcrel_reloc_fits. Handle weak symbols conservatively. (find_associated_l32r_irel): Add bfd parameter and pass it to is_l32r_relocation. (compute_text_actions, compute_ebb_proposed_actions, compute_ebb_actions, check_section_ebb_pcrels_fit, check_section_ebb_reduces, text_action_add_proposed, compute_fill_extra_space): New. (remove_literals): Replace with ... (compute_removed_literals): ... this function. Call init_section_cache. Use bfd_get_section_limit. Sort internal_relocs. Call xtensa_read_table_entries to get the property table. Skip relocations other than R_XTENSA_32 and R_XTENSA_PLT. Use new is_removable_literal, remove_dead_literal, and identify_literal_placement functions. (get_irel_at_offset): Rewrite to use bsearch on sorted relocations instead of linear search. (is_removable_literal, remove_dead_literal, identify_literal_placement): New. (relocations_reach): Update check for literal not referenced by any PC-relative relocations. Adjust call to pcrel_reloc_fits. (coalesce_shared_literal, move_shared_literal): New. (relax_section): Use bfd_get_section_limit. Call translate_section_fixes. Update calls to r_reloc_init and offset_with_removed_text. Check new is_relaxable_asm_section flag. Add call to pin_internal_relocs. Add special handling for R_XTENSA_ASM_SIMPLIFY and R_XTENSA_DIFF* relocs. Use virtual_offset info to calculate new addend_displacement variable. Replace code for deleting literals with more general code to perform the actions determined by the action_list for the section. (translate_section_fixes, translate_reloc_bfd_fix): New. (translate_reloc): Check new is_relaxable_asm_section flag. Call find_removed_literal only if is_operand_relocation. Update call to offset_with_removed_text. Use new target_offset and removed_bytes variables. (move_literal): New. (relax_property_section): Use bfd_get_section_limit. Set new is_full_prop_section flag and handle new property tables. Update calls to r_reloc_init and offset_with_removed_text. Check is_relaxable_asm_section flag. Handle expansion of zero-sized unreachable entries, with use of offset_with_removed_text_before_fill. For relocatable links, combine entries only for literal tables. (relax_section_symbols): Check is_relaxable_asm_section flag. Update calls to offset_with_removed_text. Translate st_size field for function symbols. (do_fix_for_relocatable_link): Change to return bfd_boolean to indicate failure. Add contents parameter. Update call to get_bfd_fix. Update call to r_reloc_init. Call _bfd_error_handler and return FALSE for R_XTENSA_ASM_EXPAND relocs. (do_fix_for_final_link): Add input_bfd and contents parameters. Update call to get_bfd_fix. Include offset from contents for partial_inplace relocations. (is_reloc_sym_weak): New. (pcrel_reloc_fits): Use new xtensa-isa.h functions. (prop_sec_len): New. (xtensa_is_property_section): Handle new property sections. (is_literal_section): Delete. (internal_reloc_compare): When r_offset matches, compare r_info and r_addend to make sorting predictable. (internal_reloc_matches): New. (xtensa_get_property_section_name): Handle new property sections. (xtensa_get_property_predef_flags): New. (xtensa_callback_required_dependence): Use bfd_get_section_limit. Update calls to xtensa_isa_init, is_l32r_relocation, and r_reloc_init. * xtensa-isa.c (xtensa_default_isa): Moved to elf32-xtensa.c. (xtisa_errno, xtisa_error_msg): New variables. (xtensa_isa_errno, xtensa_isa_error_msg): New. (xtensa_insnbuf_alloc): Add error handling. (xtensa_insnbuf_to_chars): Add num_chars parameter. Update to use xtensa_format_decode. Add error handling. (xtensa_insnbuf_from_chars): Add num_chars parameter. Decode the instruction length to find the number of bytes to copy. (xtensa_isa_init): Add error handling. Replace calls to xtensa_load_isa and xtensa_extend_isa with code to initialize lookup tables in the xtensa_modules structure. (xtensa_check_isa_config, xtensa_add_isa, xtensa_load_isa, xtensa_extend_isa): Delete. (xtensa_isa_free): Change to only free lookup tables. (opname_lookup_compare): Replace with ... (xtensa_isa_name_compare): ... this function. Use strcasecmp. (xtensa_insn_maxlength): Rename to ... (xtensa_isa_maxlength): ... this. (xtensa_insn_length): Delete. (xtensa_insn_length_from_first_byte): Replace with ... (xtensa_isa_length_from_chars): ... this function. (xtensa_num_opcodes): Rename to ... (xtensa_isa_num_opcodes): ... this. (xtensa_isa_num_pipe_stages, xtensa_isa_num_formats, xtensa_isa_num_regfiles, xtensa_isa_num_stages, xtensa_isa_num_sysregs, xtensa_isa_num_interfaces, xtensa_isa_num_funcUnits, xtensa_format_name, xtensa_format_lookup, xtensa_format_decode, xtensa_format_encode, xtensa_format_length, xtensa_format_num_slots, xtensa_format_slot_nop_opcode, xtensa_format_get_slot, xtensa_format_set_slot): New functions. (xtensa_opcode_lookup): Add error handling. (xtensa_decode_insn): Replace with ... (xtensa_opcode_decode): ... this function, with new format and slot parameters. Add error handling. (xtensa_encode_insn): Replace with ... (xtensa_opcode_encode): ... this function, which does the encoding via one of the entries in the "encode_fns" array. Add error handling. (xtensa_opcode_name): Add error handling. (xtensa_opcode_is_branch, xtensa_opcode_is_jump, xtensa_opcode_is_loop, xtensa_opcode_is_call): New. (xtensa_num_operands): Replace with ... (xtensa_opcode_num_operands): ... this function. Add error handling. (xtensa_opcode_num_stateOperands, xtensa_opcode_num_interfaceOperands, xtensa_opcode_num_funcUnit_uses, xtensa_opcode_funcUnit_use, xtensa_operand_name, xtensa_operand_is_visible): New. (xtensa_get_operand, xtensa_operand_kind): Delete. (xtensa_operand_inout): Add error handling and special-case for "sout" operands. (xtensa_operand_get_field, xtensa_operand_set_field): Rewritten to operate on one slot of an instruction. Added error handling. (xtensa_operand_encode): Handle default operands with no encoding functions. Check for success by comparing against decoded value. Add error handling. (xtensa_operand_decode): Handle default operands. Return decoded value through argument pointer. Add error handling. (xtensa_operand_is_register, xtensa_operand_regfile, xtensa_operand_num_regs, xtensa_operand_is_known_reg): New. (xtensa_operand_isPCRelative): Rename to ... (xtensa_operand_is_PCrelative): ... this. Add error handling. (xtensa_operand_do_reloc, xtensa_operand_undo_reloc): Return value through argument pointer. Add error handling. (xtensa_stateOperand_state, xtensa_stateOperand_inout, xtensa_interfaceOperand_interface, xtensa_regfile_lookup, xtensa_regfile_lookup_shortname, xtensa_regfile_name, xtensa_regfile_shortname, xtensa_regfile_view_parent, xtensa_regfile_num_bits, xtensa_regfile_num_entries, xtensa_state_lookup, xtensa_state_name, xtensa_state_num_bits, xtensa_state_is_exported, xtensa_sysreg_lookup, xtensa_sysreg_lookup_name, xtensa_sysreg_name, xtensa_sysreg_number, xtensa_sysreg_is_user, xtensa_interface_lookup, xtensa_interface_name, xtensa_interface_num_bits, xtensa_interface_inout, xtensa_interface_has_side_effect, xtensa_funcUnit_lookup, xtensa_funcUnit_name, xtensa_funcUnit_num_copies): New. * xtensa-modules.c: Rewrite to use new data structures. * reloc.c (BFD_RELOC_XTENSA_DIFF8, BFD_RELOC_XTENSA_DIFF16, BFD_RELOC_XTENSA_DIFF32, BFD_RELOC_XTENSA_SLOT0_OP, BFD_RELOC_XTENSA_SLOT1_OP, BFD_RELOC_XTENSA_SLOT2_OP, BFD_RELOC_XTENSA_SLOT3_OP, BFD_RELOC_XTENSA_SLOT4_OP, BFD_RELOC_XTENSA_SLOT5_OP, BFD_RELOC_XTENSA_SLOT6_OP, BFD_RELOC_XTENSA_SLOT7_OP, BFD_RELOC_XTENSA_SLOT8_OP, BFD_RELOC_XTENSA_SLOT9_OP, BFD_RELOC_XTENSA_SLOT10_OP, BFD_RELOC_XTENSA_SLOT11_OP, BFD_RELOC_XTENSA_SLOT12_OP, BFD_RELOC_XTENSA_SLOT13_OP, BFD_RELOC_XTENSA_SLOT14_OP, BFD_RELOC_XTENSA_SLOT0_ALT, BFD_RELOC_XTENSA_SLOT1_ALT, BFD_RELOC_XTENSA_SLOT2_ALT, BFD_RELOC_XTENSA_SLOT3_ALT, BFD_RELOC_XTENSA_SLOT4_ALT, BFD_RELOC_XTENSA_SLOT5_ALT, BFD_RELOC_XTENSA_SLOT6_ALT, BFD_RELOC_XTENSA_SLOT7_ALT, BFD_RELOC_XTENSA_SLOT8_ALT, BFD_RELOC_XTENSA_SLOT9_ALT, BFD_RELOC_XTENSA_SLOT10_ALT, BFD_RELOC_XTENSA_SLOT11_ALT, BFD_RELOC_XTENSA_SLOT12_ALT, BFD_RELOC_XTENSA_SLOT13_ALT, BFD_RELOC_XTENSA_SLOT14_ALT): Add new relocations. * Makefile.am (xtensa-isa.lo, xtensa-modules.lo): Update dependencies. * Makefile.in: Regenerate. * bfd-in2.h: Likewise. * libbfd.h: Likewise. gas ChangeLog * config/tc-xtensa.c (absolute_literals_supported): New global flag. (UNREACHABLE_MAX_WIDTH): Define. (XTENSA_FETCH_WIDTH): Delete. (cur_vinsn, xtensa_fetch_width, xt_saved_debug_type, past_xtensa_end, prefer_const16, prefer_l32r): New global variables. (LIT4_SECTION_NAME): Define. (lit4_state struct): Add lit4_seg_name and lit4_seg fields. (XTENSA_PROP_*, GET_XTENSA_PROP_*, SET_XTENSA_PROP_*): Define. (frag_flags struct): New. (xtensa_block_info struct): Move from tc-xtensa.h. Add flags field. (subseg_map struct): Add cur_total_freq and cur_target_freq fields. (bitfield, bit_is_set, set_bit, clear_bit): Define. (MAX_FORMATS): Define. (op_placement_info struct, op_placement_table): New. (O_pltrel, O_hi16, O_lo16): Define. (directiveE enum): Rename directive_generics to directive_transform. Delete directive_relax. Add directive_schedule, directive_absolute_literals, and directive_last_directive. (directive_info): Rename "generics" to "transform". Delete "relax". Add "schedule" and "absolute-literals". (directive_state): Adjust entries to match changes in directive_info. (xtensa_relax_statesE, RELAX_IMMED_MAXSTEPS): Move to tc-xtensa.h. (xtensa_const16_opcode, xtensa_movi_opcode, xtensa_movi_n_opcode, xtensa_l32r_opcode, xtensa_nop_opcode, xtensa_rsr_lcount_opcode): New. (xtensa_j_opcode, xtensa_rsr_opcode): Delete. (align_only_targets, software_a0_b_retw_interlock, software_avoid_b_j_loop_end, maybe_has_b_j_loop_end, software_avoid_short_loop, software_avoid_close_loop_end, software_avoid_all_short_loops, specific_opcode): Delete. (warn_unaligned_branch_targets): New. (workaround_a0_b_retw, workaround_b_j_loop_end, workaround_short_loop, workaround_close_loop_end, workaround_all_short_loops): Default FALSE. (option_[no_]link_relax, option_[no_]transform, option_[no_]absolute_literals, option_warn_unaligned_targets, option_prefer_l32r, option_prefer_const16, option_target_hardware): New enum values. (option_[no_]align_only_targets, option_literal_section_name, option_text_section_name, option_data_section_name, option_bss_section_name, option_eb, option_el): Delete. (md_longopts): Add entries for: [no-]transform, [no-]absolute-literals, warn-unaligned-targets, prefer-l32r, prefer-const16, [no-]link-relax, and target-hardware. Delete entries for [no-]target-align-only, literal-section-name, text-section-name, data-section-name, and bss-section-name. (md_parse_option): Handle new options and remove old ones. Accept but ignore [no-]density options. Warn for [no-]generics and [no-]relax and treat them as [no-]transform. (md_show_usage): Add new options and remove old ones. (xtensa_setup_hw_workarounds): New. (md_pseudo_table): Change "word" entry to use xtensa_elf_cons. Add "long", "short", "loc" and "frequency" entries. (use_generics): Rename to ... (use_transform): ... this function. Add past_xtensa_end check. (use_longcalls): Add past_xtensa_end check. (code_density_available, can_relax): Delete. (do_align_targets): New. (get_directive): Accept dashes in directive names. Warn about [no-]generics and [no-]relax directives and treat them as [no-]transform. (xtensa_begin_directive): Call md_flush_pending_output only for some directives. Check for directives inside instruction bundles. Warn about deprecated ".begin literal" usage. Warn and ignore [no-]density directives. Handle new directives. Check generating_literals flag for literal_prefix. (xtensa_end_directive): Check for directives inside instruction bundles. Warn and ignore [no-]density directives. Handle new directives. Call xtensa_set_frag_assembly_state. (xtensa_loc_directive_seen, xtensa_dwarf2_directive_loc, xtensa_dwarf2_emit_insn): New. (xtensa_literal_position): Call md_flush_pending_output. Do not check use_literal_section flag. (xtensa_literal_pseudo): Call md_flush_pending_output. Handle absolute literals. Use xtensa_elf_cons to parse the expression. (xtensa_literal_prefix): Do not check use_literal_section. Support ".lit4" sections for absolute literals. Change prefix convention to replace ".text" (or ".t" in a linkonce section). No need to call subseg_set. (xtensa_frequency_pseudo, xtensa_elf_cons, xtensa_elf_suffix): New. (expression_end): Handle closing braces and colons. (PLT_SUFFIX, plt_suffix): Delete. (expression_maybe_register): Use new xtensa-isa.h functions. Use xtensa_elf_suffix instead of checking for plt suffix, and handle O_lo16 and O_hi16 expressions as well. (tokenize_arguments): Handle closing braces and colons. (parse_arguments): Use new xtensa-isa.h functions. Handle "invisible" operands and paired register syntax. (get_invisible_operands): New. (xg_translate_sysreg_op): Handle new Xtensa LX RSR/WSR/XSR syntax. Use new xtensa-isa.h functions. (xtensa_translate_old_userreg_ops, xtensa_translate_zero_immed): New. (xg_translate_idioms): Check if inside bundle. Use use_transform. Handle new Xtensa LX RSR/WSR/XSR syntax. Remove code to widen density instructions. Use xtensa_translate_zero_immed. (operand_is_immed, operand_is_pcrel_label): Delete. (get_relaxable_immed): Use new xtensa-isa.h functions. (get_opcode_from_buf): Add slot parameter. Use new xtensa-isa.h functions. (xtensa_print_insn_table, print_vliw_insn): New. (is_direct_call_opcode): Use new xtensa-isa.h functions. (is_call_opcode, is_loop_opcode, is_conditional_branch_opcode, is_branch_or_jump_opcode): Delete. (is_movi_opcode, decode_reloc, encode_reloc, encode_alt_reloc): New. (opnum_to_reloc, reloc_to_opnum): Delete. (xtensa_insnbuf_set_operand, xtensa_insnbuf_get_operand): Use new xtensa-isa.h functions. Operate on one slot of an instruction. (xtensa_insnbuf_set_immediate_field, is_negatable_branch, xg_get_insn_size): Delete. (xg_get_build_instr_size): Use xg_get_single_size. (xg_is_narrow_insn, xg_is_single_relaxable_insn): Update calls to xg_build_widen_table. Use xg_get_single_size. (xg_get_max_narrow_insn_size): Delete. (xg_get_max_insn_widen_size, xg_get_max_insn_widen_literal_size, xg_is_relaxable_insn): Update calls to xg_build_widen_table. Use xg_get_single_size. (xg_build_to_insn): Record the loc field. Handle OP_OPERAND_HI16U and OP_OPERAND_LOW16U. Check xg_valid_literal_expression. (xg_expand_to_stack, xg_expand_narrow): Update calls to xg_build_widen_table. Use xg_get_single_size. (xg_immeds_fit): Use new xtensa-isa.h functions. Update call to xg_check_operand. (xg_symbolic_immeds_fit): Likewise. Also handle O_lo16 and O_hi16, and treat weak symbols conservatively. (xg_check_operand): Use new xtensa-isa.h functions. (is_dnrange): Delete. (xg_assembly_relax): Inline previous calls to tinsn_copy. (xg_finish_frag): Specify separate relax states for the frag and slot0. (is_branch_jmp_to_next, xg_add_branch_and_loop_targets): Use new xtensa-isa.h functions. (xg_instruction_matches_option_term, xg_instruction_matches_or_options, xg_instruction_matches_options): New. (xg_instruction_matches_rule): Handle O_register expressions. Call xg_instruction_matches_options. (transition_rule_cmp): New. (xg_instruction_match): Update call to xg_build_simplify_table. (xg_build_token_insn): Record loc fields. (xg_simplify_insn): Check is_specific_opcode field and density_supported flag. (xg_expand_assembly_insn): Skip checking code_density_available. Use new xtensa-isa.h functions. Call use_transform instead of can_relax. (xg_assemble_literal): Add error handling for O_big. Call record_alignment. Handle O_pltrel. (xg_valid_literal_expression): New. (xg_assemble_literal_space): Add slot parameter. Remove call to set_expr_symbol_offset. Add call to record_alignment. Update call to xg_finish_frag. (xg_emit_insn): Delete. (xg_emit_insn_to_buf): Add format parameter. Update calls to xg_add_opcode_fix and xtensa_insnbuf_to_chars. (xg_add_opcode_fix): Change opcode parameter to tinsn and add format and slot parameters. Handle new "alternate" relocations for absolute literals and CONST16 instructions. Check for bad uses of O_lo16 and O_hi16. Use new xtensa-isa.h functions. (xg_assemble_tokens): Delete. (is_register_writer): Use new xtensa-isa.h functions. (is_bad_loopend_opcode): Check for xtensa_rsr_lcount_opcode instead of old-style RSR from LCOUNT. (next_frag_opcode): Delete. (next_frag_opcode_is_loop, next_frag_format_size, frag_format_size, update_next_frag_state): New. (update_next_frag_nop_state): Delete. (next_frag_pre_opcode_bytes): Use next_frag_opcode_is_loop. (xtensa_mark_literal_pool_location): Check use_literal_section flag and the state of the absolute-literals directive. Add calls to record_alignment and xtensa_set_frag_assembly_state. Call xtensa_switch_to_non_abs_literal_fragment instead of xtensa_switch_to_literal_fragment. (build_nop): New. (assemble_nop): Use build_nop. Update call to xtensa_insnbuf_to_chars. (get_expanded_loop_offset): Change check for undefined opcode to an assertion. (xtensa_set_frag_assembly_state, relaxable_section, xtensa_find_unmarked_state_frags, xtensa_find_unaligned_branch_targets, xtensa_find_unaligned_loops, xg_apply_tentative_value): New. (md_begin): Update call to xtensa_isa_init. Initialize linkrelax to 1. Set lit4_seg_name. Call xg_init_vinsn. Initialize new global opcodes. Call init_op_placement_info_table and xtensa_set_frag_assembly_state. (xtensa_init_fix_data): New. (xtensa_frob_label): Reset label symbol to the current frag. Check do_align_targets and generating_literals flag. Propagate frequency info to new alignment frag. Call xtensa_set_frag_assembly_state. (xtensa_unrecognized_line): New. (xtensa_flush_pending_output): Check if inside a bundle. Add a call to xtensa_set_frag_assembly_state. (error_reset_cur_vinsn): New. (md_assemble): Remove check for literal frag. Remove call to istack_init. Call use_transform instead of use_generics. Parse explicit instruction format specifiers. Move code for a0_b_retw_interlock workaround to xg_assemble_vliw_tokens. Call error_reset_cur_vinsn on errors. Add call to get_invisible_operands. Add dwarf2_where call. Remote automatic alignment for ENTRY instructions. Move call to xtensa_clear_insn_labels to the end. Rearrange to handle bundles. (xtensa_cons_fix_new): Delete. (xtensa_handle_align): New. (xtensa_frag_init): Call xtensa_set_frag_assembly_state. Remove assignment to is_no_density field. (md_pcrel_from): Use new xtensa-isa.h functions. Use decode_reloc instead of reloc_to_opnum. Handle "alternate" relocations. (xtensa_force_relocation, xtensa_check_inside_bundle, xtensa_elf_section_change_hook): New. (xtensa_symbol_new_hook): Delete. (xtensa_fix_adjustable): Check for difference of symbols with an offset. Check for external and weak symbols. (md_apply_fix3): Remove cases for XTENSA_OP{0,1,2} relocs. (md_estimate_size_before_relax): Return expansion for the first slot. (tc_gen_reloc): Handle difference of symbols by producing XTENSA_DIFF{8,16,32} relocs and by writing the value of the difference into the output. Handle new XTENSA_SLOT*_OP relocs by storing the tentative values into the output when linkrelax is set. (XTENSA_PROP_SEC_NAME): Define. (xtensa_post_relax_hook): Call xtensa_find_unmarked_state_frags. Create literal tables only if using literal sections. Create new property tables instead of old instruction tables. Check for unaligned branch targets and loops. (finish_vinsn, find_vinsn_conflicts, check_t1_t2_reads_and_writes, new_resource_table, clear_resource_table, resize_resource_table, resources_available, reserve_resources, release_resources, opcode_funcUnit_use_unit, opcode_funcUnit_use_stage, resources_conflict, xg_find_narrowest_format, relaxation_requirements, bundle_single_op, emit_single_op, xg_assemble_vliw_tokens): New. (xtensa_end): Call xtensa_flush_pending_output. Set past_xtensa_end flag. Update checks for workaround options. Call xtensa_mark_narrow_branches and xtensa_mark_zcl_first_insns. (xtensa_cleanup_align_frags): Add special case for branch targets. Check for and mark unreachable frags. (xtensa_fix_target_frags): Remove use of align_only_targets flag. Use RELAX_LOOP_END_BYTES in special case for negatable branch at the end of a zero-overhead loop body. (frag_can_negate_branch): Handle instructions with multiple slots. Use new xtensa-isa.h functions (xtensa_mark_narrow_branches, is_narrow_branch_guaranteed_in_range, xtensa_mark_zcl_first_insns): New. (xtensa_fix_a0_b_retw_frags, xtensa_fix_b_j_loop_end_frags): Error if transformations are disabled. (next_instrs_are_b_retw): Use new xtensa-isa.h functions. Handle multislot instructions. (xtensa_fix_close_loop_end_frags, xtensa_fix_short_loop_frags): Likewise. Also error if transformations are disabled. (unrelaxed_frag_max_size): New. (unrelaxed_frag_min_insn_count, unrelax_frag_has_b_j): Use new xtensa-isa.h functions. (xtensa_sanity_check, is_empty_loop, is_local_forward_loop): Use xtensa_opcode_is_loop instead of is_loop_opcode. (get_text_align_power): Replace as_fatal with assertion. (get_text_align_fill_size): Iterate instead of using modulus when use_nops is false. (get_noop_aligned_address): Assert that this is for a machine-dependent RELAX_ALIGN_NEXT_OPCODE frag. Use next_frag_opcode_is_loop, xg_get_single_size, and frag_format_size. (get_widen_aligned_address): Rename to ... (get_aligned_diff): ... this function. Add max_diff parameter. Remove handling of rs_align/rs_align_code frags. Use next_frag_format_size, get_text_align_power, get_text_align_fill_size, next_frag_opcode_is_loop, and xg_get_single_size. Compute max_diff and pass it back to caller. (xtensa_relax_frag): Use relax_frag_loop_align. Add code for new RELAX_SLOTS, RELAX_MAYBE_UNREACHABLE, RELAX_MAYBE_DESIRE_ALIGN, RELAX_FILL_NOP, and RELAX_UNREACHABLE frag types. Check relax_seen. (relax_frag_text_align): Rename to ... (relax_frag_loop_align): ... this function. Assume loops can only be in the first slot of an instruction. (relax_frag_add_nop): Use assemble_nop instead of constructing an OR instruction. Remove call to frag_wane. (relax_frag_narrow): Rename to ... (relax_frag_for_align): ... this function. Extend to handle RELAX_FILL_NOP and RELAX_UNREACHABLE, as well as RELAX_SLOTS with RELAX_NARROW for the first slot. (find_address_of_next_align_frag, bytes_to_stretch): New. (future_alignment_required): Use find_address_of_next_align_frag and bytes_to_stretch. Look ahead to subsequent frags to make smarter alignment decisions. (relax_frag_immed): Add format, slot, and estimate_only parameters. Check if transformations are enabled for b_j_loop_end workaround. Use new xtensa-isa.h functions and handle multislot instructions. Update call to xg_assembly_relax. (md_convert_frag): Handle new RELAX_SLOTS, RELAX_UNREACHABLE, RELAX_MAYBE_UNREACHABLE, RELAX_MAYBE_DESIRE_ALIGN, and RELAX_FILL_NOP frag types. (convert_frag_narrow): Add segP, format and slot parameters. Call convert_frag_immed for branch instructions. Adjust calls to tinsn_from_chars, tinsn_immed_from_frag, and xg_emit_insn_to_buf. Use xg_get_single_size and xg_get_single_format. (convert_frag_fill_nop): New. (convert_frag_immed): Add format and slot parameters. Handle multislot instructions and use new xtensa-isa.h functions. Update calls to tinsn_immed_from_frag and xg_assembly_relax. Check if transformations enabled for b_j_loop_end workaround. Use build_nop instead of assemble_nop. Check is_specific_opcode flag. Check for unreachable frags. Use xg_get_single_size. Handle O_pltrel. (fix_new_exp_in_seg): Remove check for old plt flag. (convert_frag_immed_finish_loop): Update calls to tinsn_from_chars and xtensa_insnbuf_to_chars. Call tinsn_immed_from_frag. Change check for loop opcode to an assertion. Mark all frags up to the end of the loop as not transformable. (get_last_insn_flags, set_last_insn_flags): Use get_subseg_info. (get_subseg_info): New. (xtensa_move_literals): Call xtensa_set_frag_assembly_state. Add null check for dest_seg. (xtensa_switch_to_literal_fragment): Rewrite to handle absolute literals and use xtensa_switch_to_non_abs_literal_fragment otherwise. (xtensa_switch_to_non_abs_literal_fragment): New. (cache_literal_section): Add is_code parameter and pass it through to retrieve_literal_seg. (retrieve_literal_seg): Add is_code parameter and use it to set the flags on the literal section. Handle case where head parameter is 0. (get_frag_is_no_transform, set_frag_is_specific_opcode, set_frag_is_no_transform): New. (xtensa_create_property_segments): Add end_property_function parameter and pass it through to add_xt_block_frags. Call bfd_get_section_flags and skip SEC_DEBUGGING and !SEC_ALLOC sections. (xtensa_create_xproperty_segments, section_has_xproperty): New. (add_xt_block_frags): Add end_property_function parameter and call it if it is non-zero. Call xtensa_frag_flags_init. (xtensa_frag_flags_is_empty, xtensa_frag_flags_init, get_frag_property_flags, frag_flags_to_number, xtensa_frag_flags_combinable, xt_block_aligned_size, xtensa_xt_block_combine, add_xt_prop_frags, init_op_placement_info_table, opcode_fits_format_slot, xg_get_single_size, xg_get_single_format): New. (istack_push): Inline call to tinsn_copy. (tinsn_copy): Delete. (tinsn_has_invalid_symbolic_operands): Handle O_hi16 and O_lo16 and CONST16 opcodes. Handle O_big, O_illegal, and O_absent. (tinsn_has_complex_operands): Handle O_hi16 and O_lo16. (tinsn_to_insnbuf): Use xg_get_single_format and new xtensa-isa.h functions. Handle invisible operands. (tinsn_to_slotbuf): New. (tinsn_check_arguments): Use new xtensa-isa.h functions. (tinsn_from_chars): Add slot parameter. Rewrite using xg_init_vinsn, vinsn_from_chars, and xg_free_vinsn. (tinsn_from_insnbuf): New. (tinsn_immed_from_frag): Add slot parameter and handle multislot instructions. Handle symbol differences. (get_num_stack_text_bytes): Use xg_get_single_size. (xg_init_vinsn, xg_clear_vinsn, vinsn_has_specific_opcodes, xg_free_vinsn, vinsn_to_insnbuf, vinsn_from_chars, expr_is_register, get_expr_register, set_expr_symbol_offset_diff): New. * config/tc-xtensa.h (MAX_SLOTS): Define. (xtensa_relax_statesE): Move from tc-xtensa.c. Add RELAX_CHECK_ALIGN_NEXT_OPCODE, RELAX_MAYBE_DESIRE_ALIGN, RELAX_SLOTS, RELAX_FILL_NOP, RELAX_UNREACHABLE, RELAX_MAYBE_UNREACHABLE, and RELAX_NONE types. (RELAX_IMMED_MAXSTEPS): Move from tc-xtensa.c. (xtensa_frag_type struct): Add is_assembly_state_set, use_absolute_literals, relax_seen, is_unreachable, is_specific_opcode, is_align, is_text_align, alignment, and is_first_loop_insn fields. Replace is_generics and is_relax fields by is_no_transform field. Delete is_text and is_longcalls fields. Change text_expansion and literal_expansion to arrays of MAX_SLOTS entries. Add arrays of per-slot information: literal_frags, slot_subtypes, slot_symbols, slot_sub_symbols, and slot_offsets. Add fr_prev field. (xtensa_fix_data struct): New. (xtensa_symfield_type struct): Delete plt field. (xtensa_block_info struct): Move definition to tc-xtensa.h. Add forward declaration here. (xt_section_type enum): Delete xt_insn_sec. Add xt_prop_sec. (XTENSA_SECTION_RENAME): Undefine. (TC_FIX_TYPE, TC_INIT_FIX_DATA, TC_FORCE_RELOCATION, NO_PSEUDO_DOT, tc_unrecognized_line, md_do_align, md_elf_section_change_hook, HANDLE_ALIGN, TC_LINKRELAX_FIXUP, SUB_SEGMENT_ALIGN): Define. (TC_CONS_FIX_NEW, tc_symbol_new_hook): Delete. (unit_num_copies_func, opcode_num_units_func, opcode_funcUnit_use_unit_func, opcode_funcUnit_use_stage_func): New. (resource_table struct): New. * config/xtensa-istack.h (MAX_INSN_ARGS): Increase from 6 to 10. (TInsn struct): Add keep_wide, loc, fixup, record_fix, subtype, literal_space, symbol, sub_symbol, offset, and literal_frag fields. (tinsn_copy): Delete prototype. (vliw_insn struct): New. * config/xtensa-relax.c (insn_pattern_struct): Add options field. (widen_spec_list): Add option conditions for density and boolean instructions. Add expansions using CONST16 and conditions for using CONST16 vs. L32R. Use new Xtensa LX RSR/WSR syntax. Add entries for predicted branches. (simplify_spec_list): Add option conditions for density instructions. Add entry for NOP instruction. (append_transition): Add cmp function pointer parameter and use it to insert the new entry in order. (operand_function_LOW16U, operand_function_HI16U): New. (xg_has_userdef_op_fn, xg_apply_userdef_op_fn): Handle OP_OPERAND_LOW16U and OP_OPERAND_HI16U. (enter_opname, split_string): Use xstrdup instead of strdup. (init_insn_pattern): Initialize new options field. (clear_req_or_option_list, clear_req_option_list, clone_req_or_option_list, clone_req_option_list, parse_option_cond): New. (parse_insn_pattern): Parse option conditions. (transition_applies): New. (build_transition): Use new xtensa-isa.h functions. Fix incorrectly swapped last arguments in calls to append_constant_value_condition. Call clone_req_option_list. Add warning about invalid opcode. Handle LOW16U and HI16U function names. (build_transition_table): Add cmp parameter and use it in calls to append_transition. Use new xtensa-isa.h functions. Check transition_applies before adding entries. (xg_build_widen_table, xg_build_simplify_table): Add cmp parameter and pass it through to build_transition_table. * config/xtensa-relax.h (ReqOrOptionList, ReqOrOption, ReqOptionList, ReqOption, transition_cmp_fn): New types. (OpType enum): Add OP_OPERAND_LOW16U and OP_OPERAND_HI16U. (transition_rule struct): Add options field. * doc/as.texinfo (Overview): Update Xtensa options. * doc/c-xtensa.texi (Xtensa Options): Delete --[no-]density, --[no-]relax, and --[no-]generics options. Update descriptions of --text-section-literals and --[no-]longcalls. Add --[no-]absolute-literals and --[no-]transform. (Xtensa Syntax): Add description of syntax for FLIX instructions. Remove use of "generic" and "specific" terminology for opcodes. (Xtensa Registers): Generalize the syntax description to include user-defined register files. (Xtensa Automatic Alignment): Update. (Xtensa Branch Relaxation): Mention limitation of unconditional jumps. (Xtensa Call Relaxation): Linker can now remove most of the overhead. (Xtensa Directives): Remove confusing rules about precedence. (Density Directive, Relax Directive): Delete. (Schedule Directive): New. (Generics Directive): Rename to ... (Transform Directive): ... this node. (Literal Directive): Update for absolute literals. Missing literal_position directive is now an error. (Literal Position Directive): Update for absolute literals. (Freeregs Directive): Delete. (Absolute Literals Directive): New. (Frame Directive): Minor editing. * Makefile.am (DEPTC_xtensa_elf, DEPOBJ_xtensa_elf, DEP_xtensa_elf): Update dependencies. * Makefile.in: Regenerate. gas/testsuite ChangeLog * gas/xtensa/all.exp: Adjust expected error message for j_too_far. Change entry_align test to expect an error. * gas/xtensa/entry_misalign2.s: Use no-transform instead of no-generics directives. include ChangeLog * xtensa-config.h (XSHAL_USE_ABSOLUTE_LITERALS, XCHAL_HAVE_PREDICTED_BRANCHES, XCHAL_INST_FETCH_WIDTH): New. (XCHAL_EXTRA_SA_SIZE, XCHAL_EXTRA_SA_ALIGN): Delete. * xtensa-isa-internal.h (ISA_INTERFACE_VERSION): Delete. (config_sturct struct): Delete. (XTENSA_OPERAND_IS_REGISTER, XTENSA_OPERAND_IS_PCRELATIVE, XTENSA_OPERAND_IS_INVISIBLE, XTENSA_OPERAND_IS_UNKNOWN, XTENSA_OPCODE_IS_BRANCH, XTENSA_OPCODE_IS_JUMP, XTENSA_OPCODE_IS_LOOP, XTENSA_OPCODE_IS_CALL, XTENSA_STATE_IS_EXPORTED, XTENSA_INTERFACE_HAS_SIDE_EFFECT): Define. (xtensa_format_encode_fn, xtensa_get_slot_fn, xtensa_set_slot_fn): New. (xtensa_insn_decode_fn): Rename to ... (xtensa_opcode_decode_fn): ... this. (xtensa_immed_decode_fn, xtensa_immed_encode_fn, xtensa_do_reloc_fn, xtensa_undo_reloc_fn): Update. (xtensa_encoding_template_fn): Delete. (xtensa_opcode_encode_fn, xtensa_format_decode_fn, xtensa_length_decode_fn): New. (xtensa_format_internal, xtensa_slot_internal): New types. (xtensa_operand_internal): Delete operand_kind, inout, isPCRelative, get_field, and set_field fields. Add name, field_id, regfile, num_regs, and flags fields. (xtensa_arg_internal): New type. (xtensa_iclass_internal): Change operands field to array of xtensa_arg_internal. Add num_stateOperands, stateOperands, num_interfaceOperands, and interfaceOperands fields. (xtensa_opcode_internal): Delete length, template, and iclass fields. Add iclass_id, flags, encode_fns, num_funcUnit_uses, and funcUnit_uses. (opname_lookup_entry): Delete. (xtensa_regfile_internal, xtensa_interface_internal, xtensa_funcUnit_internal, xtensa_state_internal, xtensa_sysreg_internal, xtensa_lookup_entry): New. (xtensa_isa_internal): Replace opcode_table field with opcodes field. Change type of opname_lookup_table. Delete num_modules, module_opcode_base, module_decode_fn, config, and has_density fields. Add num_formats, formats, format_decode_fn, length_decode_fn, num_slots, slots, num_fields, num_operands, operands, num_iclasses, iclasses, num_regfiles, regfiles, num_states, states, state_lookup_table, num_sysregs, sysregs, sysreg_lookup_table, max_sysreg_num, sysreg_table, num_interfaces, interfaces, interface_lookup_table, num_funcUnits, funcUnits and funcUnit_lookup_table fields. (xtensa_isa_module, xtensa_isa_modules): Delete. (xtensa_isa_name_compare): New prototype. (xtisa_errno, xtisa_error_msg): New. * xtensa-isa.h (XTENSA_ISA_VERSION): Define. (xtensa_isa): Change type. (xtensa_operand): Delete. (xtensa_format, xtensa_regfile, xtensa_state, xtensa_sysreg, xtensa_interface, xtensa_funcUnit, xtensa_isa_status, xtensa_funcUnit_use): New types. (libisa_module_specifier): Delete. (xtensa_isa_errno, xtensa_isa_error_msg): New prototypes. (xtensa_insnbuf_free, xtensa_insnbuf_to_chars, xtensa_insnbuf_from_chars): Update prototypes. (xtensa_load_isa, xtensa_extend_isa, xtensa_default_isa, xtensa_insn_maxlength, xtensa_num_opcodes, xtensa_decode_insn, xtensa_encode_insn, xtensa_insn_length, xtensa_insn_length_from_first_byte, xtensa_num_operands, xtensa_operand_kind, xtensa_encode_result, xtensa_operand_isPCRelative): Delete. (xtensa_isa_init, xtensa_operand_inout, xtensa_operand_get_field, xtensa_operand_set_field, xtensa_operand_encode, xtensa_operand_decode, xtensa_operand_do_reloc, xtensa_operand_undo_reloc): Update prototypes. (xtensa_isa_maxlength, xtensa_isa_length_from_chars, xtensa_isa_num_pipe_stages, xtensa_isa_num_formats, xtensa_isa_num_opcodes, xtensa_isa_num_regfiles, xtensa_isa_num_states, xtensa_isa_num_sysregs, xtensa_isa_num_interfaces, xtensa_isa_num_funcUnits, xtensa_format_name, xtensa_format_lookup, xtensa_format_decode, xtensa_format_encode, xtensa_format_length, xtensa_format_num_slots, xtensa_format_slot_nop_opcode, xtensa_format_get_slot, xtensa_format_set_slot, xtensa_opcode_decode, xtensa_opcode_encode, xtensa_opcode_is_branch, xtensa_opcode_is_jump, xtensa_opcode_is_loop, xtensa_opcode_is_call, xtensa_opcode_num_operands, xtensa_opcode_num_stateOperands, xtensa_opcode_num_interfaceOperands, xtensa_opcode_num_funcUnit_uses, xtensa_opcode_funcUnit_use, xtensa_operand_name, xtensa_operand_is_visible, xtensa_operand_is_register, xtensa_operand_regfile, xtensa_operand_num_regs, xtensa_operand_is_known_reg, xtensa_operand_is_PCrelative, xtensa_stateOperand_state, xtensa_stateOperand_inout, xtensa_interfaceOperand_interface, xtensa_regfile_lookup, xtensa_regfile_lookup_shortname, xtensa_regfile_name, xtensa_regfile_shortname, xtensa_regfile_view_parent, xtensa_regfile_num_bits, xtensa_regfile_num_entries, xtensa_state_lookup, xtensa_state_name, xtensa_state_num_bits, xtensa_state_is_exported, xtensa_sysreg_lookup, xtensa_sysreg_lookup_name, xtensa_sysreg_name, xtensa_sysreg_number, xtensa_sysreg_is_user, xtensa_interface_lookup, xtensa_interface_name, xtensa_interface_num_bits, xtensa_interface_inout, xtensa_interface_has_side_effect, xtensa_funcUnit_lookup, xtensa_funcUnit_name, xtensa_funcUnit_num_copies): New prototypes. * elf/xtensa.h (R_XTENSA_DIFF8, R_XTENSA_DIFF16, R_XTENSA_DIFF32, R_XTENSA_SLOT*_OP, R_XTENSA_SLOT*_ALT): New relocations. (XTENSA_PROP_SEC_NAME): Define. (property_table_entry): Add flags field. (XTENSA_PROP_*, GET_XTENSA_PROP_*, SET_XTENSA_PROP_*): Define. ld ChangeLog * ld.texinfo (Xtensa): Describe new linker relaxation to optimize assembler-generated longcall sequences. Describe new --size-opt option. * emulparams/elf32xtensa.sh (OTHER_SECTIONS): Add .xt.prop section. * emultempl/xtensaelf.em (remove_section, replace_insn_sec_with_prop_sec, replace_instruction_table_sections, elf_xtensa_after_open): New. (OPTION_OPT_SIZEOPT, OPTION_LITERAL_MOVEMENT, OPTION_NO_LITERAL_MOVEMENT): Define. (elf32xtensa_size_opt, elf32xtensa_no_literal_movement): New globals. (PARSE_AND_LIST_LONGOPTS): Add size-opt and [no-]literal-movement. (PARSE_AND_LIST_OPTIONS): Add --size-opt. (PARSE_AND_LIST_ARGS_CASES): Handle OPTION_OPT_SIZEOPT, OPTION_LITERAL_MOVEMENT, and OPTION_NO_LITERAL_MOVEMENT. (LDEMUL_AFTER_OPEN): Set to elf_xtensa_after_open. * scripttempl/elfxtensa.sc: Update with changes from elf.sc. * Makefile.am (eelf32xtensa.c): Update dependencies. * Makefile.in: Regenerate. ld/testsuite ChangeLog * ld-xtensa/lcall1.s: Use .literal directive. * ld-xtensa/lcall2.s: Align function entry. * ld-xtensa/coalesce2.s: Likewise. opcodes ChangeLog * xtensa-dis.c (state_names): Delete. (fetch_data): Use xtensa_isa_maxlength. (print_xtensa_operand): Replace operand parameter with opcode/operand pair. Remove print_sr_name parameter. Use new xtensa-isa.h functions. (print_insn_xtensa): Use new xtensa-isa.h functions. Handle multislot instruction bundles. Use xmalloc instead of malloc.
Diffstat (limited to 'gas/config')
-rw-r--r--gas/config/tc-xtensa.c8989
-rw-r--r--gas/config/tc-xtensa.h338
-rw-r--r--gas/config/xtensa-istack.h55
-rw-r--r--gas/config/xtensa-relax.c614
-rw-r--r--gas/config/xtensa-relax.h62
5 files changed, 7452 insertions, 2606 deletions
diff --git a/gas/config/tc-xtensa.c b/gas/config/tc-xtensa.c
index 5d5ccea5da..ab626a4b01 100644
--- a/gas/config/tc-xtensa.c
+++ b/gas/config/tc-xtensa.c
@@ -1,5 +1,5 @@
/* tc-xtensa.c -- Assemble Xtensa instructions.
- Copyright 2003 Free Software Foundation, Inc.
+ Copyright 2003, 2004 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
@@ -15,10 +15,11 @@
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to
- the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
+ the Free Software Foundation, 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA. */
#include <string.h>
+#include <limits.h>
#include "as.h"
#include "sb.h"
#include "safe-ctype.h"
@@ -40,13 +41,6 @@
/* 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
@@ -71,17 +65,26 @@ 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. */
+/* Flags to indicate whether the hardware supports the density and
+ absolute literals options. */
-#if STATIC_LIBISA
bfd_boolean density_supported = XCHAL_HAVE_DENSITY;
-#else
-bfd_boolean density_supported = TRUE;
-#endif
+bfd_boolean absolute_literals_supported = XSHAL_USE_ABSOLUTE_LITERALS;
+
+/* Maximum width we would pad an unreachable frag to get alignment. */
+#define UNREACHABLE_MAX_WIDTH 8
+
+static void error_reset_cur_vinsn PARAMS ((void));
+static vliw_insn cur_vinsn;
+
+size_t xtensa_fetch_width = XCHAL_INST_FETCH_WIDTH;
-#define XTENSA_FETCH_WIDTH 4
+static enum debug_info_type xt_saved_debug_type = DEBUG_NONE;
+
+/* Some functions are only valid in the front end. This variable
+ allows us to assert that we haven't crossed over into the
+ back end. */
+static bfd_boolean past_xtensa_end = FALSE;
/* Flags for properties of the last instruction in a segment. */
#define FLAG_IS_A0_WRITER 0x1
@@ -94,21 +97,24 @@ bfd_boolean density_supported = TRUE;
their own special .fini.literal and .init.literal sections. */
#define LITERAL_SECTION_NAME xtensa_section_rename (".literal")
+#define LIT4_SECTION_NAME xtensa_section_rename (".lit4")
#define FINI_SECTION_NAME xtensa_section_rename (".fini")
#define INIT_SECTION_NAME xtensa_section_rename (".init")
#define FINI_LITERAL_SECTION_NAME xtensa_section_rename (".fini.literal")
#define INIT_LITERAL_SECTION_NAME xtensa_section_rename (".init.literal")
-/* This type is used for the directive_stack to keep track of the
+/* This type is used for the directive_stack to keep track of the
state of the literal collection pools. */
typedef struct lit_state_struct
{
const char *lit_seg_name;
+ const char *lit4_seg_name;
const char *init_lit_seg_name;
const char *fini_lit_seg_name;
segT lit_seg;
+ segT lit4_seg;
segT init_lit_seg;
segT fini_lit_seg;
} lit_state;
@@ -153,9 +159,145 @@ static sym_list *saved_insn_labels = NULL;
static sym_list *literal_syms;
+/* Flags to determine whether to prefer const16 or l32r
+ if both options are available. */
+int prefer_const16 = 0;
+int prefer_l32r = 0;
+
/* Global flag to indicate when we are emitting literals. */
int generating_literals = 0;
+/* The following PROPERTY table definitions are copied from
+ <elf/xtensa.h> and must be kept in sync with the code there. */
+
+/* Flags in the property tables to specify whether blocks of memory
+ are literals, instructions, data, or unreachable. For
+ instructions, blocks that begin loop targets and branch targets are
+ designated. Blocks that do not allow density, instruction
+ reordering or transformation are also specified. Finally, for
+ branch targets, branch target alignment priority is included.
+ Alignment of the next block is specified in the current block
+ and the size of the current block does not include any fill required
+ to align to the next block. */
+
+#define XTENSA_PROP_LITERAL 0x00000001
+#define XTENSA_PROP_INSN 0x00000002
+#define XTENSA_PROP_DATA 0x00000004
+#define XTENSA_PROP_UNREACHABLE 0x00000008
+/* Instruction only properties at beginning of code. */
+#define XTENSA_PROP_INSN_LOOP_TARGET 0x00000010
+#define XTENSA_PROP_INSN_BRANCH_TARGET 0x00000020
+/* Instruction only properties about code. */
+#define XTENSA_PROP_INSN_NO_DENSITY 0x00000040
+#define XTENSA_PROP_INSN_NO_REORDER 0x00000080
+#define XTENSA_PROP_INSN_NO_TRANSFORM 0x00000100
+
+/* Branch target alignment information. This transmits information
+ to the linker optimization about the priority of aligning a
+ particular block for branch target alignment: None, low priority,
+ high priority, or required. These only need to be checked in
+ instruction blocks marked as XTENSA_PROP_INSN_BRANCH_TARGET.
+ Common usage is
+
+ switch (GET_XTENSA_PROP_BT_ALIGN (flags))
+ case XTENSA_PROP_BT_ALIGN_NONE:
+ case XTENSA_PROP_BT_ALIGN_LOW:
+ case XTENSA_PROP_BT_ALIGN_HIGH:
+ case XTENSA_PROP_BT_ALIGN_REQUIRE:
+*/
+#define XTENSA_PROP_BT_ALIGN_MASK 0x00000600
+
+/* No branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_NONE 0x0
+/* Low priority branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_LOW 0x1
+/* High priority branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_HIGH 0x2
+/* Required branch target alignment. */
+#define XTENSA_PROP_BT_ALIGN_REQUIRE 0x3
+
+#define GET_XTENSA_PROP_BT_ALIGN(flag) \
+ (((unsigned) ((flag) & (XTENSA_PROP_BT_ALIGN_MASK))) >> 9)
+#define SET_XTENSA_PROP_BT_ALIGN(flag, align) \
+ (((flag) & (~XTENSA_PROP_BT_ALIGN_MASK)) | \
+ (((align) << 9) & XTENSA_PROP_BT_ALIGN_MASK))
+
+
+/* Alignment is specified in the block BEFORE the one that needs
+ alignment. Up to 5 bits. Use GET_XTENSA_PROP_ALIGNMENT(flags) to
+ get the required alignment specified as a power of 2. Use
+ SET_XTENSA_PROP_ALIGNMENT(flags, pow2) to set the required
+ alignment. Be careful of side effects since the SET will evaluate
+ flags twice. Also, note that the SIZE of a block in the property
+ table does not include the alignment size, so the alignment fill
+ must be calculated to determine if two blocks are contiguous.
+ TEXT_ALIGN is not currently implemented but is a placeholder for a
+ possible future implementation. */
+
+#define XTENSA_PROP_ALIGN 0x00000800
+
+#define XTENSA_PROP_ALIGNMENT_MASK 0x0001f000
+
+#define GET_XTENSA_PROP_ALIGNMENT(flag) \
+ (((unsigned) ((flag) & (XTENSA_PROP_ALIGNMENT_MASK))) >> 12)
+#define SET_XTENSA_PROP_ALIGNMENT(flag, align) \
+ (((flag) & (~XTENSA_PROP_ALIGNMENT_MASK)) | \
+ (((align) << 12) & XTENSA_PROP_ALIGNMENT_MASK))
+
+#define XTENSA_PROP_INSN_ABSLIT 0x00020000
+
+
+/* Structure for saving instruction and alignment per-fragment data
+ that will be written to the object file. This structure is
+ equivalent to the actual data that will be written out to the file
+ but is easier to use. We provide a conversion to file flags
+ in frag_flags_to_number. */
+
+typedef struct frag_flags_struct frag_flags;
+
+struct frag_flags_struct
+{
+ /* is_literal should only be used after xtensa_move_literals.
+ If you need to check if you are generating a literal fragment,
+ then use the generating_literals global. */
+
+ unsigned is_literal : 1;
+ unsigned is_insn : 1;
+ unsigned is_data : 1;
+ unsigned is_unreachable : 1;
+
+ struct
+ {
+ unsigned is_loop_target : 1;
+ unsigned is_branch_target : 1; /* Branch targets have a priority. */
+ unsigned bt_align_priority : 2;
+
+ unsigned is_no_density : 1;
+ /* no_longcalls flag does not need to be placed in the object file. */
+ /* is_specific_opcode implies no_transform. */
+ unsigned is_no_transform : 1;
+
+ unsigned is_no_reorder : 1;
+
+ /* Uses absolute literal addressing for l32r. */
+ unsigned is_abslit : 1;
+ } insn;
+ unsigned is_align : 1;
+ unsigned alignment : 5;
+};
+
+
+/* Structure for saving information about a block of property data
+ for frags that have the same flags. */
+struct xtensa_block_info_struct
+{
+ segT sec;
+ bfd_vma offset;
+ size_t size;
+ frag_flags flags;
+ struct xtensa_block_info_struct *next;
+};
+
/* Structure for saving the current state before emitting literals. */
typedef struct emit_state_struct
@@ -167,6 +309,82 @@ typedef struct emit_state_struct
} emit_state;
+/* 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;
+ /* the fall-through frequency + the branch target frequency
+ typically used for the instruction after a call */
+ float cur_total_freq;
+ /* the branch target frequency alone */
+ float cur_target_freq;
+
+ struct subseg_map_struct *next;
+} subseg_map;
+
+
+/* Opcode placement information */
+
+typedef unsigned long long bitfield;
+#define bit_is_set(bit, bf) ((bf) & (0x01ll << (bit)))
+#define set_bit(bit, bf) ((bf) |= (0x01ll << (bit)))
+#define clear_bit(bit, bf) ((bf) &= ~(0x01ll << (bit)))
+
+#define MAX_FORMATS 32
+
+typedef struct op_placement_info_struct
+{
+ int num_formats;
+ /* A number describing how restrictive the issue is for this
+ opcode. For example, an opcode that fits lots of different
+ formats has a high freedom, as does an opcode that fits
+ only one format but many slots in that format. The most
+ restrictive is the opcode that fits only one slot in one
+ format. */
+ int issuef;
+ /* The single format (i.e., if the op can live in a bundle by itself),
+ narrowest format, and widest format the op can be bundled in
+ and their sizes: */
+ xtensa_format single;
+ xtensa_format narrowest;
+ xtensa_format widest;
+ char narrowest_size;
+ char widest_size;
+ char single_size;
+
+ /* formats is a bitfield with the Nth bit set
+ if the opcode fits in the Nth xtensa_format. */
+ bitfield formats;
+
+ /* slots[N]'s Mth bit is set if the op fits in the
+ Mth slot of the Nth xtensa_format. */
+ bitfield slots[MAX_FORMATS];
+
+ /* A count of the number of slots in a given format
+ an op can fit (i.e., the bitcount of the slot field above). */
+ char slots_in_format[MAX_FORMATS];
+
+} op_placement_info, *op_placement_info_table;
+
+op_placement_info_table op_placement_table;
+
+
+/* Extra expression types. */
+
+#define O_pltrel O_md1 /* like O_symbol but use a PLT reloc */
+#define O_hi16 O_md2 /* use high 16 bits of symbolic value */
+#define O_lo16 O_md3 /* use low 16 bits of symbolic value */
+
+
/* Directives. */
typedef enum
@@ -174,11 +392,13 @@ typedef enum
directive_none = 0,
directive_literal,
directive_density,
- directive_generics,
- directive_relax,
+ directive_transform,
directive_freeregs,
directive_longcalls,
- directive_literal_prefix
+ directive_literal_prefix,
+ directive_schedule,
+ directive_absolute_literals,
+ directive_last_directive
} directiveE;
typedef struct
@@ -189,143 +409,51 @@ typedef struct
const directive_infoS directive_info[] =
{
- {"none", FALSE},
- {"literal", FALSE},
- {"density", TRUE},
- {"generics", TRUE},
- {"relax", TRUE},
- {"freeregs", FALSE},
- {"longcalls", TRUE},
- {"literal_prefix", FALSE}
+ { "none", FALSE },
+ { "literal", FALSE },
+ { "density", TRUE },
+ { "transform", TRUE },
+ { "freeregs", FALSE },
+ { "longcalls", TRUE },
+ { "literal_prefix", FALSE },
+ { "schedule", TRUE },
+ { "absolute-literals", TRUE }
};
bfd_boolean directive_state[] =
{
FALSE, /* none */
FALSE, /* literal */
-#if STATIC_LIBISA && !XCHAL_HAVE_DENSITY
+#if !XCHAL_HAVE_DENSITY
FALSE, /* density */
#else
TRUE, /* density */
#endif
- TRUE, /* generics */
- TRUE, /* relax */
+ TRUE, /* transform */
FALSE, /* freeregs */
FALSE, /* longcalls */
- FALSE /* literal_prefix */
-};
-
-
-enum xtensa_relax_statesE
-{
- RELAX_ALIGN_NEXT_OPCODE,
- /* Use the first opcode of the next fragment to determine the
- alignment requirements. This is ONLY used for LOOPS
- currently. */
-
- RELAX_DESIRE_ALIGN_IF_TARGET,
- /* These are placed in front of labels. They will all be converted
- to RELAX_DESIRE_ALIGN / RELAX_LOOP_END or rs_fill of 0 before
- relaxation begins. */
-
- RELAX_ADD_NOP_IF_A0_B_RETW,
- /* These are placed in front of conditional branches. It will be
- turned into a NOP (using a1) if the branch is immediately
- followed by a RETW or RETW.N. Otherwise it will be turned into
- an rs_fill of 0 before relaxation begins. */
-
- RELAX_ADD_NOP_IF_PRE_LOOP_END,
- /* These are placed after JX instructions. It will be turned into a
- NOP if there is one instruction before a loop end label.
- Otherwise it will be turned into an rs_fill of 0 before
- relaxation begins. This is used to avoid a hardware TIE
- interlock issue prior to T1040. */
-
- RELAX_ADD_NOP_IF_SHORT_LOOP,
- /* These are placed after LOOP instructions. It will be turned into
- a NOP when: (1) there are less than 3 instructions in the loop;
- we place 2 of these in a row to add up to 2 NOPS in short loops;
- or (2) The instructions in the loop do not include a branch or
- jump. Otherwise it will be turned into an rs_fill of 0 before
- relaxation begins. This is used to avoid hardware bug
- PR3830. */
-
- RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
- /* These are placed after LOOP instructions. It will be turned into
- a NOP if there are less than 12 bytes to the end of some other
- loop's end. Otherwise it will be turned into an rs_fill of 0
- before relaxation begins. This is used to avoid hardware bug
- PR3830. */
-
- RELAX_DESIRE_ALIGN,
- /* The next fragment like its first instruction to NOT cross a
- 4-byte boundary. */
-
- RELAX_LOOP_END,
- /* This will be turned into a NOP or NOP.N if the previous
- instruction is expanded to negate a loop. */
-
- RELAX_LOOP_END_ADD_NOP,
- /* When the code density option is available, this will generate a
- NOP.N marked RELAX_NARROW. Otherwise, it will create an rs_fill
- fragment with a NOP in it. */
-
- RELAX_LITERAL,
- /* Another fragment could generate an expansion here but has not yet. */
-
- RELAX_LITERAL_NR,
- /* Expansion has been generated by an instruction that generates a
- literal. However, the stretch has NOT been reported yet in this
- fragment. */
-
- RELAX_LITERAL_FINAL,
- /* Expansion has been generated by an instruction that generates a
- literal. */
-
- RELAX_LITERAL_POOL_BEGIN,
- RELAX_LITERAL_POOL_END,
- /* Technically these are not relaxations at all, but mark a location
- to store literals later. Note that fr_var stores the frchain for
- BEGIN frags and fr_var stores now_seg for END frags. */
-
- RELAX_NARROW,
- /* The last instruction in this fragment (at->fr_opcode) can be
- freely replaced with a single wider instruction if a future
- alignment desires or needs it. */
-
- RELAX_IMMED,
- /* The last instruction in this fragment (at->fr_opcode) contains
- the value defined by fr_symbol (fr_offset = 0). If the value
- does not fit, use the specified expansion. This is similar to
- "NARROW", except that these may not be expanded in order to align
- code. */
-
- RELAX_IMMED_STEP1,
- /* The last instruction in this fragment (at->fr_opcode) contains a
- literal. It has already been expanded at least 1 step. */
-
- RELAX_IMMED_STEP2
- /* The last instruction in this fragment (at->fr_opcode) contains a
- literal. It has already been expanded at least 2 steps. */
+ FALSE, /* literal_prefix */
+ TRUE, /* schedule */
+#if XSHAL_USE_ABSOLUTE_LITERALS
+ TRUE /* absolute_literals */
+#else
+ FALSE /* absolute_literals */
+#endif
};
-/* This is used as a stopper to bound the number of steps that
- can be taken. */
-#define RELAX_IMMED_MAXSTEPS (RELAX_IMMED_STEP2 - RELAX_IMMED)
-
-
typedef bfd_boolean (*frag_predicate) (const fragS *);
+typedef void (*frag_flags_fn) (const fragS *, frag_flags *);
+/* Command-line option functions. */
+static void xtensa_setup_hw_workarounds PARAMS ((int, int));
/* Directive functions. */
-static bfd_boolean use_generics
+static bfd_boolean use_transform
PARAMS ((void));
static bfd_boolean use_longcalls
PARAMS ((void));
-static bfd_boolean code_density_available
- PARAMS ((void));
-static bfd_boolean can_relax
+static bfd_boolean do_align_targets
PARAMS ((void));
static void directive_push
PARAMS ((directiveE, bfd_boolean, const void *));
@@ -342,12 +470,22 @@ static void xtensa_begin_directive
PARAMS ((int));
static void xtensa_end_directive
PARAMS ((int));
+static void xtensa_dwarf2_directive_loc
+ PARAMS ((int));
+static void xtensa_dwarf2_emit_insn
+ PARAMS ((int, struct dwarf2_line_info *));
static void xtensa_literal_prefix
PARAMS ((char const *, int));
static void xtensa_literal_position
PARAMS ((int));
static void xtensa_literal_pseudo
PARAMS ((int));
+static void xtensa_frequency_pseudo
+ PARAMS ((int));
+static void xtensa_elf_cons
+ PARAMS ((int));
+static bfd_reloc_code_real_type xtensa_elf_suffix
+ PARAMS ((char **, expressionS *));
/* Parsing and Idiom Translation Functions. */
@@ -356,15 +494,21 @@ static const char *expression_end
static unsigned tc_get_register
PARAMS ((const char *));
static void expression_maybe_register
- PARAMS ((xtensa_operand, expressionS *));
+ PARAMS ((xtensa_opcode, int, expressionS *));
static int tokenize_arguments
PARAMS ((char **, char *));
static bfd_boolean parse_arguments
PARAMS ((TInsn *, int, char **));
+static int get_invisible_operands
+ PARAMS ((TInsn *));
static int xg_translate_idioms
PARAMS ((char **, int *, char **));
static int xg_translate_sysreg_op
PARAMS ((char **, int *, char **));
+static int xtensa_translate_old_userreg_ops
+ PARAMS ((char **));
+static int xtensa_translate_zero_immed
+ PARAMS ((char *, char *, char **, int *, char **));
static void xg_reverse_shift_count
PARAMS ((char **));
static int xg_arg_is_constant
@@ -376,21 +520,21 @@ static int xg_check_num_args
/* 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 *));
+ PARAMS ((const char *, int));
+#ifdef TENSILICA_DEBUG
+static void xtensa_print_insn_table
+ PARAMS ((void));
+static void print_vliw_insn
+ PARAMS ((xtensa_insnbuf));
+#endif
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
+static bfd_boolean is_movi_opcode
PARAMS ((xtensa_opcode));
static bfd_boolean is_the_loop_opcode
PARAMS ((xtensa_opcode));
@@ -398,39 +542,28 @@ 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
+static int decode_reloc
+ PARAMS ((bfd_reloc_code_real_type, int *, bfd_boolean *));
+static bfd_reloc_code_real_type encode_reloc
+ PARAMS ((int));
+static bfd_reloc_code_real_type encode_alt_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,
+ PARAMS ((xtensa_insnbuf, xtensa_format, int, xtensa_opcode, int, uint32,
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 *));
+ PARAMS ((xtensa_insnbuf, xtensa_format, int, xtensa_opcode, int));
/* 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
@@ -454,23 +587,30 @@ static bfd_boolean xg_immeds_fit
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));
+ PARAMS ((int32, xtensa_opcode, int));
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));
+ PARAMS ((char *, enum xtensa_relax_statesE, 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_options
+ PARAMS ((TInsn *, const ReqOption *));
+static bfd_boolean xg_instruction_matches_or_options
+ PARAMS ((TInsn *, const ReqOrOptionList *));
+static bfd_boolean xg_instruction_matches_option_term
+ PARAMS ((TInsn *, const ReqOrOption *));
static bfd_boolean xg_instruction_matches_rule
PARAMS ((TInsn *, TransitionRule *));
static TransitionRule *xg_instruction_match
PARAMS ((TInsn *));
+static int transition_rule_cmp
+ PARAMS ((const TransitionRule *, const TransitionRule *));
static bfd_boolean xg_build_token_insn
PARAMS ((BuildInstr *, TInsn *, TInsn *));
static bfd_boolean xg_simplify_insn
@@ -479,8 +619,10 @@ static bfd_boolean xg_expand_assembly_insn
PARAMS ((IStack *, TInsn *));
static symbolS *xg_assemble_literal
PARAMS ((TInsn *));
+static bfd_boolean xg_valid_literal_expression
+ PARAMS ((const expressionS *));
static void xg_assemble_literal_space
- PARAMS ((int));
+ PARAMS ((int, int));
static symbolS *xtensa_create_literal_symbol
PARAMS ((segT, fragS *));
static void xtensa_add_literal_sym
@@ -491,18 +633,14 @@ 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));
+ PARAMS ((TInsn *, xtensa_format, char *, fragS *, offsetT, bfd_boolean));
static bfd_boolean xg_add_opcode_fix
- PARAMS ((xtensa_opcode, int, expressionS *, fragS *, offsetT));
+ PARAMS ((TInsn *, int, xtensa_format, 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
@@ -511,10 +649,14 @@ static bfd_boolean is_unaligned_label
PARAMS ((symbolS *));
static fragS *next_non_empty_frag
PARAMS ((const fragS *));
-static xtensa_opcode next_frag_opcode
+static bfd_boolean next_frag_opcode_is_loop
+ PARAMS ((const fragS *, xtensa_opcode *));
+static int next_frag_format_size
PARAMS ((const fragS *));
-static void update_next_frag_nop_state
- PARAMS ((fragS *));
+static int frag_format_size
+ PARAMS ((const fragS *));
+static void update_next_frag_state
+ PARAMS ((fragS *, bfd_boolean));
static bfd_boolean next_frag_is_branch_target
PARAMS ((const fragS *));
static bfd_boolean next_frag_is_loop_target
@@ -529,12 +671,44 @@ static void xtensa_move_labels
PARAMS ((fragS *, valueT, bfd_boolean));
static void assemble_nop
PARAMS ((size_t, char *));
+static void build_nop
+ PARAMS ((TInsn *, int));
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 *));
+static void xtensa_set_frag_assembly_state
+ PARAMS ((fragS *));
+static bfd_boolean relaxable_section
+ PARAMS ((asection *));
+static void xtensa_find_unmarked_state_frags
+ PARAMS ((void));
+static void xtensa_find_unaligned_branch_targets
+ PARAMS ((bfd *, asection *, PTR));
+static void xtensa_find_unaligned_loops
+ PARAMS ((bfd *, asection *, PTR));
+static void xg_apply_tentative_value
+ PARAMS ((fixS *, valueT));
+static void finish_vinsn
+ PARAMS ((vliw_insn *));
+static bfd_boolean find_vinsn_conflicts
+ PARAMS ((vliw_insn *));
+static char check_t1_t2_reads_and_writes
+ PARAMS ((TInsn *, TInsn *));
+static bfd_boolean resources_conflict
+ PARAMS ((vliw_insn *));
+static xtensa_format xg_find_narrowest_format
+ PARAMS ((vliw_insn *));
+static int relaxation_requirements
+ PARAMS ((vliw_insn *));
+static void bundle_single_op
+ PARAMS ((TInsn *));
+static bfd_boolean emit_single_op
+ PARAMS ((TInsn *));
+static void xg_assemble_vliw_tokens
+ PARAMS ((vliw_insn *));
/* Helpers for xtensa_end(). */
@@ -544,6 +718,12 @@ static void xtensa_fix_target_frags
PARAMS ((void));
static bfd_boolean frag_can_negate_branch
PARAMS ((fragS *));
+static void xtensa_mark_narrow_branches
+ PARAMS ((void));
+static bfd_boolean is_narrow_branch_guaranteed_in_range
+ PARAMS ((fragS *, TInsn *));
+static void xtensa_mark_zcl_first_insns
+ PARAMS ((void));
static void xtensa_fix_a0_b_retw_frags
PARAMS ((void));
static bfd_boolean next_instrs_are_b_retw
@@ -558,6 +738,8 @@ 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 size_t unrelaxed_frag_max_size
+ PARAMS ((fragS *));
static void xtensa_fix_short_loop_frags
PARAMS ((void));
static size_t count_insns_to_loop_end
@@ -589,30 +771,36 @@ static size_t get_text_align_nth_nop_size
PARAMS ((size_t, size_t, bfd_boolean));
static addressT get_noop_aligned_address
PARAMS ((fragS *, addressT));
-static addressT get_widen_aligned_address
- PARAMS ((fragS *, addressT));
+static addressT get_aligned_diff
+ PARAMS ((fragS *, addressT, addressT *));
/* Helpers for xtensa_relax_frag(). */
-static long relax_frag_text_align
+static long relax_frag_loop_align
PARAMS ((fragS *, long));
static long relax_frag_add_nop
PARAMS ((fragS *));
-static long relax_frag_narrow
+static long relax_frag_for_align
PARAMS ((fragS *, long));
-static bfd_boolean future_alignment_required
+static long future_alignment_required
PARAMS ((fragS *, long));
+static addressT find_address_of_next_align_frag
+ PARAMS ((fragS **, int *, int *, int *, bfd_boolean *));
+static long bytes_to_stretch
+ PARAMS ((fragS *, int, int, int, int));
static long relax_frag_immed
- PARAMS ((segT, fragS *, long, int, int *));
+ PARAMS ((segT, fragS *, long, int, xtensa_format, int, int *, bfd_boolean));
/* Helpers for md_convert_frag(). */
static void convert_frag_align_next_opcode
PARAMS ((fragS *));
static void convert_frag_narrow
+ PARAMS ((segT, fragS *, xtensa_format, int));
+static void convert_frag_fill_nop
PARAMS ((fragS *));
static void convert_frag_immed
- PARAMS ((segT, fragS *, int));
+ PARAMS ((segT, fragS *, int, xtensa_format, int));
static fixS *fix_new_exp_in_seg
PARAMS ((segT, subsegT, fragS *, int, int, expressionS *, int,
bfd_reloc_code_real_type));
@@ -636,6 +824,8 @@ static void xtensa_insert_section
PARAMS ((segT, segT));
static void xtensa_move_seg_list_to_beginning
PARAMS ((seg_list *));
+static subseg_map *get_subseg_info
+ PARAMS ((segT, subsegT));
static void xtensa_move_literals
PARAMS ((void));
static void mark_literal_frags
@@ -648,41 +838,82 @@ static segT get_last_sec
PARAMS ((void));
static void xtensa_switch_to_literal_fragment
PARAMS ((emit_state *));
+static void xtensa_switch_to_non_abs_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 *));
+ PARAMS ((seg_list *, const char *, segT *, bfd_boolean));
static segT retrieve_literal_seg
- PARAMS ((seg_list *, const char *));
+ PARAMS ((seg_list *, const char *, bfd_boolean));
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. */
+/* Property flags on fragments and conversion to object file flags. */
+static bfd_boolean get_frag_is_literal
+ PARAMS ((const fragS *));
+static bfd_boolean get_frag_is_insn
+ PARAMS ((const fragS *));
+static bfd_boolean get_frag_is_no_transform
+ PARAMS ((fragS *));
+static void set_frag_is_specific_opcode
+ PARAMS ((fragS *, bfd_boolean));
+static void set_frag_is_no_transform
+ PARAMS ((fragS *, bfd_boolean));
static void xtensa_create_property_segments
- PARAMS ((frag_predicate, const char *, xt_section_type));
+ PARAMS ((frag_predicate, frag_predicate, const char *, xt_section_type));
+static void xtensa_create_xproperty_segments
+ PARAMS ((frag_flags_fn, 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));
+ PARAMS ((segT, frag_predicate));
+static bfd_boolean section_has_xproperty
+ PARAMS ((segT, frag_flags_fn));
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 *));
+ PARAMS ((segT, segT, xtensa_block_info **, frag_predicate, frag_predicate));
+static bfd_boolean xtensa_frag_flags_is_empty
+ PARAMS ((const frag_flags *));
+static void xtensa_frag_flags_init
+ PARAMS ((frag_flags *));
+static void get_frag_property_flags
+ PARAMS ((const fragS *, frag_flags *));
+static bfd_vma frag_flags_to_number
+ PARAMS ((const frag_flags *));
+static bfd_boolean xtensa_frag_flags_combinable
+ PARAMS ((const frag_flags *, const frag_flags *));
+static bfd_vma xt_block_aligned_size
+ PARAMS ((const xtensa_block_info *));
+static bfd_boolean xtensa_xt_block_combine
+ PARAMS ((xtensa_block_info *, const xtensa_block_info *));
+static void add_xt_prop_frags
+ PARAMS ((segT, segT, xtensa_block_info **, frag_flags_fn));
/* Import from elf32-xtensa.c in BFD library. */
+
extern char *xtensa_get_property_section_name
PARAMS ((asection *, const char *));
+/* op_placement_info functions. */
+
+static void init_op_placement_info_table
+ PARAMS ((void));
+extern bfd_boolean opcode_fits_format_slot
+ PARAMS ((xtensa_opcode, xtensa_format, int));
+static int xg_get_single_size
+ PARAMS ((xtensa_opcode));
+static xtensa_format xg_get_single_format
+ PARAMS ((xtensa_opcode));
+
/* TInsn and IStack functions. */
+
static bfd_boolean tinsn_has_symbolic_operands
PARAMS ((const TInsn *));
static bfd_boolean tinsn_has_invalid_symbolic_operands
@@ -691,26 +922,52 @@ static bfd_boolean tinsn_has_complex_operands
PARAMS ((const TInsn *));
static bfd_boolean tinsn_to_insnbuf
PARAMS ((TInsn *, xtensa_insnbuf));
+static bfd_boolean tinsn_to_slotbuf
+ PARAMS ((xtensa_format, int, TInsn *, xtensa_insnbuf));
static bfd_boolean tinsn_check_arguments
PARAMS ((const TInsn *));
static void tinsn_from_chars
- PARAMS ((TInsn *, char *));
+ PARAMS ((TInsn *, char *, int));
+static void tinsn_from_insnbuf
+ PARAMS ((TInsn *, xtensa_insnbuf, xtensa_format, int));
static void tinsn_immed_from_frag
- PARAMS ((TInsn *, fragS *));
+ PARAMS ((TInsn *, fragS *, int));
static int get_num_stack_text_bytes
PARAMS ((IStack *));
static int get_num_stack_literal_bytes
PARAMS ((IStack *));
+/* vliw_insn functions. */
+
+static void xg_init_vinsn
+ PARAMS ((vliw_insn *));
+static void xg_clear_vinsn
+ PARAMS ((vliw_insn *));
+static bfd_boolean vinsn_has_specific_opcodes
+ PARAMS ((vliw_insn *));
+static void xg_free_vinsn
+ PARAMS ((vliw_insn *));
+static bfd_boolean vinsn_to_insnbuf
+ PARAMS ((vliw_insn *, char *, fragS *, bfd_boolean));
+static void vinsn_from_chars
+ PARAMS ((vliw_insn *, char *));
+
/* Expression Utilities. */
+
bfd_boolean expr_is_const
PARAMS ((const expressionS *));
offsetT get_expr_const
PARAMS ((const expressionS *));
void set_expr_const
PARAMS ((expressionS *, offsetT));
+bfd_boolean expr_is_register
+ PARAMS ((const expressionS *));
+offsetT get_expr_register
+ PARAMS ((const expressionS *));
void set_expr_symbol_offset
PARAMS ((expressionS *, symbolS *, offsetT));
+static void set_expr_symbol_offset_diff
+ PARAMS ((expressionS *, symbolS *, symbolS *, offsetT));
bfd_boolean expr_is_equal
PARAMS ((expressionS *, expressionS *));
static void copy_expr
@@ -739,20 +996,24 @@ static xtensa_opcode xtensa_callx0_opcode;
static xtensa_opcode xtensa_callx4_opcode;
static xtensa_opcode xtensa_callx8_opcode;
static xtensa_opcode xtensa_callx12_opcode;
+static xtensa_opcode xtensa_const16_opcode;
static xtensa_opcode xtensa_entry_opcode;
+static xtensa_opcode xtensa_movi_opcode;
+static xtensa_opcode xtensa_movi_n_opcode;
static xtensa_opcode xtensa_isync_opcode;
-static xtensa_opcode xtensa_j_opcode;
static xtensa_opcode xtensa_jx_opcode;
+static xtensa_opcode xtensa_l32r_opcode;
static xtensa_opcode xtensa_loop_opcode;
static xtensa_opcode xtensa_loopnez_opcode;
static xtensa_opcode xtensa_loopgtz_opcode;
+static xtensa_opcode xtensa_nop_opcode;
static xtensa_opcode xtensa_nop_n_opcode;
static xtensa_opcode xtensa_or_opcode;
static xtensa_opcode xtensa_ret_opcode;
static xtensa_opcode xtensa_ret_n_opcode;
static xtensa_opcode xtensa_retw_opcode;
static xtensa_opcode xtensa_retw_n_opcode;
-static xtensa_opcode xtensa_rsr_opcode;
+static xtensa_opcode xtensa_rsr_lcount_opcode;
static xtensa_opcode xtensa_waiti_opcode;
@@ -760,34 +1021,22 @@ static xtensa_opcode xtensa_waiti_opcode;
bfd_boolean use_literal_section = TRUE;
static bfd_boolean align_targets = TRUE;
-static bfd_boolean align_only_targets = FALSE;
-static bfd_boolean software_a0_b_retw_interlock = TRUE;
+static bfd_boolean warn_unaligned_branch_targets = FALSE;
static bfd_boolean has_a0_b_retw = FALSE;
-static bfd_boolean workaround_a0_b_retw = TRUE;
-
-static bfd_boolean 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 workaround_a0_b_retw = FALSE;
+static bfd_boolean workaround_b_j_loop_end = FALSE;
+static bfd_boolean workaround_short_loop = FALSE;
static bfd_boolean maybe_has_short_loop = FALSE;
-
-static bfd_boolean software_avoid_close_loop_end = TRUE;
-static bfd_boolean workaround_close_loop_end = TRUE;
+static bfd_boolean workaround_close_loop_end = FALSE;
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;
+/* When workaround_short_loops is TRUE, all loops with early exits must
+ have at least 3 instructions. workaround_all_short_loops is a modifier
+ to the workaround_short_loop flag. In addition to the
+ workaround_short_loop actions, all straightline loopgtz and loopnez
+ must have at least 3 instructions. */
-/* This is on a per-instruction basis. */
-static bfd_boolean specific_opcode = FALSE;
+static bfd_boolean workaround_all_short_loops = FALSE;
enum
{
@@ -797,17 +1046,25 @@ enum
option_relax,
option_no_relax,
+ option_link_relax,
+ option_no_link_relax,
+
option_generics,
option_no_generics,
+ option_transform,
+ option_no_transform,
+
option_text_section_literals,
option_no_text_section_literals,
+ option_absolute_literals,
+ option_no_absolute_literals,
+
option_align_targets,
option_no_align_targets,
- option_align_only_targets,
- option_no_align_only_targets,
+ option_warn_unaligned_targets,
option_longcalls,
option_no_longcalls,
@@ -830,88 +1087,84 @@ enum
option_no_workarounds,
#ifdef XTENSA_SECTION_RENAME
- option_literal_section_name,
- option_text_section_name,
- option_data_section_name,
- option_bss_section_name,
option_rename_section_name,
#endif
- option_eb,
- option_el
+ option_prefer_l32r,
+ option_prefer_const16,
+
+ option_target_hardware
};
const char *md_shortopts = "";
struct option md_longopts[] =
{
- {"density", no_argument, NULL, option_density},
- {"no-density", no_argument, NULL, option_no_density},
- /* At least as early as alameda, --[no-]relax didn't work as
- documented, so as of albany, --[no-]relax is equivalent to
- --[no-]generics. Both of these will be deprecated in
- BearValley. */
- {"relax", no_argument, NULL, option_generics},
- {"no-relax", no_argument, NULL, option_no_generics},
- {"generics", no_argument, NULL, option_generics},
- {"no-generics", no_argument, NULL, option_no_generics},
- {"text-section-literals", no_argument, NULL, option_text_section_literals},
- {"no-text-section-literals", no_argument, NULL,
- option_no_text_section_literals},
+ { "density", no_argument, NULL, option_density },
+ { "no-density", no_argument, NULL, option_no_density },
+
+ /* Both "relax" and "generics" are deprecated and treated as equivalent
+ to the "transform" option. */
+ { "relax", no_argument, NULL, option_relax },
+ { "no-relax", no_argument, NULL, option_no_relax },
+ { "generics", no_argument, NULL, option_generics },
+ { "no-generics", no_argument, NULL, option_no_generics },
+
+ { "transform", no_argument, NULL, option_transform },
+ { "no-transform", no_argument, NULL, option_no_transform },
+ { "text-section-literals", no_argument, NULL, option_text_section_literals },
+ { "no-text-section-literals", no_argument, NULL,
+ option_no_text_section_literals },
+ { "absolute-literals", no_argument, NULL, option_absolute_literals },
+ { "no-absolute-literals", no_argument, NULL, option_no_absolute_literals },
/* This option was changed from -align-target to -target-align
because it conflicted with the "-al" option. */
- {"target-align", no_argument, NULL, option_align_targets},
- {"no-target-align", no_argument, NULL,
- option_no_align_targets},
-#if 0
- /* This option should do a better job aligning targets because
- it will only attempt to align targets that are the target of a
- branch. */
- { "target-align-only", no_argument, NULL, option_align_only_targets },
- { "no-target-align-only", no_argument, NULL, option_no_align_only_targets },
-#endif /* 0 */
- {"longcalls", no_argument, NULL, option_longcalls},
- {"no-longcalls", no_argument, NULL, option_no_longcalls},
-
- {"no-workaround-a0-b-retw", no_argument, NULL,
- option_no_workaround_a0_b_retw},
- {"workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw},
-
- {"no-workaround-b-j-loop-end", no_argument, NULL,
- option_no_workaround_b_j_loop_end},
- {"workaround-b-j-loop-end", no_argument, NULL,
- option_workaround_b_j_loop_end},
-
- {"no-workaround-short-loops", no_argument, NULL,
- option_no_workaround_short_loop},
- {"workaround-short-loops", no_argument, NULL, option_workaround_short_loop},
+ { "target-align", no_argument, NULL, option_align_targets },
+ { "no-target-align", no_argument, NULL,
+ option_no_align_targets },
+ { "warn-unaligned-targets", no_argument, NULL, option_warn_unaligned_targets },
+ { "longcalls", no_argument, NULL, option_longcalls },
+ { "no-longcalls", no_argument, NULL, option_no_longcalls },
+
+ { "no-workaround-a0-b-retw", no_argument, NULL,
+ option_no_workaround_a0_b_retw },
+ { "workaround-a0-b-retw", no_argument, NULL, option_workaround_a0_b_retw },
+
+ { "no-workaround-b-j-loop-end", no_argument, NULL,
+ option_no_workaround_b_j_loop_end },
+ { "workaround-b-j-loop-end", no_argument, NULL,
+ option_workaround_b_j_loop_end },
+
+ { "no-workaround-short-loops", no_argument, NULL,
+ option_no_workaround_short_loop },
+ { "workaround-short-loops", no_argument, NULL, option_workaround_short_loop },
- {"no-workaround-all-short-loops", no_argument, NULL,
- option_no_workaround_all_short_loops},
- {"workaround-all-short-loop", no_argument, NULL,
- option_workaround_all_short_loops},
+ { "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},
+ { "prefer-l32r", no_argument, NULL, option_prefer_l32r },
+ { "prefer-const16", no_argument, NULL, option_prefer_const16 },
- {"no-workarounds", no_argument, NULL, option_no_workarounds},
+ { "no-workarounds", no_argument, NULL, option_no_workarounds },
+
+ { "no-workaround-close-loop-end", no_argument, NULL,
+ option_no_workaround_close_loop_end },
+ { "workaround-close-loop-end", no_argument, NULL,
+ option_workaround_close_loop_end },
#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},
+ { "rename-section", required_argument, NULL,
+ option_rename_section_name },
#endif /* XTENSA_SECTION_RENAME */
- {NULL, no_argument, NULL, 0}
+ { "link-relax", no_argument, NULL, option_link_relax },
+ { "no-link-relax", no_argument, NULL, option_no_link_relax },
+
+ { "target-hardware", required_argument, NULL, option_target_hardware },
+
+ { NULL, no_argument, NULL, 0 }
};
size_t md_longopts_size = sizeof md_longopts;
@@ -925,23 +1178,29 @@ md_parse_option (c, arg)
switch (c)
{
case option_density:
- if (!density_supported)
- {
- as_bad (_("'--density' option not supported in this Xtensa "
- "configuration"));
- return 0;
- }
- directive_state[directive_density] = TRUE;
+ as_warn (_("--density option is ignored"));
return 1;
case option_no_density:
- directive_state[directive_density] = FALSE;
+ as_warn (_("--no-density option is ignored"));
return 1;
- case option_generics:
- directive_state[directive_generics] = TRUE;
+ case option_link_relax:
+ linkrelax = 1;
return 1;
- case option_no_generics:
- directive_state[directive_generics] = FALSE;
+ case option_no_link_relax:
+ linkrelax = 0;
return 1;
+ case option_generics:
+ as_warn (_("--generics is deprecated; use --transform instead"));
+ return md_parse_option (option_transform, arg);
+ case option_no_generics:
+ as_warn (_("--no-generics is deprecated; use --no-transform instead"));
+ return md_parse_option (option_no_transform, arg);
+ case option_relax:
+ as_warn (_("--relax is deprecated; use --transform instead"));
+ return md_parse_option (option_transform, arg);
+ case option_no_relax:
+ as_warn (_("--no-relax is deprecated; use --no-transform instead"));
+ return md_parse_option (option_no_transform, arg);
case option_longcalls:
directive_state[directive_longcalls] = TRUE;
return 1;
@@ -954,63 +1213,60 @@ md_parse_option (c, arg)
case option_no_text_section_literals:
use_literal_section = TRUE;
return 1;
+ case option_absolute_literals:
+ if (!absolute_literals_supported)
+ {
+ as_fatal (_("--absolute-literals option not supported in this Xtensa configuration"));
+ return 0;
+ }
+ directive_state[directive_absolute_literals] = TRUE;
+ return 1;
+ case option_no_absolute_literals:
+ directive_state[directive_absolute_literals] = FALSE;
+ return 1;
+
case option_workaround_a0_b_retw:
workaround_a0_b_retw = TRUE;
- software_a0_b_retw_interlock = TRUE;
return 1;
case option_no_workaround_a0_b_retw:
workaround_a0_b_retw = FALSE;
- software_a0_b_retw_interlock = FALSE;
return 1;
case option_workaround_b_j_loop_end:
workaround_b_j_loop_end = TRUE;
- software_avoid_b_j_loop_end = TRUE;
return 1;
case option_no_workaround_b_j_loop_end:
workaround_b_j_loop_end = FALSE;
- software_avoid_b_j_loop_end = FALSE;
return 1;
case option_workaround_short_loop:
workaround_short_loop = TRUE;
- software_avoid_short_loop = TRUE;
return 1;
case option_no_workaround_short_loop:
workaround_short_loop = FALSE;
- software_avoid_short_loop = FALSE;
return 1;
case option_workaround_all_short_loops:
workaround_all_short_loops = TRUE;
- software_avoid_all_short_loops = TRUE;
return 1;
case option_no_workaround_all_short_loops:
workaround_all_short_loops = FALSE;
- software_avoid_all_short_loops = FALSE;
return 1;
case option_workaround_close_loop_end:
workaround_close_loop_end = TRUE;
- software_avoid_close_loop_end = TRUE;
return 1;
case option_no_workaround_close_loop_end:
workaround_close_loop_end = FALSE;
- software_avoid_close_loop_end = FALSE;
return 1;
case option_no_workarounds:
workaround_a0_b_retw = FALSE;
- software_a0_b_retw_interlock = FALSE;
workaround_b_j_loop_end = FALSE;
- software_avoid_b_j_loop_end = FALSE;
workaround_short_loop = FALSE;
- software_avoid_short_loop = FALSE;
workaround_all_short_loops = FALSE;
- software_avoid_all_short_loops = FALSE;
workaround_close_loop_end = FALSE;
- software_avoid_close_loop_end = FALSE;
return 1;
-
+
case option_align_targets:
align_targets = TRUE;
return 1;
@@ -1018,38 +1274,11 @@ md_parse_option (c, arg)
align_targets = FALSE;
return 1;
- case option_align_only_targets:
- align_only_targets = TRUE;
- return 1;
- case option_no_align_only_targets:
- align_only_targets = FALSE;
+ case option_warn_unaligned_targets:
+ warn_unaligned_branch_targets = TRUE;
return 1;
#ifdef XTENSA_SECTION_RENAME
- case option_literal_section_name:
- add_section_rename (".literal", arg);
- as_warn (_("'--literal-section-name' is deprecated; "
- "use '--rename-section .literal=NEWNAME'"));
- 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;
@@ -1060,6 +1289,56 @@ md_parse_option (c, arg)
should be emitted or not. FIXME: Not implemented. */
return 1;
+ case option_prefer_l32r:
+ if (prefer_const16)
+ as_fatal (_("prefer-l32r conflicts with prefer-const16"));
+ prefer_l32r = 1;
+ return 1;
+
+ case option_prefer_const16:
+ if (prefer_l32r)
+ as_fatal (_("prefer-const16 conflicts with prefer-l32r"));
+ prefer_const16 = 1;
+ return 1;
+
+ case option_target_hardware:
+ {
+ int earliest, latest = 0;
+ if (*arg == 0 || *arg == '-')
+ as_fatal (_("invalid target hardware version"));
+
+ earliest = strtol (arg, &arg, 0);
+
+ if (*arg == 0)
+ latest = earliest;
+ else if (*arg == '-')
+ {
+ if (*++arg == 0)
+ as_fatal (_("invalid target hardware version"));
+ latest = strtol (arg, &arg, 0);
+ }
+ if (*arg != 0)
+ as_fatal (_("invalid target hardware version"));
+
+ xtensa_setup_hw_workarounds (earliest, latest);
+ return 1;
+ }
+
+ case option_transform:
+ /* This option has no affect other than to use the defaults,
+ which are already set. */
+ return 1;
+
+ case option_no_transform:
+ /* This option turns off all transformations of any kind.
+ However, because we want to preserve the state of other
+ directives, we only change its own field. Thus, before
+ you perform any transformation, always check if transform
+ is available. If you use the functions we provide for this
+ purpose, you will be ok. */
+ directive_state[directive_transform] = FALSE;
+ return 1;
+
default:
return 0;
}
@@ -1070,27 +1349,42 @@ 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"
+ fputs ("\n\
+Xtensa options:\n\
+--[no-]text-section-literals\n\
+ [Do not] put literals in the text section\n\
+--[no-]absolute-literals\n\
+ [Do not] default to use non-PC-relative literals\n\
+--[no-]target-align [Do not] try to align branch targets\n\
+--[no-]longcalls [Do not] emit 32-bit call sequences\n\
+--[no-]transform [Do not] transform instructions\n"
#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
+"--rename-section old=new(:old1=new1)*\n\
+ Rename section 'old' to 'new'\n"
+#endif /* XTENSA_SECTION_RENAME */
, stream);
}
+
+static void
+xtensa_setup_hw_workarounds (earliest, latest)
+ int earliest;
+ int latest;
+{
+ if (earliest > latest)
+ as_fatal (_("illegal range of target hardware versions"));
+
+ /* Enable all workarounds for pre-T1050.0 hardware. */
+ if (earliest < 105000 || latest < 105000)
+ {
+ workaround_a0_b_retw |= TRUE;
+ workaround_b_j_loop_end |= TRUE;
+ workaround_short_loop |= TRUE;
+ workaround_close_loop_end |= TRUE;
+ workaround_all_short_loops |= TRUE;
+ }
+}
+
/* Directive data and functions. */
@@ -1109,42 +1403,48 @@ 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},
+ { "align", s_align_bytes, 0 }, /* Defaulting is invalid (0). */
+ { "literal_position", xtensa_literal_position, 0 },
+ { "frame", s_ignore, 0 }, /* Formerly used for STABS debugging. */
+ { "long", xtensa_elf_cons, 4 },
+ { "word", xtensa_elf_cons, 4 },
+ { "short", xtensa_elf_cons, 2 },
+ { "begin", xtensa_begin_directive, 0 },
+ { "end", xtensa_end_directive, 0 },
+ { "loc", xtensa_dwarf2_directive_loc, 0 },
+ { "literal", xtensa_literal_pseudo, 0 },
+ { "frequency", xtensa_frequency_pseudo, 0 },
+ { NULL, 0, 0 },
};
bfd_boolean
-use_generics ()
+use_transform ()
{
- return directive_state[directive_generics];
+ /* After md_end, you should be checking frag by frag, rather
+ than state directives. */
+ assert (!past_xtensa_end);
+ return directive_state[directive_transform];
}
bfd_boolean
use_longcalls ()
{
- return directive_state[directive_longcalls];
-}
-
-
-bfd_boolean
-code_density_available ()
-{
- return directive_state[directive_density];
+ /* After md_end, you should be checking frag by frag, rather
+ than state directives. */
+ assert (!past_xtensa_end);
+ return directive_state[directive_longcalls] && use_transform ();
}
bfd_boolean
-can_relax ()
+do_align_targets ()
{
- return use_generics ();
+ /* After md_end, you should be checking frag by frag, rather
+ than state directives. */
+ assert (!past_xtensa_end);
+ return align_targets && use_transform ();
}
@@ -1238,6 +1538,7 @@ get_directive (directive, negated)
{
int len;
unsigned i;
+ char *directive_string;
if (strncmp (input_line_pointer, "no-", 3) != 0)
*negated = FALSE;
@@ -1248,16 +1549,33 @@ get_directive (directive, negated)
}
len = strspn (input_line_pointer,
- "abcdefghijklmnopqrstuvwxyz_/0123456789.");
+ "abcdefghijklmnopqrstuvwxyz_-/0123456789.");
+
+ /* This code is a hack to make .begin [no-][generics|relax] exactly
+ equivalent to .begin [no-]transform. We should remove it when
+ we stop accepting those options. */
+
+ if (strncmp (input_line_pointer, "generics", strlen ("generics")) == 0)
+ {
+ as_warn (_("[no-]generics is deprecated; use [no-]transform instead"));
+ directive_string = "transform";
+ }
+ else if (strncmp (input_line_pointer, "relax", strlen ("relax")) == 0)
+ {
+ as_warn (_("[no-]relax is deprecated; use [no-]transform instead"));
+ directive_string = "transform";
+ }
+ else
+ directive_string = input_line_pointer;
for (i = 0; i < sizeof (directive_info) / sizeof (*directive_info); ++i)
{
- if (strncmp (input_line_pointer, directive_info[i].name, len) == 0)
+ if (strncmp (directive_string, directive_info[i].name, len) == 0)
{
input_line_pointer += len;
*directive = (directiveE) i;
if (*negated && !directive_info[i].can_be_negated)
- as_bad (_("directive %s can't be negated"),
+ as_bad (_("directive %s cannot be negated"),
directive_info[i].name);
return;
}
@@ -1278,8 +1596,6 @@ xtensa_begin_directive (ignore)
int len;
lit_state *ls;
- md_flush_pending_output ();
-
get_directive (&directive, &negated);
if (directive == (directiveE) XTENSA_UNDEFINED)
{
@@ -1287,6 +1603,9 @@ xtensa_begin_directive (ignore)
return;
}
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
switch (directive)
{
case directive_literal:
@@ -1297,15 +1616,19 @@ xtensa_begin_directive (ignore)
saved_insn_labels = insn_labels;
insn_labels = NULL;
}
+ as_warn (_(".begin literal is deprecated; use .literal instead"));
state = (emit_state *) xmalloc (sizeof (emit_state));
xtensa_switch_to_literal_fragment (state);
directive_push (directive_literal, negated, state);
break;
case directive_literal_prefix:
+ /* Have to flush pending output because a movi relaxed to an l32r
+ might produce a literal. */
+ md_flush_pending_output ();
/* Check to see if the current fragment is a literal
fragment. If it is, then this operation is not allowed. */
- if (frag_now->tc_frag_data.is_literal)
+ if (generating_literals)
{
as_bad (_("cannot set literal_prefix inside literal fragment"));
return;
@@ -1341,15 +1664,32 @@ xtensa_begin_directive (ignore)
directive_push (directive_freeregs, negated, 0);
break;
+ case directive_schedule:
+ md_flush_pending_output ();
+ frag_var (rs_fill, 0, 0, frag_now->fr_subtype,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ directive_push (directive_schedule, negated, 0);
+ xtensa_set_frag_assembly_state (frag_now);
+ break;
+
case directive_density:
- if (!density_supported && !negated)
+ as_warn (_(".begin [no-]density is ignored"));
+ break;
+
+ case directive_absolute_literals:
+ md_flush_pending_output ();
+ if (!absolute_literals_supported && !negated)
{
- as_warn (_("Xtensa density option not supported; ignored"));
+ as_warn (_("Xtensa absolute literals option not supported; ignored"));
break;
}
- /* fall through */
+ xtensa_set_frag_assembly_state (frag_now);
+ directive_push (directive, negated, 0);
+ break;
default:
+ md_flush_pending_output ();
+ xtensa_set_frag_assembly_state (frag_now);
directive_push (directive, negated, 0);
break;
}
@@ -1367,26 +1707,43 @@ xtensa_end_directive (ignore)
const char *file;
unsigned int line;
emit_state *state;
+ emit_state **state_ptr;
lit_state *s;
- md_flush_pending_output ();
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
get_directive (&end_directive, &end_negated);
- if (end_directive == (directiveE) XTENSA_UNDEFINED)
+
+ md_flush_pending_output ();
+
+ switch (end_directive)
{
+ case (directiveE) XTENSA_UNDEFINED:
discard_rest_of_line ();
return;
- }
- if (end_directive == directive_density && !density_supported && !end_negated)
- {
- as_warn (_("Xtensa density option not supported; ignored"));
+ case directive_density:
+ as_warn (_(".end [no-]density is ignored"));
demand_empty_rest_of_line ();
- return;
+ break;
+
+ case directive_absolute_literals:
+ if (!absolute_literals_supported && !end_negated)
+ {
+ as_warn (_("Xtensa absolute literals option not supported; ignored"));
+ demand_empty_rest_of_line ();
+ return;
+ }
+ break;
+
+ default:
+ break;
}
+ state_ptr = &state; /* use state_ptr to avoid type-punning warning */
directive_pop (&begin_directive, &begin_negated, &file, &line,
- (const void **) &state);
+ (const void **) state_ptr);
if (begin_directive != directive_none)
{
@@ -1403,6 +1760,7 @@ xtensa_end_directive (ignore)
case directive_literal:
frag_var (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_restore_emit_state (state);
+ xtensa_set_frag_assembly_state (frag_now);
free (state);
if (!inside_directive (directive_literal))
{
@@ -1412,9 +1770,6 @@ xtensa_end_directive (ignore)
}
break;
- case directive_freeregs:
- break;
-
case directive_literal_prefix:
/* Restore the default collection sections from saved state. */
s = (lit_state *) state;
@@ -1427,7 +1782,12 @@ xtensa_end_directive (ignore)
free (s);
break;
+ case directive_schedule:
+ case directive_freeregs:
+ break;
+
default:
+ xtensa_set_frag_assembly_state (frag_now);
break;
}
}
@@ -1437,23 +1797,49 @@ xtensa_end_directive (ignore)
}
+/* Wrap dwarf2 functions so that we correctly support the .loc directive. */
+
+static bfd_boolean xtensa_loc_directive_seen = FALSE;
+
+static void
+xtensa_dwarf2_directive_loc (x)
+ int x;
+{
+ xtensa_loc_directive_seen = TRUE;
+ dwarf2_directive_loc (x);
+}
+
+
+static void
+xtensa_dwarf2_emit_insn (size, loc)
+ int size;
+ struct dwarf2_line_info *loc;
+{
+ if (debug_type != DEBUG_DWARF2 && ! xtensa_loc_directive_seen)
+ return;
+ xtensa_loc_directive_seen = FALSE;
+ dwarf2_gen_line_info (frag_now_fix () - size, loc);
+}
+
+
/* Place an aligned literal fragment at the current location. */
static void
xtensa_literal_position (ignore)
int ignore ATTRIBUTE_UNUSED;
{
+ md_flush_pending_output ();
+
if (inside_directive (directive_literal))
as_warn (_(".literal_position inside literal directive; ignoring"));
- else if (!use_literal_section)
- xtensa_mark_literal_pool_location ();
+ xtensa_mark_literal_pool_location ();
demand_empty_rest_of_line ();
xtensa_clear_insn_labels ();
}
-/* Support .literal label, value@plt + offset. */
+/* Support .literal label, expr, ... */
static void
xtensa_literal_pseudo (ignored)
@@ -1462,7 +1848,6 @@ xtensa_literal_pseudo (ignored)
emit_state state;
char *p, *base_name;
char c;
- expressionS expP;
segT dest_seg;
if (inside_directive (directive_literal))
@@ -1472,6 +1857,8 @@ xtensa_literal_pseudo (ignored)
return;
}
+ md_flush_pending_output ();
+
/* Previous labels go with whatever follows this directive, not with
the literal, so save them now. */
saved_insn_labels = insn_labels;
@@ -1484,14 +1871,14 @@ xtensa_literal_pseudo (ignored)
xtensa_switch_to_literal_fragment (&state);
- /* ...but if we aren't using text-section-literals, then we
+ /* ...but if we aren't using text-section-literals, then we
need to put them in the section we just switched to. */
- if (use_literal_section)
+ if (use_literal_section || directive_state[directive_absolute_literals])
dest_seg = now_seg;
- /* All literals are aligned to four-byte boundaries
- which is handled by switch to literal fragment. */
- /* frag_align (2, 0, 0); */
+ /* All literals are aligned to four-byte boundaries. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
c = get_symbol_end ();
/* Just after name is now '\0'. */
@@ -1511,20 +1898,10 @@ xtensa_literal_pseudo (ignored)
colon (base_name);
- do
- {
- input_line_pointer++; /* skip ',' or ':' */
-
- expr (0, &expP);
-
- /* We only support 4-byte literals with .literal. */
- emit_expr (&expP, 4);
- }
- while (*input_line_pointer == ',');
-
*p = c;
+ input_line_pointer++; /* skip ',' or ':' */
- demand_empty_rest_of_line ();
+ xtensa_elf_cons (4);
xtensa_restore_emit_state (&state);
@@ -1539,17 +1916,9 @@ 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;
+ char *name, *linkonce_suffix;
+ char *newname, *newname4;
+ size_t linkonce_len;
/* Get a null-terminated copy of the name. */
name = xmalloc (len + 1);
@@ -1560,24 +1929,217 @@ xtensa_literal_prefix (start, len)
/* Allocate the sections (interesting note: the memory pointing to
the name is actually used for the name by the new section). */
+
newname = xmalloc (len + strlen (".literal") + 1);
- strcpy (newname, name);
- strcpy (newname + len, ".literal");
+ newname4 = xmalloc (len + strlen (".lit4") + 1);
+
+ linkonce_len = sizeof (".gnu.linkonce.") - 1;
+ if (strncmp (name, ".gnu.linkonce.", linkonce_len) == 0
+ && (linkonce_suffix = strchr (name + linkonce_len, '.')) != 0)
+ {
+ strcpy (newname, ".gnu.linkonce.literal");
+ strcpy (newname4, ".gnu.linkonce.lit4");
+
+ strcat (newname, linkonce_suffix);
+ strcat (newname4, linkonce_suffix);
+ }
+ else
+ {
+ int suffix_pos = len;
+
+ /* If the section name ends with ".text", then replace that suffix
+ instead of appending an additional suffix. */
+ if (len >= 5 && strcmp (name + len - 5, ".text") == 0)
+ suffix_pos -= 5;
+
+ strcpy (newname, name);
+ strcpy (newname4, name);
- /* Note that retrieve_literal_seg does not create a segment if
+ strcpy (newname + suffix_pos, ".literal");
+ strcpy (newname4 + suffix_pos, ".lit4");
+ }
+
+ /* Note that retrieve_literal_seg does not create a segment if
it already exists. */
- default_lit_sections.lit_seg = NULL; /* retrieved on demand */
+ default_lit_sections.lit_seg = NULL;
+ default_lit_sections.lit4_seg = NULL;
/* Canonicalizing section names allows renaming literal
sections to occur correctly. */
- default_lit_sections.lit_seg_name =
- tc_canonicalize_symbol_name (newname);
+ default_lit_sections.lit_seg_name = tc_canonicalize_symbol_name (newname);
+ default_lit_sections.lit4_seg_name = tc_canonicalize_symbol_name (newname4);
free (name);
+}
- /* Restore the current section and subsection and set the
- generation into the old segment. */
- subseg_set (s_now, ss_now);
+
+/* Support ".frequency branch_target_frequency fall_through_frequency". */
+
+static void
+xtensa_frequency_pseudo (ignored)
+ int ignored ATTRIBUTE_UNUSED;
+{
+ float fall_through_f, target_f;
+ subseg_map *seginfo;
+
+ fall_through_f = (float) strtod (input_line_pointer, &input_line_pointer);
+ if (fall_through_f < 0)
+ {
+ as_bad (_("fall through frequency must be greater than 0"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ target_f = (float) strtod (input_line_pointer, &input_line_pointer);
+ if (target_f < 0)
+ {
+ as_bad (_("branch target frequency must be greater than 0"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ seginfo = get_subseg_info (now_seg, now_subseg);
+ seginfo->cur_target_freq = target_f;
+ seginfo->cur_total_freq = target_f + fall_through_f;
+
+ demand_empty_rest_of_line ();
+}
+
+
+/* Like normal .long/.short/.word, except support @plt, etc.
+ Clobbers input_line_pointer, checks end-of-line. */
+
+static void
+xtensa_elf_cons (nbytes)
+ int nbytes;
+{
+ expressionS exp;
+ bfd_reloc_code_real_type reloc;
+
+ md_flush_pending_output ();
+
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("directives are not valid inside bundles"));
+
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol
+ && *input_line_pointer == '@'
+ && ((reloc = xtensa_elf_suffix (&input_line_pointer, &exp))
+ != BFD_RELOC_NONE))
+ {
+ reloc_howto_type *reloc_howto =
+ bfd_reloc_type_lookup (stdoutput, reloc);
+
+ if (reloc == BFD_RELOC_UNUSED || !reloc_howto)
+ as_bad (_("unsupported relocation"));
+ else if ((reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
+ || (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT))
+ as_bad (_("opcode-specific %s relocation used outside "
+ "an instruction"), reloc_howto->name);
+ else if (nbytes != (int) bfd_get_reloc_size (reloc_howto))
+ as_bad (_("%s relocations do not fit in %d bytes"),
+ reloc_howto->name, nbytes);
+ else
+ {
+ char *p = frag_more ((int) nbytes);
+ xtensa_set_frag_assembly_state (frag_now);
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ nbytes, &exp, 0, reloc);
+ }
+ }
+ else
+ emit_expr (&exp, (unsigned int) nbytes);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--; /* Put terminator back into stream. */
+ demand_empty_rest_of_line ();
+}
+
+
+/* Parse @plt, etc. and return the desired relocation. */
+static bfd_reloc_code_real_type
+xtensa_elf_suffix (str_p, exp_p)
+ char **str_p;
+ expressionS *exp_p;
+{
+ struct map_bfd
+ {
+ char *string;
+ int length;
+ bfd_reloc_code_real_type reloc;
+ };
+
+ char ident[20];
+ char *str = *str_p;
+ char *str2;
+ int ch;
+ int len;
+ struct map_bfd *ptr;
+
+#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
+
+ static struct map_bfd mapping[] =
+ {
+ MAP ("l", BFD_RELOC_LO16),
+ MAP ("h", BFD_RELOC_HI16),
+ MAP ("plt", BFD_RELOC_XTENSA_PLT),
+ { (char *) 0, 0, BFD_RELOC_UNUSED }
+ };
+
+ if (*str++ != '@')
+ return BFD_RELOC_NONE;
+
+ for (ch = *str, str2 = ident;
+ (str2 < ident + sizeof (ident) - 1
+ && (ISALNUM (ch) || ch == '@'));
+ ch = *++str)
+ {
+ *str2++ = (ISLOWER (ch)) ? ch : TOLOWER (ch);
+ }
+
+ *str2 = '\0';
+ len = str2 - ident;
+
+ ch = ident[0];
+ for (ptr = &mapping[0]; ptr->length > 0; ptr++)
+ if (ch == ptr->string[0]
+ && len == ptr->length
+ && memcmp (ident, ptr->string, ptr->length) == 0)
+ {
+ /* Now check for "identifier@suffix+constant". */
+ if (*str == '-' || *str == '+')
+ {
+ char *orig_line = input_line_pointer;
+ expressionS new_exp;
+
+ input_line_pointer = str;
+ expression (&new_exp);
+ if (new_exp.X_op == O_constant)
+ {
+ exp_p->X_add_number += new_exp.X_add_number;
+ str = input_line_pointer;
+ }
+
+ if (&input_line_pointer != str_p)
+ input_line_pointer = orig_line;
+ }
+
+ *str_p = str;
+ return ptr->reloc;
+ }
+
+ return BFD_RELOC_UNUSED;
}
@@ -1591,9 +2153,11 @@ expression_end (name)
{
switch (*name)
{
+ case '}':
case ';':
case '\0':
case ',':
+ case ':':
return name;
case ' ':
case '\t':
@@ -1664,45 +2228,67 @@ tc_get_register (prefix)
}
-#define PLT_SUFFIX "@PLT"
-#define plt_suffix "@plt"
-
static void
-expression_maybe_register (opnd, tok)
- xtensa_operand opnd;
+expression_maybe_register (opc, opnd, tok)
+ xtensa_opcode opc;
+ int opnd;
expressionS *tok;
{
- char *kind = xtensa_operand_kind (opnd);
+ xtensa_isa isa = xtensa_default_isa;
- if ((strlen (kind) == 1)
- && (*kind == 'l' || *kind == 'L' || *kind == 'i' || *kind == 'r'))
+ /* Check if this is an immediate operand. */
+ if (xtensa_operand_is_register (isa, opc, opnd) == 0)
{
+ bfd_reloc_code_real_type reloc;
segT t = expression (tok);
- if (t == absolute_section && operand_is_pcrel_label (opnd))
+ if (t == absolute_section
+ && xtensa_operand_is_PCrelative (isa, opc, opnd) == 1)
{
assert (tok->X_op == O_constant);
tok->X_op = O_symbol;
tok->X_add_symbol = &abs_symbol;
}
- if (tok->X_op == O_symbol
- && (!strncmp (input_line_pointer, PLT_SUFFIX,
- strlen (PLT_SUFFIX) - 1)
- || !strncmp (input_line_pointer, plt_suffix,
- strlen (plt_suffix) - 1)))
+
+ if ((tok->X_op == O_constant || tok->X_op == O_symbol)
+ && (reloc = xtensa_elf_suffix (&input_line_pointer, tok))
+ && (reloc != BFD_RELOC_NONE))
{
- symbol_get_tc (tok->X_add_symbol)->plt = 1;
- input_line_pointer += strlen (plt_suffix);
+ switch (reloc)
+ {
+ default:
+ case BFD_RELOC_UNUSED:
+ as_bad (_("unsupported relocation"));
+ break;
+
+ case BFD_RELOC_XTENSA_PLT:
+ tok->X_op = O_pltrel;
+ break;
+
+ case BFD_RELOC_LO16:
+ if (tok->X_op == O_constant)
+ tok->X_add_number &= 0xffff;
+ else
+ tok->X_op = O_lo16;
+ break;
+
+ case BFD_RELOC_HI16:
+ if (tok->X_op == O_constant)
+ tok->X_add_number = ((unsigned) tok->X_add_number) >> 16;
+ else
+ tok->X_op = O_hi16;
+ break;
+ }
}
}
else
{
- unsigned reg = tc_get_register (kind);
+ xtensa_regfile opnd_rf = xtensa_operand_regfile (isa, opc, opnd);
+ unsigned reg = tc_get_register (xtensa_regfile_shortname (isa, opnd_rf));
if (reg != ERROR_REG_NUM) /* Already errored */
{
uint32 buf = reg;
- if ((xtensa_operand_encode (opnd, &buf) != xtensa_encode_result_ok)
- || (reg != xtensa_operand_decode (opnd, buf)))
+ if (xtensa_operand_encode (isa, opc, opnd, &buf))
as_bad (_("register number out of range"));
}
@@ -1723,11 +2309,12 @@ tokenize_arguments (args, str)
char *old_input_line_pointer;
bfd_boolean saw_comma = FALSE;
bfd_boolean saw_arg = FALSE;
+ bfd_boolean saw_colon = FALSE;
int num_args = 0;
char *arg_end, *arg;
int arg_len;
-
- /* Save and restore input_line_pointer around this function. */
+
+ /* Save and restore input_line_pointer around this function. */
old_input_line_pointer = input_line_pointer;
input_line_pointer = str;
@@ -1737,51 +2324,70 @@ tokenize_arguments (args, str)
switch (*input_line_pointer)
{
case '\0':
+ case '}':
goto fini;
+ case ':':
+ input_line_pointer++;
+ if (saw_comma || saw_colon || !saw_arg)
+ goto err;
+ saw_colon = TRUE;
+ break;
+
case ',':
input_line_pointer++;
- if (saw_comma || !saw_arg)
+ if (saw_comma || saw_colon || !saw_arg)
goto err;
saw_comma = TRUE;
break;
default:
- if (!saw_comma && saw_arg)
+ if (!saw_comma && !saw_colon && saw_arg)
goto err;
arg_end = input_line_pointer + 1;
while (!expression_end (arg_end))
arg_end += 1;
-
+
arg_len = arg_end - input_line_pointer;
- arg = (char *) xmalloc (arg_len + 1);
+ arg = (char *) xmalloc ((saw_colon ? 1 : 0) + arg_len + 1);
args[num_args] = arg;
+ if (saw_colon)
+ *arg++ = ':';
strncpy (arg, input_line_pointer, arg_len);
arg[arg_len] = '\0';
-
+
input_line_pointer = arg_end;
num_args += 1;
saw_comma = FALSE;
+ saw_colon = FALSE;
saw_arg = TRUE;
break;
}
}
fini:
- if (saw_comma)
+ if (saw_comma || saw_colon)
goto err;
input_line_pointer = old_input_line_pointer;
return num_args;
err:
+ if (saw_comma)
+ as_bad (_("extra comma"));
+ else if (saw_colon)
+ as_bad (_("extra colon"));
+ else if (!saw_arg)
+ as_bad (_("missing argument"));
+ else
+ as_bad (_("missing comma or colon"));
input_line_pointer = old_input_line_pointer;
return -1;
}
-/* Parse the arguments to an opcode. Return true on error. */
+/* Parse the arguments to an opcode. Return TRUE on error. */
static bfd_boolean
parse_arguments (insn, num_args, arg_strings)
@@ -1789,55 +2395,170 @@ parse_arguments (insn, num_args, arg_strings)
int num_args;
char **arg_strings;
{
- expressionS *tok = insn->tok;
+ expressionS *tok, *last_tok;
xtensa_opcode opcode = insn->opcode;
bfd_boolean had_error = TRUE;
- xtensa_isa isa = xtensa_default_isa;
- int n;
+ xtensa_isa isa = xtensa_default_isa;
+ int n, num_regs = 0;
int opcode_operand_count;
- int actual_operand_count = 0;
- xtensa_operand opnd = NULL;
+ int opnd_cnt, last_opnd_cnt;
+ unsigned int next_reg = 0;
char *old_input_line_pointer;
if (insn->insn_type == ITYPE_LITERAL)
opcode_operand_count = 1;
else
- opcode_operand_count = xtensa_num_operands (isa, opcode);
+ opcode_operand_count = xtensa_opcode_num_operands (isa, opcode);
+ tok = insn->tok;
memset (tok, 0, sizeof (*tok) * MAX_INSN_ARGS);
/* Save and restore input_line_pointer around this function. */
- old_input_line_pointer = input_line_pointer;
+ old_input_line_pointer = input_line_pointer;
+
+ last_tok = 0;
+ last_opnd_cnt = -1;
+ opnd_cnt = 0;
+
+ /* Skip invisible operands. */
+ while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0)
+ {
+ opnd_cnt += 1;
+ tok++;
+ }
for (n = 0; n < num_args; n++)
- {
+ {
input_line_pointer = arg_strings[n];
+ if (*input_line_pointer == ':')
+ {
+ xtensa_regfile opnd_rf;
+ input_line_pointer++;
+ if (num_regs == 0)
+ goto err;
+ assert (opnd_cnt > 0);
+ num_regs--;
+ opnd_rf = xtensa_operand_regfile (isa, opcode, last_opnd_cnt);
+ if (next_reg
+ != tc_get_register (xtensa_regfile_shortname (isa, opnd_rf)))
+ as_warn (_("incorrect register number, ignoring"));
+ next_reg++;
+ }
+ else
+ {
+ if (opnd_cnt >= opcode_operand_count)
+ {
+ as_warn (_("too many arguments"));
+ goto err;
+ }
+ assert (opnd_cnt < MAX_INSN_ARGS);
- if (actual_operand_count >= opcode_operand_count)
- {
- as_warn (_("too many arguments"));
- goto err;
- }
- assert (actual_operand_count < MAX_INSN_ARGS);
+ expression_maybe_register (opcode, opnd_cnt, tok);
+ next_reg = tok->X_add_number + 1;
- 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;
+ if (xtensa_operand_is_register (isa, opcode, opnd_cnt) == 1)
+ {
+ num_regs = xtensa_operand_num_regs (isa, opcode, opnd_cnt) - 1;
+ /* minus 1 because we are seeing one right now */
+ }
+ else
+ num_regs = 0;
- if (tok->X_op == O_illegal || tok->X_op == O_absent)
- goto err;
- actual_operand_count++;
- tok++;
- }
+ last_tok = tok;
+ last_opnd_cnt = opnd_cnt;
+
+ do
+ {
+ opnd_cnt += 1;
+ tok++;
+ }
+ while (xtensa_operand_is_visible (isa, opcode, opnd_cnt) == 0);
+ }
+ }
+
+ if (num_regs > 0 && ((int) next_reg != last_tok->X_add_number + 1))
+ goto err;
insn->ntok = tok - insn->tok;
had_error = FALSE;
err:
- input_line_pointer = old_input_line_pointer;
+ input_line_pointer = old_input_line_pointer;
return had_error;
}
+static int
+get_invisible_operands (insn)
+ TInsn *insn;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_format fmt;
+ xtensa_opcode opc = insn->opcode;
+ int slot, opnd, fmt_found;
+ unsigned val;
+
+ if (!slotbuf)
+ slotbuf = xtensa_insnbuf_alloc (isa);
+
+ /* Find format/slot where this can be encoded. */
+ fmt_found = 0;
+ slot = 0;
+ for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+ {
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opc) == 0)
+ {
+ fmt_found = 1;
+ break;
+ }
+ }
+ if (fmt_found) break;
+ }
+
+ if (!fmt_found)
+ {
+ as_bad (_("cannot encode opcode \"%s\""), xtensa_opcode_name (isa, opc));
+ return -1;
+ }
+
+ /* First encode all the visible operands
+ (to deal with shared field operands). */
+ for (opnd = 0; opnd < insn->ntok; opnd++)
+ {
+ if (xtensa_operand_is_visible (isa, opc, opnd) == 1
+ && (insn->tok[opnd].X_op == O_register
+ || insn->tok[opnd].X_op == O_constant))
+ {
+ val = insn->tok[opnd].X_add_number;
+ xtensa_operand_encode (isa, opc, opnd, &val);
+ xtensa_operand_set_field (isa, opc, opnd, fmt, slot, slotbuf, val);
+ }
+ }
+
+ /* Then pull out the values for the invisible ones. */
+ for (opnd = 0; opnd < insn->ntok; opnd++)
+ {
+ if (xtensa_operand_is_visible (isa, opc, opnd) == 0)
+ {
+ xtensa_operand_get_field (isa, opc, opnd, fmt, slot, slotbuf, &val);
+ xtensa_operand_decode (isa, opc, opnd, &val);
+ insn->tok[opnd].X_add_number = val;
+ if (xtensa_operand_is_register (isa, opc, opnd) == 1)
+ insn->tok[opnd].X_op = O_register;
+ else
+ insn->tok[opnd].X_op = O_constant;
+ }
+ }
+
+ return 0;
+}
+
+
static void
xg_reverse_shift_count (cnt_argp)
char **cnt_argp;
@@ -1893,13 +2614,13 @@ xg_replace_opname (popname, newop)
static int
xg_check_num_args (pnum_args, expected_num, opname, arg_strings)
int *pnum_args;
- int expected_num;
+ int expected_num;
char *opname;
char **arg_strings;
{
int num_args = *pnum_args;
- if (num_args < expected_num)
+ if (num_args < expected_num)
{
as_bad (_("not enough operands (%d) for '%s'; expected %d"),
num_args, opname, expected_num);
@@ -1923,14 +2644,20 @@ xg_check_num_args (pnum_args, expected_num, opname, arg_strings)
}
+/* If the register is not specified as part of the opcode,
+ then get it from the operand and move it to the opcode. */
+
static int
xg_translate_sysreg_op (popname, pnum_args, arg_strings)
char **popname;
int *pnum_args;
char **arg_strings;
{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_sysreg sr;
char *opname, *new_opname;
- offsetT val;
+ const char *sr_name;
+ int is_user, is_write;
bfd_boolean has_underbar = FALSE;
opname = *popname;
@@ -1939,40 +2666,148 @@ xg_translate_sysreg_op (popname, pnum_args, arg_strings)
has_underbar = TRUE;
opname += 1;
}
+ is_user = (opname[1] == 'u');
+ is_write = (opname[0] == 'w');
- /* Opname == [rw]ur... */
-
- if (opname[3] == '\0')
- {
- /* If the register is not specified as part of the opcode,
- then get it from the operand and move it to the opcode. */
+ /* Opname == [rw]ur or [rwx]sr... */
- if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
- return -1;
+ if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
+ return -1;
+ /* Check if the argument is a symbolic register name. */
+ sr = xtensa_sysreg_lookup_name (isa, arg_strings[1]);
+ /* Handle WSR to "INTSET" as a special case. */
+ if (sr == XTENSA_UNDEFINED && is_write && !is_user
+ && !strcasecmp (arg_strings[1], "intset"))
+ sr = xtensa_sysreg_lookup_name (isa, "interrupt");
+ if (sr == XTENSA_UNDEFINED
+ || (xtensa_sysreg_is_user (isa, sr) == 1) != is_user)
+ {
+ /* Maybe it's a register number.... */
+ offsetT val;
if (!xg_arg_is_constant (arg_strings[1], &val))
{
- as_bad (_("register number for `%s' is not a constant"), opname);
+ as_bad (_("invalid register '%s' for '%s' instruction"),
+ arg_strings[1], opname);
return -1;
}
- if ((unsigned) val > 255)
+ sr = xtensa_sysreg_lookup (isa, val, is_user);
+ if (sr == XTENSA_UNDEFINED)
{
- as_bad (_("register number (%ld) for `%s' is out of range"),
+ as_bad (_("invalid register number (%ld) for '%s' instruction"),
val, opname);
return -1;
}
+ }
- /* Remove the last argument, which is now part of the opcode. */
- free (arg_strings[1]);
- arg_strings[1] = 0;
- *pnum_args = 1;
+ /* Remove the last argument, which is now part of the opcode. */
+ free (arg_strings[1]);
+ arg_strings[1] = 0;
+ *pnum_args = 1;
+
+ /* Translate the opcode. */
+ sr_name = xtensa_sysreg_name (isa, sr);
+ /* Another special case for "WSR.INTSET".... */
+ if (is_write && !is_user && !strcasecmp ("interrupt", sr_name))
+ sr_name = "intset";
+ new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ sprintf (new_opname, "%s%s.%s", (has_underbar ? "_" : ""),
+ *popname, sr_name);
+ free (*popname);
+ *popname = new_opname;
+
+ return 0;
+}
+
+
+static int
+xtensa_translate_old_userreg_ops (popname)
+ char **popname;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_sysreg sr;
+ char *opname, *new_opname;
+ const char *sr_name;
+ bfd_boolean has_underbar = FALSE;
+
+ opname = *popname;
+ if (opname[0] == '_')
+ {
+ has_underbar = TRUE;
+ opname += 1;
+ }
+
+ sr = xtensa_sysreg_lookup_name (isa, opname + 1);
+ if (sr != XTENSA_UNDEFINED)
+ {
+ /* The new default name ("nnn") is different from the old default
+ name ("URnnn"). The old default is handled below, and we don't
+ want to recognize [RW]nnn, so do nothing if the name is the (new)
+ default. */
+ static char namebuf[10];
+ sprintf (namebuf, "%d", xtensa_sysreg_number (isa, sr));
+ if (strcmp (namebuf, opname + 1) == 0)
+ return 0;
+ }
+ else
+ {
+ offsetT val;
+ char *end;
+
+ /* Only continue if the reg name is "URnnn". */
+ if (opname[1] != 'u' || opname[2] != 'r')
+ return 0;
+ val = strtoul (opname + 3, &end, 10);
+ if (*end != '\0')
+ return 0;
+
+ sr = xtensa_sysreg_lookup (isa, val, 1);
+ if (sr == XTENSA_UNDEFINED)
+ {
+ as_bad (_("invalid register number (%ld) for '%s'"),
+ val, opname);
+ return -1;
+ }
+ }
+
+ /* Translate the opcode. */
+ sr_name = xtensa_sysreg_name (isa, sr);
+ new_opname = (char *) xmalloc (strlen (sr_name) + 6);
+ sprintf (new_opname, "%s%cur.%s", (has_underbar ? "_" : ""),
+ opname[0], sr_name);
+ free (*popname);
+ *popname = new_opname;
+
+ return 0;
+}
+
+
+static int
+xtensa_translate_zero_immed (old_op, new_op, popname, pnum_args, arg_strings)
+ char *old_op;
+ char *new_op;
+ char **popname;
+ int *pnum_args;
+ char **arg_strings;
+{
+ char *opname;
+ offsetT val;
+
+ opname = *popname;
+ assert (opname[0] != '_');
+
+ if (strcmp (opname, old_op) != 0)
+ return 0;
- /* 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;
+ if (xg_check_num_args (pnum_args, 3, opname, arg_strings))
+ return -1;
+ if (xg_arg_is_constant (arg_strings[1], &val) && val == 0)
+ {
+ xg_replace_opname (popname, new_op);
+ free (arg_strings[1]);
+ arg_strings[1] = arg_strings[2];
+ arg_strings[2] = 0;
+ *pnum_args = 2;
}
return 0;
@@ -1991,6 +2826,9 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
char *opname = *popname;
bfd_boolean has_underbar = FALSE;
+ if (cur_vinsn.inside_bundle)
+ return 0;
+
if (*opname == '_')
{
has_underbar = TRUE;
@@ -1999,7 +2837,7 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
if (strcmp (opname, "mov") == 0)
{
- if (!has_underbar && code_density_available ())
+ if (use_transform () && !has_underbar && density_supported)
xg_replace_opname (popname, "mov.n");
else
{
@@ -2033,9 +2871,10 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
return 0;
}
- if (strcmp (opname, "nop") == 0)
+ if (xtensa_nop_opcode == XTENSA_UNDEFINED
+ && strcmp (opname, "nop") == 0)
{
- if (!has_underbar && code_density_available ())
+ if (use_transform () && !has_underbar && density_supported)
xg_replace_opname (popname, "nop.n");
else
{
@@ -2053,80 +2892,39 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
return 0;
}
- if ((opname[0] == 'r' || opname[0] == 'w')
- && opname[1] == 'u'
- && opname[2] == 'r')
+ /* Recognize [RW]UR and [RWX]SR. */
+ if ((((opname[0] == 'r' || opname[0] == 'w')
+ && (opname[1] == 'u' || opname[1] == 's'))
+ || (opname[0] == 'x' && opname[1] == 's'))
+ && opname[2] == 'r'
+ && opname[3] == '\0')
return xg_translate_sysreg_op (popname, pnum_args, arg_strings);
+ /* Backward compatibility for RUR and WUR: Recognize [RW]UR<nnn> and
+ [RW]<name> if <name> is the non-default name of a user register. */
+ if ((opname[0] == 'r' || opname[0] == 'w')
+ && xtensa_opcode_lookup (xtensa_default_isa, opname) == XTENSA_UNDEFINED)
+ return xtensa_translate_old_userreg_ops (popname);
- /* WIDENING DENSITY OPCODES
-
- questionable relaxations (widening) from old "tai" idioms:
-
- ADD.N --> ADD
- BEQZ.N --> BEQZ
- RET.N --> RET
- RETW.N --> RETW
- MOVI.N --> MOVI
- MOV.N --> MOV
- NOP.N --> NOP
-
- Note: this incomplete list was imported to match the "tai"
- behavior; other density opcodes are not handled.
-
- The xtensa-relax code may know how to do these but it doesn't do
- anything when these density opcodes appear inside a no-density
- region. Somehow GAS should either print an error when that happens
- or do the widening. The old "tai" behavior was to do the widening.
- For now, I'll make it widen but print a warning.
-
- FIXME: GAS needs to detect density opcodes inside no-density
- regions and treat them as errors. This code should be removed
- when that is done. */
-
- if (use_generics ()
- && !has_underbar
- && density_supported
- && !code_density_available ())
+ /* Relax branches that don't allow comparisons against an immediate value
+ of zero to the corresponding branches with implicit zero immediates. */
+ if (!has_underbar && use_transform ())
{
- if (strcmp (opname, "add.n") == 0)
- xg_replace_opname (popname, "add");
-
- else if (strcmp (opname, "beqz.n") == 0)
- xg_replace_opname (popname, "beqz");
-
- else if (strcmp (opname, "ret.n") == 0)
- xg_replace_opname (popname, "ret");
-
- else if (strcmp (opname, "retw.n") == 0)
- xg_replace_opname (popname, "retw");
+ if (xtensa_translate_zero_immed ("bnei", "bnez", popname,
+ pnum_args, arg_strings))
+ return -1;
- else if (strcmp (opname, "movi.n") == 0)
- xg_replace_opname (popname, "movi");
+ if (xtensa_translate_zero_immed ("beqi", "beqz", popname,
+ pnum_args, arg_strings))
+ return -1;
- else if (strcmp (opname, "mov.n") == 0)
- {
- if (xg_check_num_args (pnum_args, 2, opname, arg_strings))
- return -1;
- xg_replace_opname (popname, "or");
- arg_strings[2] = (char *) xmalloc (strlen (arg_strings[1]) + 1);
- strcpy (arg_strings[2], arg_strings[1]);
- *pnum_args = 3;
- }
+ if (xtensa_translate_zero_immed ("bgei", "bgez", popname,
+ pnum_args, arg_strings))
+ return -1;
- else if (strcmp (opname, "nop.n") == 0)
- {
- if (xg_check_num_args (pnum_args, 0, opname, arg_strings))
- return -1;
- xg_replace_opname (popname, "or");
- arg_strings[0] = (char *) xmalloc (3);
- arg_strings[1] = (char *) xmalloc (3);
- arg_strings[2] = (char *) xmalloc (3);
- strcpy (arg_strings[0], "a1");
- strcpy (arg_strings[1], "a1");
- strcpy (arg_strings[2], "a1");
- *pnum_args = 3;
- }
+ if (xtensa_translate_zero_immed ("blti", "bltz", popname,
+ pnum_args, arg_strings))
+ return -1;
}
return 0;
@@ -2135,55 +2933,12 @@ xg_translate_idioms (popname, pnum_args, arg_strings)
/* Functions for dealing with the Xtensa ISA. */
-/* Return true if the given operand is an immed or target instruction,
- i.e., has a reloc associated with it. Currently, this is only true
- if the operand kind is "i, "l" or "L". */
-
-static bfd_boolean
-operand_is_immed (opnd)
- xtensa_operand opnd;
-{
- const char *opkind = xtensa_operand_kind (opnd);
- if (opkind[0] == '\0' || opkind[1] != '\0')
- return FALSE;
- switch (opkind[0])
- {
- case 'i':
- case 'l':
- case 'L':
- return TRUE;
- }
- return FALSE;
-}
-
-
-/* Return true if the given operand is a pc-relative label. This is
- true for "l", "L", and "r" operand kinds. */
-
-bfd_boolean
-operand_is_pcrel_label (opnd)
- xtensa_operand opnd;
-{
- const char *opkind = xtensa_operand_kind (opnd);
- if (opkind[0] == '\0' || opkind[1] != '\0')
- return FALSE;
- switch (opkind[0])
- {
- case 'r':
- case 'l':
- case 'L':
- return TRUE;
- }
- return FALSE;
-}
-
-
/* Currently the assembler only allows us to use a single target per
fragment. Because of this, only one operand for a given
- instruction may be symbolic. If there is an operand of kind "lrL",
+ instruction may be symbolic. If there is a PC-relative operand,
the last one is chosen. Otherwise, the result is the number of the
- last operand of type "i", and if there are none of those, we fail
- and return -1. */
+ last immediate operand, and if there are none of those, we fail and
+ return -1. */
int
get_relaxable_immed (opcode)
@@ -2191,72 +2946,155 @@ get_relaxable_immed (opcode)
{
int last_immed = -1;
int noperands, opi;
- xtensa_operand operand;
if (opcode == XTENSA_UNDEFINED)
return -1;
- noperands = xtensa_num_operands (xtensa_default_isa, opcode);
+ noperands = xtensa_opcode_num_operands (xtensa_default_isa, opcode);
for (opi = noperands - 1; opi >= 0; opi--)
{
- operand = xtensa_get_operand (xtensa_default_isa, opcode, opi);
- if (operand_is_pcrel_label (operand))
+ if (xtensa_operand_is_visible (xtensa_default_isa, opcode, opi) == 0)
+ continue;
+ if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, opi) == 1)
return opi;
- if (last_immed == -1 && operand_is_immed (operand))
+ if (last_immed == -1
+ && xtensa_operand_is_register (xtensa_default_isa, opcode, opi) == 0)
last_immed = opi;
}
return last_immed;
}
-xtensa_opcode
-get_opcode_from_buf (buf)
+static xtensa_opcode
+get_opcode_from_buf (buf, slot)
const char *buf;
+ int slot;
{
static xtensa_insnbuf insnbuf = NULL;
- xtensa_opcode opcode;
+ static xtensa_insnbuf slotbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt;
+
if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, buf, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return XTENSA_UNDEFINED;
+
+ if (slot >= xtensa_format_num_slots (isa, fmt))
+ return XTENSA_UNDEFINED;
- xtensa_insnbuf_from_chars (isa, insnbuf, buf);
- opcode = xtensa_decode_insn (isa, insnbuf);
- return opcode;
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ return xtensa_opcode_decode (isa, fmt, slot, slotbuf);
}
-static bfd_boolean
-is_direct_call_opcode (opcode)
- xtensa_opcode opcode;
+#ifdef TENSILICA_DEBUG
+
+/* For debugging, print out the mapping of opcode numbers to opcodes. */
+
+void
+xtensa_print_insn_table ()
{
- if (opcode == XTENSA_UNDEFINED)
- return FALSE;
+ int num_opcodes, num_operands;
+ xtensa_opcode opcode;
+ xtensa_isa isa = xtensa_default_isa;
- return (opcode == xtensa_call0_opcode
- || opcode == xtensa_call4_opcode
- || opcode == xtensa_call8_opcode
- || opcode == xtensa_call12_opcode);
+ num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+ for (opcode = 0; opcode < num_opcodes; opcode++)
+ {
+ int opn;
+ fprintf (stderr, "%d: %s: ", opcode, xtensa_opcode_name (isa, opcode));
+ num_operands = xtensa_opcode_num_operands (isa, opcode);
+ for (opn = 0; opn < num_operands; opn++)
+ {
+ if (xtensa_operand_is_visible (isa, opcode, opn) == 0)
+ continue;
+ if (xtensa_operand_is_register (isa, opcode, opn) == 1)
+ {
+ xtensa_regfile opnd_rf =
+ xtensa_operand_regfile (isa, opcode, opn);
+ fprintf (stderr, "%s ", xtensa_regfile_shortname (isa, opnd_rf));
+ }
+ else if (xtensa_operand_is_PCrelative (isa, opcode, opn) == 1)
+ fputs ("[lLr] ", stderr);
+ else
+ fputs ("i ", stderr);
+ }
+ fprintf (stderr, "\n");
+ }
+}
+
+
+static void
+print_vliw_insn (vbuf)
+ xtensa_insnbuf vbuf;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format f = xtensa_format_decode (isa, vbuf);
+ xtensa_insnbuf sbuf = xtensa_insnbuf_alloc (isa);
+ int op;
+
+ fprintf (stderr, "format = %d\n", f);
+
+ for (op = 0; op < xtensa_format_num_slots (isa, f); op++)
+ {
+ xtensa_opcode opcode;
+ const char *opname;
+ int operands;
+
+ xtensa_format_get_slot (isa, f, op, vbuf, sbuf);
+ opcode = xtensa_opcode_decode (isa, f, op, sbuf);
+ opname = xtensa_opcode_name (isa, opcode);
+
+ fprintf (stderr, "op in slot %i is %s;\n", op, opname);
+ fprintf (stderr, " operands = ");
+ for (operands = 0;
+ operands < xtensa_opcode_num_operands (isa, opcode);
+ operands++)
+ {
+ unsigned int val;
+ if (xtensa_operand_is_visible (isa, opcode, operands) == 0)
+ continue;
+ xtensa_operand_get_field (isa, opcode, operands, f, op, sbuf, &val);
+ xtensa_operand_decode (isa, opcode, operands, &val);
+ fprintf (stderr, "%d ", val);
+ }
+ fprintf (stderr, "\n");
+ }
+ xtensa_insnbuf_free (isa, sbuf);
}
+#endif /* TENSILICA_DEBUG */
+
static bfd_boolean
-is_call_opcode (opcode)
+is_direct_call_opcode (opcode)
xtensa_opcode opcode;
{
- if (is_direct_call_opcode (opcode))
- return TRUE;
+ xtensa_isa isa = xtensa_default_isa;
+ int n, num_operands;
- if (opcode == XTENSA_UNDEFINED)
+ if (xtensa_opcode_is_call (isa, opcode) == 0)
return FALSE;
- return (opcode == xtensa_callx0_opcode
- || opcode == xtensa_callx4_opcode
- || opcode == xtensa_callx8_opcode
- || opcode == xtensa_callx12_opcode);
+ num_operands = xtensa_opcode_num_operands (isa, opcode);
+ for (n = 0; n < num_operands; n++)
+ {
+ if (xtensa_operand_is_register (isa, opcode, n) == 0
+ && xtensa_operand_is_PCrelative (isa, opcode, n) == 1)
+ return TRUE;
+ }
+ return FALSE;
}
-/* Return true if the opcode is an entry opcode. This is used because
+/* 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. */
@@ -2271,20 +3109,18 @@ is_entry_opcode (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. */
+/* Return TRUE if the opcode is a movi or movi.n opcode. This is
+ so we can relax "movi aX, foo" in the front end. */
static bfd_boolean
-is_loop_opcode (opcode)
+is_movi_opcode (opcode)
xtensa_opcode opcode;
{
if (opcode == XTENSA_UNDEFINED)
return FALSE;
- return (opcode == xtensa_loop_opcode
- || opcode == xtensa_loopnez_opcode
- || opcode == xtensa_loopgtz_opcode);
+ return (opcode == xtensa_movi_opcode)
+ || (opcode == xtensa_movi_n_opcode);
}
@@ -2310,7 +3146,7 @@ is_jx_opcode (opcode)
}
-/* Return true if the opcode is a retw or retw.n.
+/* Return TRUE if the opcode is a retw or retw.n.
Needed to add nops to avoid a hardware interlock issue. */
static bfd_boolean
@@ -2324,198 +3160,109 @@ is_windowed_return_opcode (opcode)
}
-/* Return true if the opcode type is "l" and the opcode is NOT a jump. */
+/* Convert from BFD relocation type code to slot and operand number.
+ Returns non-zero on failure. */
-static bfd_boolean
-is_conditional_branch_opcode (opcode)
- xtensa_opcode opcode;
+static int
+decode_reloc (reloc, slot, is_alt)
+ bfd_reloc_code_real_type reloc;
+ int *slot;
+ bfd_boolean *is_alt;
{
- 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++)
+ if (reloc >= BFD_RELOC_XTENSA_SLOT0_OP
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_OP)
{
- xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
- if (strcmp (xtensa_operand_kind (operand), "l") == 0)
- return TRUE;
+ *slot = reloc - BFD_RELOC_XTENSA_SLOT0_OP;
+ *is_alt = FALSE;
}
- 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++)
+ else if (reloc >= BFD_RELOC_XTENSA_SLOT0_ALT
+ && reloc <= BFD_RELOC_XTENSA_SLOT14_ALT)
{
- xtensa_operand opnd =
- xtensa_get_operand (xtensa_default_isa, opcode, opn);
- const char *opkind = xtensa_operand_kind (opnd);
- if (opkind && opkind[0] == 'l' && opkind[1] == '\0')
- return TRUE;
+ *slot = reloc - BFD_RELOC_XTENSA_SLOT0_ALT;
+ *is_alt = TRUE;
}
- return FALSE;
+ else
+ return -1;
+
+ return 0;
}
-/* Convert from operand numbers to BFD relocation type code.
- Return BFD_RELOC_NONE on failure. */
+/* Convert from slot number to BFD relocation type code for the
+ standard PC-relative relocations. Return BFD_RELOC_NONE on
+ failure. */
-bfd_reloc_code_real_type
-opnum_to_reloc (opnum)
- int opnum;
+static bfd_reloc_code_real_type
+encode_reloc (slot)
+ int slot;
{
- switch (opnum)
- {
- case 0:
- return BFD_RELOC_XTENSA_OP0;
- case 1:
- return BFD_RELOC_XTENSA_OP1;
- case 2:
- return BFD_RELOC_XTENSA_OP2;
- default:
- break;
- }
- return BFD_RELOC_NONE;
+ if (slot < 0 || slot > 14)
+ return BFD_RELOC_NONE;
+
+ return BFD_RELOC_XTENSA_SLOT0_OP + slot;
}
-/* Convert from BFD relocation type code to operand number.
- Return -1 on failure. */
+/* Convert from slot numbers to BFD relocation type code for the
+ "alternate" relocations. Return BFD_RELOC_NONE on failure. */
-int
-reloc_to_opnum (reloc)
- bfd_reloc_code_real_type reloc;
+static bfd_reloc_code_real_type
+encode_alt_reloc (slot)
+ int slot;
{
- switch (reloc)
- {
- case BFD_RELOC_XTENSA_OP0:
- return 0;
- case BFD_RELOC_XTENSA_OP1:
- return 1;
- case BFD_RELOC_XTENSA_OP2:
- return 2;
- default:
- break;
- }
- return -1;
+ if (slot < 0 || slot > 14)
+ return BFD_RELOC_NONE;
+
+ return BFD_RELOC_XTENSA_SLOT0_ALT + slot;
}
static void
-xtensa_insnbuf_set_operand (insnbuf, opcode, operand, value, file, line)
- xtensa_insnbuf insnbuf;
+xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, operand, value,
+ file, line)
+ xtensa_insnbuf slotbuf;
+ xtensa_format fmt;
+ int slot;
xtensa_opcode opcode;
- xtensa_operand operand;
- int32 value;
+ int operand;
+ uint32 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)
+ if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
{
- case xtensa_encode_result_ok:
- break;
- case xtensa_encode_result_align:
- as_bad_where ((char *) file, line,
- _("operand %d not properly aligned for '%s'"),
- value, xtensa_opcode_name (xtensa_default_isa, opcode));
- break;
- case xtensa_encode_result_not_in_table:
- as_bad_where ((char *) file, line,
- _("operand %d not in immediate table for '%s'"),
- value, xtensa_opcode_name (xtensa_default_isa, opcode));
- break;
- case xtensa_encode_result_too_high:
- as_bad_where ((char *) file, line,
- _("operand %d too large for '%s'"), value,
- xtensa_opcode_name (xtensa_default_isa, opcode));
- break;
- case xtensa_encode_result_too_low:
- as_bad_where ((char *) file, line,
- _("operand %d too small for '%s'"), value,
- xtensa_opcode_name (xtensa_default_isa, opcode));
- break;
- case xtensa_encode_result_not_ok:
- as_bad_where ((char *) file, line,
- _("operand %d is invalid for '%s'"), value,
- xtensa_opcode_name (xtensa_default_isa, opcode));
- break;
- default:
- abort ();
+ if (xtensa_operand_is_PCrelative (xtensa_default_isa, opcode, operand)
+ == 1)
+ as_bad_where ((char *) file, line,
+ _("operand %u is out of range for '%s'"), value,
+ xtensa_opcode_name (xtensa_default_isa, opcode));
+ else
+ as_bad_where ((char *) file, line,
+ _("operand %u is invalid for '%s'"), value,
+ xtensa_opcode_name (xtensa_default_isa, opcode));
+ return;
}
- xtensa_operand_set_field (operand, insnbuf, valbuf);
+ xtensa_operand_set_field (xtensa_default_isa, opcode, operand, fmt, slot,
+ slotbuf, valbuf);
}
static uint32
-xtensa_insnbuf_get_operand (insnbuf, opcode, opnum)
- xtensa_insnbuf insnbuf;
+xtensa_insnbuf_get_operand (slotbuf, fmt, slot, opcode, opnum)
+ xtensa_insnbuf slotbuf;
+ xtensa_format fmt;
+ int slot;
xtensa_opcode opcode;
int opnum;
{
- xtensa_operand op = xtensa_get_operand (xtensa_default_isa, opcode, opnum);
- return xtensa_operand_decode (op, xtensa_operand_get_field (op, insnbuf));
-}
-
-
-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;
+ uint32 val = 0;
+ (void) xtensa_operand_get_field (xtensa_default_isa, opcode, opnum,
+ fmt, slot, slotbuf, &val);
+ (void) xtensa_operand_decode (xtensa_default_isa, opcode, opnum, &val);
+ return val;
}
@@ -2534,20 +3281,11 @@ is_unique_insn_expansion (r)
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);
+ return xg_get_single_size (insn->opcode);
}
@@ -2555,7 +3293,7 @@ bfd_boolean
xg_is_narrow_insn (insn)
TInsn *insn;
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int num_match = 0;
assert (insn->insn_type == ITYPE_INSN);
@@ -2571,7 +3309,7 @@ xg_is_narrow_insn (insn)
/* It only generates one instruction... */
assert (insn->insn_type == ITYPE_INSN);
/* ...and it is a larger instruction. */
- if (xg_get_insn_size (insn)
+ if (xg_get_single_size (insn->opcode)
< xg_get_build_instr_size (rule->to_instr))
{
num_match++;
@@ -2588,7 +3326,7 @@ bfd_boolean
xg_is_single_relaxable_insn (insn)
TInsn *insn;
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int num_match = 0;
assert (insn->insn_type == ITYPE_INSN);
@@ -2601,9 +3339,10 @@ xg_is_single_relaxable_insn (insn)
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)
+ if (xg_get_single_size (insn->opcode)
<= xg_get_build_instr_size (rule->to_instr))
{
num_match++;
@@ -2616,50 +3355,15 @@ xg_is_single_relaxable_insn (insn)
}
-/* 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 ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
- int max_size = xtensa_insn_length (xtensa_default_isa, opcode);
+ int max_size = xg_get_single_size (opcode);
assert (opcode < table->num_opcodes);
@@ -2683,9 +3387,7 @@ xg_get_max_insn_widen_size (opcode)
switch (build_list->typ)
{
case INSTR_INSTR:
- this_size += xtensa_insn_length (xtensa_default_isa,
- build_list->opcode);
-
+ this_size += xg_get_single_size (build_list->opcode);
break;
case INSTR_LITERAL_DEF:
case INSTR_LABEL_DEF:
@@ -2706,7 +3408,7 @@ int
xg_get_max_insn_widen_literal_size (opcode)
xtensa_opcode opcode;
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
int max_size = 0;
@@ -2732,7 +3434,7 @@ xg_get_max_insn_widen_literal_size (opcode)
switch (build_list->typ)
{
case INSTR_LITERAL_DEF:
- /* hard coded 4-byte literal. */
+ /* Hard-coded 4-byte literal. */
this_size += 4;
break;
case INSTR_INSTR:
@@ -2754,7 +3456,7 @@ xg_is_relaxable_insn (insn, lateral_steps)
int lateral_steps;
{
int steps_taken = 0;
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
@@ -2797,7 +3499,7 @@ get_special_label_symbol ()
}
-/* Return true on success. */
+/* Return TRUE on success. */
bfd_boolean
xg_build_to_insn (targ, insn, bi)
@@ -2809,6 +3511,7 @@ xg_build_to_insn (targ, insn, bi)
symbolS *sym;
memset (targ, 0, sizeof (TInsn));
+ targ->loc = insn->loc;
switch (bi->typ)
{
case INSTR_INSTR:
@@ -2844,6 +3547,38 @@ xg_build_to_insn (targ, insn, bi)
sym = get_special_label_symbol ();
set_expr_symbol_offset (&targ->tok[op_num], sym, 0);
break;
+ case OP_OPERAND_HI16U:
+ case OP_OPERAND_LOW16U:
+ assert (op_data < insn->ntok);
+ if (expr_is_const (&insn->tok[op_data]))
+ {
+ long val;
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ val = xg_apply_userdef_op_fn (op->typ,
+ targ->tok[op_num].
+ X_add_number);
+ targ->tok[op_num].X_add_number = val;
+ }
+ else
+ {
+ /* For const16 we can create relocations for these. */
+ if (targ->opcode == XTENSA_UNDEFINED
+ || (targ->opcode != xtensa_const16_opcode))
+ return FALSE;
+ assert (op_data < insn->ntok);
+ /* Need to build a O_lo16 or O_hi16. */
+ copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
+ if (targ->tok[op_num].X_op == O_symbol)
+ {
+ if (op->typ == OP_OPERAND_HI16U)
+ targ->tok[op_num].X_op = O_hi16;
+ else if (op->typ == OP_OPERAND_LOW16U)
+ targ->tok[op_num].X_op = O_lo16;
+ else
+ return FALSE;
+ }
+ }
+ break;
default:
/* currently handles:
OP_OPERAND_LOW8
@@ -2889,6 +3624,9 @@ xg_build_to_insn (targ, insn, bi)
{
case OP_OPERAND:
assert (op_data < insn->ntok);
+ /* We can only pass resolvable literals through. */
+ if (!xg_valid_literal_expression (&insn->tok[op_data]))
+ return FALSE;
copy_expr (&targ->tok[op_num], &insn->tok[op_data]);
break;
case OP_LITERAL:
@@ -2906,7 +3644,7 @@ xg_build_to_insn (targ, insn, bi)
targ->opcode = XTENSA_UNDEFINED;
targ->insn_type = ITYPE_LABEL;
targ->is_specific_opcode = FALSE;
- /* Literal with no ops. is a label? */
+ /* Literal with no ops is a label? */
assert (op == NULL);
break;
@@ -2918,7 +3656,7 @@ xg_build_to_insn (targ, insn, bi)
}
-/* Return true on success. */
+/* Return TRUE on success. */
bfd_boolean
xg_build_to_stack (istack, insn, bi)
@@ -2937,7 +3675,7 @@ xg_build_to_stack (istack, insn, bi)
}
-/* Return true on valid expansion. */
+/* Return TRUE on valid expansion. */
bfd_boolean
xg_expand_to_stack (istack, insn, lateral_steps)
@@ -2947,7 +3685,7 @@ xg_expand_to_stack (istack, insn, lateral_steps)
{
int stack_size = istack->ninsn;
int steps_taken = 0;
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
@@ -2994,7 +3732,7 @@ xg_expand_narrow (targ, insn)
TInsn *targ;
TInsn *insn;
{
- TransitionTable *table = xg_build_widen_table ();
+ TransitionTable *table = xg_build_widen_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->insn_type == ITYPE_INSN);
@@ -3007,7 +3745,7 @@ xg_expand_narrow (targ, insn)
&& is_unique_insn_expansion (rule))
{
/* Is it a larger instruction? */
- if (xg_get_insn_size (insn)
+ if (xg_get_single_size (insn->opcode)
<= xg_get_build_instr_size (rule->to_instr))
{
xg_build_to_insn (targ, insn, rule->to_instr);
@@ -3020,12 +3758,13 @@ xg_expand_narrow (targ, insn)
/* Assumes: All immeds are constants. Check that all constants fit
- into their immeds; return false if not. */
+ into their immeds; return FALSE if not. */
static bfd_boolean
xg_immeds_fit (insn)
const TInsn *insn;
{
+ xtensa_isa isa = xtensa_default_isa;
int i;
int n = insn->ntok;
@@ -3033,20 +3772,17 @@ xg_immeds_fit (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))
+ if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
continue;
switch (expr->X_op)
{
case O_register:
case O_constant:
- {
- if (xg_check_operand (expr->X_add_number, opnd))
- return FALSE;
- }
+ if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+ return FALSE;
break;
+
default:
/* The symbol should have a fixup associated with it. */
assert (FALSE);
@@ -3068,8 +3804,11 @@ xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
offsetT pc_offset;
long stretch;
{
+ xtensa_isa isa = xtensa_default_isa;
symbolS *symbolP;
- offsetT target, pc, new_offset;
+ fragS *sym_frag;
+ offsetT target, pc;
+ uint32 new_offset;
int i;
int n = insn->ntok;
@@ -3078,16 +3817,21 @@ xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
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))
+ if (xtensa_operand_is_register (isa, insn->opcode, i) == 1)
continue;
switch (expr->X_op)
{
case O_register:
case O_constant:
- if (xg_check_operand (expr->X_add_number, opnd))
+ if (xg_check_operand (expr->X_add_number, insn->opcode, i))
+ return FALSE;
+ break;
+
+ case O_lo16:
+ case O_hi16:
+ /* Check for the worst case. */
+ if (xg_check_operand (0xffff, insn->opcode, i))
return FALSE;
break;
@@ -3097,13 +3841,21 @@ xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
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)
+ /* If it is PC-relative and the symbol is not in the same
+ segment as the PC.... */
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 0
|| S_GET_SEGMENT (expr->X_add_symbol) != pc_seg)
return FALSE;
+ /* If it is a weak symbol, then assume it won't reach. This will
+ only affect calls when longcalls are enabled, because if
+ longcalls are disabled, then the call is marked as a specific
+ opcode. */
+ if (S_IS_WEAK (expr->X_add_symbol))
+ return FALSE;
+
symbolP = expr->X_add_symbol;
+ sym_frag = symbol_get_frag (symbolP);
target = S_GET_VALUE (symbolP) + expr->X_add_number;
pc = pc_frag->fr_address + pc_offset;
@@ -3113,11 +3865,16 @@ xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
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))
+ if (stretch != 0
+ && sym_frag->relax_marker != pc_frag->relax_marker
+ && S_GET_SEGMENT (symbolP) == pc_seg)
+ {
+ target += stretch;
+ }
+
+ new_offset = target;
+ xtensa_operand_do_reloc (isa, insn->opcode, i, &new_offset, pc);
+ if (xg_check_operand (new_offset, insn->opcode, i))
return FALSE;
break;
@@ -3132,80 +3889,22 @@ xg_symbolic_immeds_fit (insn, pc_seg, pc_frag, pc_offset, stretch)
/* This will check to see if the value can be converted into the
- operand type. It will return true if it does not fit. */
+ operand type. It will return TRUE if it does not fit. */
static bfd_boolean
-xg_check_operand (value, operand)
+xg_check_operand (value, opcode, operand)
int32 value;
- xtensa_operand operand;
+ xtensa_opcode opcode;
+ int 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;
+ if (xtensa_operand_encode (xtensa_default_isa, opcode, operand, &valbuf))
+ return TRUE;
+ return FALSE;
}
+
/* Relax the assembly instruction at least "min_steps".
Return the number of steps taken. */
@@ -3215,10 +3914,10 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
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. */
+ 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;
@@ -3240,7 +3939,7 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
istack_push (istack, insn);
return steps_taken;
}
- tinsn_copy (&current_insn, insn);
+ current_insn = *insn;
/* Walk through all of the single instruction expansions. */
while (xg_is_single_relaxable_insn (&current_insn))
@@ -3259,7 +3958,7 @@ xg_assembly_relax (istack, insn, pc_seg, pc_frag, pc_offset, min_steps,
return steps_taken;
}
}
- tinsn_copy (&current_insn, &single_target);
+ current_insn = single_target;
}
/* Now check for a multi-instruction expansion. */
@@ -3302,9 +4001,10 @@ xg_force_frag_space (size)
void
-xg_finish_frag (last_insn, state, max_growth, is_insn)
+xg_finish_frag (last_insn, frag_state, slot0_state, max_growth, is_insn)
char *last_insn;
- enum xtensa_relax_statesE state;
+ enum xtensa_relax_statesE frag_state;
+ enum xtensa_relax_statesE slot0_state;
int max_growth;
bfd_boolean is_insn;
{
@@ -3314,6 +4014,7 @@ xg_finish_frag (last_insn, state, max_growth, is_insn)
beginning of the growth area. */
fragS *old_frag;
+
xg_force_frag_space (max_growth);
old_frag = frag_now;
@@ -3323,7 +4024,10 @@ xg_finish_frag (last_insn, state, max_growth, 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);
+ frag_state, frag_now->fr_symbol, frag_now->fr_offset, last_insn);
+
+ old_frag->tc_frag_data.slot_subtypes[0] = slot0_state;
+ xtensa_set_frag_assembly_state (frag_now);
/* Just to make sure that we did not split it up. */
assert (old_frag->fr_next == frag_now);
@@ -3337,19 +4041,18 @@ is_branch_jmp_to_next (insn, fragP)
{
xtensa_isa isa = xtensa_default_isa;
int i;
- int num_ops = xtensa_num_operands (isa, insn->opcode);
+ int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
int target_op = -1;
symbolS *sym;
fragS *target_frag;
- if (is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_branch (isa, insn->opcode) == 0
+ && xtensa_opcode_is_jump (isa, insn->opcode) == 0)
return FALSE;
for (i = 0; i < num_ops; i++)
{
- xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
- char *kind = xtensa_operand_kind (opnd);
- if (strlen (kind) == 1 && *kind == 'l')
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1)
{
target_op = i;
break;
@@ -3388,32 +4091,25 @@ xg_add_branch_and_loop_targets (insn)
TInsn *insn;
{
xtensa_isa isa = xtensa_default_isa;
- int num_ops = xtensa_num_operands (isa, insn->opcode);
+ int num_ops = xtensa_opcode_num_operands (isa, insn->opcode);
- if (is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_loop (isa, insn->opcode) == 1)
{
int i = 1;
- xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
- char *kind = xtensa_operand_kind (opnd);
- if (strlen (kind) == 1 && *kind == 'l')
- if (insn->tok[i].X_op == O_symbol)
- symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
+ && insn->tok[i].X_op == O_symbol)
+ symbol_get_tc (insn->tok[i].X_add_symbol)->is_loop_target = TRUE;
return;
}
- /* Currently, we do not add branch targets. This is an optimization
- for later that tries to align only branch targets, not just any
- label in a text section. */
-
- if (align_only_targets)
+ if (xtensa_opcode_is_branch (isa, insn->opcode) == 1
+ || xtensa_opcode_is_loop (isa, insn->opcode) == 1)
{
int i;
for (i = 0; i < insn->ntok && i < num_ops; i++)
{
- xtensa_operand opnd = xtensa_get_operand (isa, insn->opcode, i);
- char *kind = xtensa_operand_kind (opnd);
- if (strlen (kind) == 1 && *kind == 'l'
+ if (xtensa_operand_is_PCrelative (isa, insn->opcode, i) == 1
&& insn->tok[i].X_op == O_symbol)
{
symbolS *sym = insn->tok[i].X_add_symbol;
@@ -3426,6 +4122,68 @@ xg_add_branch_and_loop_targets (insn)
}
+/* The routine xg_instruction_matches_option_term must return TRUE
+ when a given option term is true. The meaning of all of the option
+ terms is given interpretation by this function. This is needed when
+ an option depends on the state of a directive, but there are no such
+ options in use right now. */
+
+bfd_boolean
+xg_instruction_matches_option_term (insn, option)
+ TInsn *insn ATTRIBUTE_UNUSED;
+ const ReqOrOption *option;
+{
+ if (strcmp (option->option_name, "realnop") == 0
+ || strncmp (option->option_name, "IsaUse", 6) == 0)
+ {
+ /* These conditions were evaluated statically when building the
+ relaxation table. There's no need to reevaluate them now. */
+ return TRUE;
+ }
+ else
+ {
+ as_fatal (_("internal error: unknown option name '%s'"),
+ option->option_name);
+ }
+}
+
+
+bfd_boolean
+xg_instruction_matches_or_options (insn, or_option)
+ TInsn *insn;
+ const ReqOrOptionList *or_option;
+{
+ const ReqOrOption *option;
+ /* Must match each of the AND terms. */
+ for (option = or_option; option != NULL; option = option->next)
+ {
+ if (xg_instruction_matches_option_term (insn, option))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+bfd_boolean
+xg_instruction_matches_options (insn, options)
+ TInsn *insn;
+ const ReqOptionList *options;
+{
+ const ReqOption *req_options;
+ /* Must match each of the AND terms. */
+ for (req_options = options;
+ req_options != NULL;
+ req_options = req_options->next)
+ {
+ /* Must match one of the OR clauses. */
+ if (!xg_instruction_matches_or_options (insn,
+ req_options->or_option_terms))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
/* Return the transition rule that matches or NULL if none matches. */
bfd_boolean
@@ -3452,19 +4210,40 @@ xg_instruction_matches_rule (insn, rule)
/* 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)
+ if (expr_is_const (exp1))
{
- 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;
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (get_expr_const (exp1) != cond->op_data)
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (get_expr_const (exp1) == cond->op_data)
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else if (expr_is_register (exp1))
+ {
+ switch (cond->cmp)
+ {
+ case OP_EQUAL:
+ if (get_expr_register (exp1) != cond->op_data)
+ return FALSE;
+ break;
+ case OP_NOTEQUAL:
+ if (get_expr_register (exp1) == cond->op_data)
+ return FALSE;
+ break;
+ default:
+ return FALSE;
+ }
}
+ else
+ return FALSE;
break;
case OP_OPERAND:
@@ -3492,15 +4271,87 @@ xg_instruction_matches_rule (insn, rule)
return FALSE;
}
}
+ if (!xg_instruction_matches_options (insn, rule->options))
+ return FALSE;
+
return TRUE;
}
+static int
+transition_rule_cmp (a, b)
+ const TransitionRule *a;
+ const TransitionRule *b;
+{
+ bfd_boolean a_greater = FALSE;
+ bfd_boolean b_greater = FALSE;
+
+ ReqOptionList *l_a = a->options;
+ ReqOptionList *l_b = b->options;
+
+ /* We only care if they both are the same except for
+ a const16 vs. an l32r. */
+
+ while (l_a && l_b && ((l_a->next == NULL) == (l_b->next == NULL)))
+ {
+ ReqOrOptionList *l_or_a = l_a->or_option_terms;
+ ReqOrOptionList *l_or_b = l_b->or_option_terms;
+ while (l_or_a && l_or_b && ((l_a->next == NULL) == (l_b->next == NULL)))
+ {
+ if (l_or_a->is_true != l_or_b->is_true)
+ return 0;
+ if (strcmp (l_or_a->option_name, l_or_b->option_name) != 0)
+ {
+ /* This is the case we care about. */
+ if (strcmp (l_or_a->option_name, "IsaUseConst16") == 0
+ && strcmp (l_or_b->option_name, "IsaUseL32R") == 0)
+ {
+ if (prefer_const16)
+ a_greater = TRUE;
+ else
+ b_greater = TRUE;
+ }
+ else if (strcmp (l_or_a->option_name, "IsaUseL32R") == 0
+ && strcmp (l_or_b->option_name, "IsaUseConst16") == 0)
+ {
+ if (prefer_const16)
+ b_greater = TRUE;
+ else
+ a_greater = TRUE;
+ }
+ else
+ return 0;
+ }
+ l_or_a = l_or_a->next;
+ l_or_b = l_or_b->next;
+ }
+ if (l_or_a || l_or_b)
+ return 0;
+
+ l_a = l_a->next;
+ l_b = l_b->next;
+ }
+ if (l_a || l_b)
+ return 0;
+
+ /* Incomparable if the substitution was used differently in two cases. */
+ if (a_greater && b_greater)
+ return 0;
+
+ if (b_greater)
+ return 1;
+ if (a_greater)
+ return -1;
+
+ return 0;
+}
+
+
TransitionRule *
xg_instruction_match (insn)
TInsn *insn;
{
- TransitionTable *table = xg_build_simplify_table ();
+ TransitionTable *table = xg_build_simplify_table (&transition_rule_cmp);
TransitionList *l;
assert (insn->opcode < table->num_opcodes);
@@ -3515,7 +4366,7 @@ xg_instruction_match (insn)
}
-/* Return false if no error. */
+/* Return FALSE if no error. */
bfd_boolean
xg_build_token_insn (instr_spec, old_insn, new_insn)
@@ -3532,11 +4383,13 @@ xg_build_token_insn (instr_spec, old_insn, new_insn)
new_insn->insn_type = ITYPE_INSN;
new_insn->opcode = instr_spec->opcode;
new_insn->is_specific_opcode = FALSE;
+ new_insn->loc = old_insn->loc;
break;
case INSTR_LITERAL_DEF:
new_insn->insn_type = ITYPE_LITERAL;
new_insn->opcode = XTENSA_UNDEFINED;
new_insn->is_specific_opcode = FALSE;
+ new_insn->loc = old_insn->loc;
break;
case INSTR_LABEL_DEF:
as_bad (_("INSTR_LABEL_DEF not supported yet"));
@@ -3582,15 +4435,20 @@ xg_build_token_insn (instr_spec, old_insn, new_insn)
}
-/* Return true if it was simplified. */
+/* Return TRUE if it was simplified. */
bfd_boolean
xg_simplify_insn (old_insn, new_insn)
TInsn *old_insn;
TInsn *new_insn;
{
- TransitionRule *rule = xg_instruction_match (old_insn);
+ TransitionRule *rule;
BuildInstr *insn_spec;
+
+ if (old_insn->is_specific_opcode || !density_supported)
+ return FALSE;
+
+ rule = xg_instruction_match (old_insn);
if (rule == NULL)
return FALSE;
@@ -3610,7 +4468,7 @@ xg_simplify_insn (old_insn, new_insn)
/* 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
+ multiple instructions/literals onto the stack. Return FALSE if no
error. */
static bfd_boolean
@@ -3622,15 +4480,13 @@ xg_expand_assembly_insn (istack, orig_insn)
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;
- }
+ /* Narrow it if we can. xg_simplify_insn now does all the
+ appropriate checking (e.g., for the density option). */
+ if (xg_simplify_insn (orig_insn, &new_insn))
+ orig_insn = &new_insn;
- noperands = xtensa_num_operands (xtensa_default_isa, orig_insn->opcode);
+ noperands = xtensa_opcode_num_operands (xtensa_default_isa,
+ orig_insn->opcode);
if (orig_insn->ntok < noperands)
{
as_bad (_("found %d operands for '%s': Expected %d"),
@@ -3645,12 +4501,12 @@ xg_expand_assembly_insn (istack, orig_insn)
xtensa_opcode_name (xtensa_default_isa, orig_insn->opcode),
noperands);
- /* If there are not enough operands, we will assert above. If there
+ /* If there are not enough operands, we will assert above. If there
are too many, just cut out the extras here. */
orig_insn->ntok = noperands;
- /* Cases:
+ /* Cases:
Instructions with all constant immeds:
Assemble them and relax the instruction if possible.
@@ -3658,20 +4514,20 @@ xg_expand_assembly_insn (istack, orig_insn)
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.
-
+ 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.
+ 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 ())
+ if (orig_insn->is_specific_opcode || !use_transform ())
{
istack_push (istack, orig_insn);
return FALSE;
@@ -3692,14 +4548,6 @@ xg_expand_assembly_insn (istack, orig_insn)
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;
}
@@ -3718,6 +4566,8 @@ xg_assemble_literal (insn)
offsetT litsize = 4;
offsetT litalign = 2; /* 2^2 = 4 */
expressionS saved_loc;
+ expressionS * emit_val;
+
set_expr_symbol_offset (&saved_loc, frag_now->fr_symbol, frag_now_fix ());
assert (insn->insn_type == ITYPE_LITERAL);
@@ -3725,12 +4575,39 @@ xg_assemble_literal (insn)
xtensa_switch_to_literal_fragment (&state);
+ emit_val = &insn->tok[0];
+ if (emit_val->X_op == O_big)
+ {
+ int size = emit_val->X_add_number * CHARS_PER_LITTLENUM;
+ if (size > litsize)
+ {
+ /* This happens when someone writes a "movi a2, big_number". */
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("invalid immediate"));
+ xtensa_restore_emit_state (&state);
+ return NULL;
+ }
+ }
+
/* Force a 4-byte align here. Note that this opens a new frag, so all
literals done with this function have a frag to themselves. That's
important for the way text section literals work. */
frag_align (litalign, 0, 0);
+ record_alignment (now_seg, litalign);
- emit_expr (&insn->tok[0], litsize);
+ if (emit_val->X_op == O_pltrel)
+ {
+ char *p = frag_more (litsize);
+ xtensa_set_frag_assembly_state (frag_now);
+ if (emit_val->X_add_symbol)
+ emit_val->X_op = O_symbol;
+ else
+ emit_val->X_op = O_constant;
+ fix_new_exp (frag_now, p - frag_now->fr_literal,
+ litsize, emit_val, 0, BFD_RELOC_XTENSA_PLT);
+ }
+ else
+ emit_expr (emit_val, litsize);
assert (frag_now->tc_frag_data.literal_frag == NULL);
frag_now->tc_frag_data.literal_frag = get_literal_pool_location (now_seg);
@@ -3744,25 +4621,43 @@ xg_assemble_literal (insn)
}
+bfd_boolean
+xg_valid_literal_expression (exp)
+ const expressionS *exp;
+{
+ switch (exp->X_op)
+ {
+ case O_constant:
+ case O_symbol:
+ case O_big:
+ case O_uminus:
+ case O_subtract:
+ case O_pltrel:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+
static void
-xg_assemble_literal_space (size)
+xg_assemble_literal_space (size, slot)
/* const */ int size;
+ int slot;
{
emit_state state;
- /* We might have to do something about this alignment. It only
+ /* 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);
+ record_alignment (now_seg, litalign);
xg_force_frag_space (size);
@@ -3770,11 +4665,11 @@ xg_assemble_literal_space (size)
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);
+ xg_finish_frag (0, RELAX_LITERAL, 0, size, FALSE);
/* Go back. */
xtensa_restore_emit_state (&state);
- frag_now->tc_frag_data.literal_frag = lit_saved_frag;
+ frag_now->tc_frag_data.literal_frags[slot] = lit_saved_frag;
}
@@ -3854,7 +4749,7 @@ xtensa_clear_insn_labels (void)
}
-/* Return true if the section flags are marked linkonce
+/* Return TRUE if the section flags are marked linkonce
or the name is .gnu.linkonce*. */
bfd_boolean
@@ -3879,113 +4774,10 @@ get_is_linkonce_section (abfd, sec)
}
-/* 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;
+xg_emit_insn_to_buf (tinsn, fmt, buf, fragP, offset, build_fix)
+ TInsn *tinsn;
+ xtensa_format fmt;
char *buf;
fragS *fragP;
offsetT offset;
@@ -3997,63 +4789,109 @@ xg_emit_insn_to_buf (t_insn, buf, fragP, offset, build_fix)
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- has_symbolic_immed = tinsn_to_insnbuf (t_insn, insnbuf);
+ has_symbolic_immed = tinsn_to_insnbuf (tinsn, insnbuf);
if (has_symbolic_immed && build_fix)
{
/* Add a fixup. */
- int opnum = get_relaxable_immed (t_insn->opcode);
- expressionS *exp = &t_insn->tok[opnum];
+ int opnum = get_relaxable_immed (tinsn->opcode);
+ expressionS *exp = &tinsn->tok[opnum];
- if (!xg_add_opcode_fix (t_insn->opcode,
- opnum, exp, fragP, offset))
+ if (!xg_add_opcode_fix (tinsn, opnum, fmt, 0, exp, fragP, offset))
ok = FALSE;
}
fragP->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
+ xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf, 0);
return ok;
}
/* Put in a fixup record based on the opcode.
- Return true on success. */
+ Return TRUE on success. */
bfd_boolean
-xg_add_opcode_fix (opcode, opnum, expr, fragP, offset)
- xtensa_opcode opcode;
+xg_add_opcode_fix (tinsn, opnum, fmt, slot, expr, fragP, offset)
+ TInsn *tinsn;
int opnum;
+ xtensa_format fmt;
+ int slot;
expressionS *expr;
fragS *fragP;
offsetT offset;
-{
- bfd_reloc_code_real_type reloc;
- reloc_howto_type *howto;
- int insn_length;
+{
+ xtensa_opcode opcode = tinsn->opcode;
+ bfd_reloc_code_real_type reloc;
+ reloc_howto_type *howto;
+ int fmt_length;
fixS *the_fix;
- reloc = opnum_to_reloc (opnum);
- if (reloc == BFD_RELOC_NONE)
+ reloc = BFD_RELOC_NONE;
+
+ /* First try the special cases for "alternate" relocs. */
+ if (opcode == xtensa_l32r_opcode)
+ {
+ if (fragP->tc_frag_data.use_absolute_literals)
+ reloc = encode_alt_reloc (slot);
+ }
+ else if (opcode == xtensa_const16_opcode)
+ {
+ if (expr->X_op == O_lo16)
+ {
+ reloc = encode_reloc (slot);
+ expr->X_op = O_symbol;
+ }
+ else if (expr->X_op == O_hi16)
+ {
+ reloc = encode_alt_reloc (slot);
+ expr->X_op = O_symbol;
+ }
+ }
+
+ if (opnum != get_relaxable_immed (opcode))
{
- as_bad (_("invalid relocation operand %i on '%s'"),
+ as_bad (_("invalid relocation for operand %i of '%s'"),
opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
return FALSE;
}
- howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ /* Handle erroneous "@h" and "@l" expressions here before they propagate
+ into the symbol table where the generic portions of the assembler
+ won't know what to do with them. */
+ if (expr->X_op == O_lo16 || expr->X_op == O_hi16)
+ {
+ as_bad (_("invalid expression for operand %i of '%s'"),
+ opnum, xtensa_opcode_name (xtensa_default_isa, opcode));
+ return FALSE;
+ }
+ /* Next try the generic relocs. */
+ if (reloc == BFD_RELOC_NONE)
+ reloc = encode_reloc (slot);
+ if (reloc == BFD_RELOC_NONE)
+ {
+ as_bad (_("invalid relocation in instruction slot %i"), slot);
+ return FALSE;
+ }
+
+ howto = bfd_reloc_type_lookup (stdoutput, reloc);
if (!howto)
{
- as_bad (_("undefined symbol for opcode \"%s\"."),
+ 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,
+ fmt_length = xtensa_format_length (xtensa_default_isa, fmt);
+ the_fix = fix_new_exp (fragP, offset, fmt_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)))
+ if (expr->X_add_symbol
+ && (S_IS_EXTERNAL (expr->X_add_symbol)
+ || S_IS_WEAK (expr->X_add_symbol)))
the_fix->fx_plt = TRUE;
+
+ the_fix->tc_fix_data.X_add_symbol = expr->X_add_symbol;
+ the_fix->tc_fix_data.X_add_number = expr->X_add_number;
+ the_fix->tc_fix_data.slot = slot;
return TRUE;
}
@@ -4083,7 +4921,7 @@ xg_resolve_labels (insn, label_sym)
{
symbolS *sym = get_special_label_symbol ();
int i;
- /* assert(!insn->is_literal); */
+ /* assert (!insn->is_literal); */
for (i = 0; i < insn->ntok; i++)
if (insn->tok[i].X_add_symbol == sym)
insn->tok[i].X_add_symbol = label_sym;
@@ -4091,157 +4929,7 @@ xg_resolve_labels (insn, 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
+/* Return TRUE if the instruction can write to the specified
integer register. */
static bfd_boolean
@@ -4254,16 +4942,18 @@ is_register_writer (insn, regset, regnum)
int num_ops;
xtensa_isa isa = xtensa_default_isa;
- num_ops = xtensa_num_operands (isa, insn->opcode);
+ num_ops = xtensa_opcode_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 == '=')
+ char inout;
+ inout = xtensa_operand_inout (isa, insn->opcode, i);
+ if ((inout == 'o' || inout == 'm')
+ && xtensa_operand_is_register (isa, insn->opcode, i) == 1)
{
- if (strcmp (xtensa_operand_kind (operand), regset) == 0)
+ xtensa_regfile opnd_rf =
+ xtensa_operand_regfile (isa, insn->opcode, i);
+ if (!strcmp (xtensa_regfile_shortname (isa, opnd_rf), regset))
{
if ((insn->tok[i].X_op == O_register)
&& (insn->tok[i].X_add_number == regnum))
@@ -4297,16 +4987,10 @@ is_bad_loopend_opcode (tinsn)
|| opcode == xtensa_ret_n_opcode
|| opcode == xtensa_retw_opcode
|| opcode == xtensa_retw_n_opcode
- || opcode == xtensa_waiti_opcode)
+ || opcode == xtensa_waiti_opcode
+ || opcode == xtensa_rsr_lcount_opcode)
return TRUE;
- /* An RSR of LCOUNT is illegal as the last opcode in a loop. */
- if (opcode == xtensa_rsr_opcode
- && tinsn->ntok >= 2
- && tinsn->tok[1].X_op == O_constant
- && tinsn->tok[1].X_add_number == 2)
- return TRUE;
-
return FALSE;
}
@@ -4317,8 +5001,8 @@ is_bad_loopend_opcode (tinsn)
not be aligned: FAKE_LABEL_NAME . {"F", "L", "endfunc"}. */
bfd_boolean
-is_unaligned_label (sym)
- symbolS *sym;
+is_unaligned_label (sym)
+ symbolS *sym;
{
const char *name = S_GET_NAME (sym);
static size_t fake_size = 0;
@@ -4332,7 +5016,7 @@ is_unaligned_label (sym)
if (fake_size == 0)
fake_size = strlen (FAKE_LABEL_NAME);
- if (name
+ if (name
&& strncmp (FAKE_LABEL_NAME, name, fake_size) == 0
&& (name[fake_size] == 'F'
|| name[fake_size] == 'L'
@@ -4362,26 +5046,93 @@ next_non_empty_frag (fragP)
}
-xtensa_opcode
-next_frag_opcode (fragP)
- const fragS * fragP;
+static bfd_boolean
+next_frag_opcode_is_loop (fragP, opcode)
+ const fragS *fragP;
+ xtensa_opcode *opcode;
+{
+ xtensa_opcode out_opcode;
+ const fragS *next_fragP = next_non_empty_frag (fragP);
+
+ if (next_fragP == NULL)
+ return FALSE;
+
+ out_opcode = get_opcode_from_buf (next_fragP->fr_literal, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa, out_opcode) == 1)
+ {
+ *opcode = out_opcode;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+static int
+next_frag_format_size (fragP)
+ const fragS *fragP;
{
const fragS *next_fragP = next_non_empty_frag (fragP);
+ return frag_format_size (next_fragP);
+}
+
+
+static int
+frag_format_size (fragP)
+ const fragS * fragP;
+{
static xtensa_insnbuf insnbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt;
+ int fmt_size;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
- if (next_fragP == NULL)
+ if (fragP == NULL)
return XTENSA_UNDEFINED;
- xtensa_insnbuf_from_chars (isa, insnbuf, next_fragP->fr_literal);
- return xtensa_decode_insn (isa, insnbuf);
+ xtensa_insnbuf_from_chars (isa, insnbuf, fragP->fr_literal, 0);
+
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return XTENSA_UNDEFINED;
+ fmt_size = xtensa_format_length (isa, fmt);
+
+ /* If the next format won't be changing due to relaxation, just
+ return the length of the first format. */
+ if (fragP->fr_opcode != fragP->fr_literal)
+ return fmt_size;
+
+ /* If during relaxation we have to pull an instruction out of a
+ multi-slot instruction, we will return the more conservative
+ number. This works because alignment on bigger instructions
+ is more restrictive than alignment on smaller instructions.
+ This is more conservative than we would like, but it happens
+ infrequently. */
+
+ if (xtensa_format_num_slots (xtensa_default_isa, fmt) > 1)
+ return fmt_size;
+
+ /* If we aren't doing one of our own relaxations or it isn't
+ slot-based, then the insn size won't change. */
+ if (fragP->fr_type != rs_machine_dependent)
+ return fmt_size;
+ if (fragP->fr_subtype != RELAX_SLOTS)
+ return fmt_size;
+
+ /* If an instruction is about to grow, return the longer size. */
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP1
+ || fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED_STEP2)
+ return 3;
+
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ return 2 + fragP->tc_frag_data.text_expansion[0];
+
+ return fmt_size;
}
-/* Return true if the target frag is one of the next non-empty frags. */
+/* Return TRUE if the target frag is one of the next non-empty frags. */
bfd_boolean
is_next_frag_target (fragP, target)
@@ -4413,20 +5164,66 @@ is_next_frag_target (fragP, target)
switch its state so it will instantiate a NOP. */
static void
-update_next_frag_nop_state (fragP)
+update_next_frag_state (fragP, unreachable)
fragS *fragP;
+ bfd_boolean unreachable;
{
fragS *next_fragP = fragP->fr_next;
+ fragS *new_target = NULL;
- while (next_fragP && next_fragP->fr_fix == 0)
+ if (align_targets)
{
- if (next_fragP->fr_type == rs_machine_dependent
- && next_fragP->fr_subtype == RELAX_LOOP_END)
+ /* We are guaranteed there will be one of these... */
+ while (!(next_fragP->fr_type == rs_machine_dependent
+ && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || next_fragP->fr_subtype == RELAX_UNREACHABLE)))
+ next_fragP = next_fragP->fr_next;
+
+ assert (next_fragP->fr_type == rs_machine_dependent
+ && (next_fragP->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || next_fragP->fr_subtype == RELAX_UNREACHABLE));
+
+ /* ...and one of these. */
+ new_target = next_fragP->fr_next;
+ while (!(new_target->fr_type == rs_machine_dependent
+ && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+ || new_target->fr_subtype == RELAX_DESIRE_ALIGN)))
+ new_target = new_target->fr_next;
+
+ assert (new_target->fr_type == rs_machine_dependent
+ && (new_target->fr_subtype == RELAX_MAYBE_DESIRE_ALIGN
+ || new_target->fr_subtype == RELAX_DESIRE_ALIGN));
+ }
+ if (unreachable)
+ {
+ if (align_targets)
{
- next_fragP->fr_subtype = RELAX_LOOP_END_ADD_NOP;
- return;
+ next_fragP->fr_subtype = RELAX_UNREACHABLE;
+ next_fragP->tc_frag_data.is_unreachable = TRUE;
+ new_target->fr_subtype = RELAX_DESIRE_ALIGN;
+ new_target->tc_frag_data.is_branch_target = TRUE;
+ }
+ 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;
+ }
+ }
+ else
+ {
+ if (align_targets)
+ {
+ next_fragP->fr_subtype = RELAX_MAYBE_UNREACHABLE;
+ next_fragP->tc_frag_data.is_unreachable = FALSE;
+ new_target->fr_subtype = RELAX_MAYBE_DESIRE_ALIGN;
+ new_target->tc_frag_data.is_branch_target = FALSE;
}
- next_fragP = next_fragP->fr_next;
}
}
@@ -4435,7 +5232,7 @@ static bfd_boolean
next_frag_is_branch_target (fragP)
const fragS *fragP;
{
- /* Sometimes an empty will end up here due storage allocation issues,
+ /* Sometimes an empty will end up here due to storage allocation issues,
so we have to skip until we find something legit. */
for (fragP = fragP->fr_next; fragP; fragP = fragP->fr_next)
{
@@ -4470,13 +5267,13 @@ next_frag_pre_opcode_bytes (fragp)
const fragS *fragp;
{
const fragS *next_fragp = fragp->fr_next;
+ xtensa_opcode next_opcode;
- xtensa_opcode next_opcode = next_frag_opcode (fragp);
- if (!is_loop_opcode (next_opcode))
+ if (!next_frag_opcode_is_loop (fragp, &next_opcode))
return 0;
- /* Sometimes an empty will end up here due storage allocation issues.
- So we have to skip until we find something legit. */
+ /* Sometimes an empty will end up here due to storage allocation issues,
+ so we have to skip until we find something legit. */
while (next_fragp->fr_fix == 0)
next_fragp = next_fragp->fr_next;
@@ -4485,8 +5282,9 @@ next_frag_pre_opcode_bytes (fragp)
/* There is some implicit knowledge encoded in here.
The LOOP instructions that are NOT RELAX_IMMED have
- been relaxed. */
- if (next_fragp->fr_subtype > RELAX_IMMED)
+ been relaxed. Note that we can assume that the LOOP
+ instruction is in slot 0 because loops aren't bundleable. */
+ if (next_fragp->tc_frag_data.slot_subtypes[0] > RELAX_IMMED)
return get_expanded_loop_offset (next_opcode);
return 0;
@@ -4505,27 +5303,37 @@ xtensa_mark_literal_pool_location ()
emit_state s;
fragS *pool_location;
+ if (use_literal_section && !directive_state[directive_absolute_literals])
+ return;
+
frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
/* We stash info in the fr_var of these frags
- so we can later move the literal's fixes into this
+ so we can later move the literal's fixes into this
frchain's fix list. We can use fr_var because fr_var's
interpretation depends solely on the fr_type and subtype. */
pool_location = frag_now;
- frag_variant (rs_machine_dependent, 0, (int) frchain_now,
+ frag_variant (rs_machine_dependent, 0, (int) frchain_now,
RELAX_LITERAL_POOL_BEGIN, NULL, 0, NULL);
- frag_variant (rs_machine_dependent, 0, (int) now_seg,
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_variant (rs_machine_dependent, 0, (int) now_seg,
RELAX_LITERAL_POOL_END, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
/* Now put a frag into the literal pool that points to this location. */
set_literal_pool_location (now_seg, pool_location);
- xtensa_switch_to_literal_fragment (&s);
+ xtensa_switch_to_non_abs_literal_fragment (&s);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
/* Close whatever frag is there. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.literal_frag = pool_location;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
xtensa_restore_emit_state (&s);
+ xtensa_set_frag_assembly_state (frag_now);
}
@@ -4564,7 +5372,7 @@ xtensa_move_labels (new_frag, new_offset, loops_ok)
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)
+ if (loops_ok || ! symbol_get_tc (lit_sym)->is_loop_target)
{
S_SET_VALUE (lit_sym, new_offset);
symbol_set_frag (lit_sym, new_frag);
@@ -4573,6 +5381,40 @@ xtensa_move_labels (new_frag, new_offset, loops_ok)
}
+/* Build a nop of the correct size into tinsn. */
+
+static void
+build_nop (tinsn, size)
+ TInsn *tinsn;
+ int size;
+{
+ tinsn_init (tinsn);
+ switch (size)
+ {
+ case 2:
+ tinsn->opcode = xtensa_nop_n_opcode;
+ tinsn->ntok = 0;
+ if (tinsn->opcode == XTENSA_UNDEFINED)
+ as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
+ break;
+
+ case 3:
+ if (xtensa_nop_opcode == XTENSA_UNDEFINED)
+ {
+ tinsn->opcode = xtensa_or_opcode;
+ set_expr_const (&tinsn->tok[0], 1);
+ set_expr_const (&tinsn->tok[1], 1);
+ set_expr_const (&tinsn->tok[2], 1);
+ tinsn->ntok = 3;
+ }
+ else
+ tinsn->opcode = xtensa_nop_opcode;
+
+ assert (tinsn->opcode != XTENSA_UNDEFINED);
+ }
+}
+
+
/* Assemble a NOP of the requested size in the buffer. User must have
allocated "buf" with at least "size" bytes. */
@@ -4582,38 +5424,15 @@ assemble_nop (size, buf)
char *buf;
{
static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn;
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ TInsn tinsn;
- tinsn_init (&t_insn);
- switch (size)
- {
- case 2:
- t_insn.opcode = xtensa_nop_n_opcode;
- t_insn.ntok = 0;
- if (t_insn.opcode == XTENSA_UNDEFINED)
- as_fatal (_("opcode 'NOP.N' unavailable in this configuration"));
- tinsn_to_insnbuf (&t_insn, insnbuf);
- xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
- break;
+ build_nop (&tinsn, size);
- case 3:
- t_insn.opcode = xtensa_or_opcode;
- assert (t_insn.opcode != XTENSA_UNDEFINED);
- if (t_insn.opcode == XTENSA_UNDEFINED)
- as_fatal (_("opcode 'OR' unavailable in this configuration"));
- set_expr_const (&t_insn.tok[0], 1);
- set_expr_const (&t_insn.tok[1], 1);
- set_expr_const (&t_insn.tok[2], 1);
- t_insn.ntok = 3;
- tinsn_to_insnbuf (&t_insn, insnbuf);
- xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf);
- break;
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- default:
- as_fatal (_("invalid %d-byte NOP requested"), size);
- }
+ tinsn_to_insnbuf (&tinsn, insnbuf);
+ xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, buf, 0);
}
@@ -4631,8 +5450,7 @@ get_expanded_loop_offset (opcode)
/* This is the OFFSET of the loop instruction in the expanded loop.
This MUST correspond directly to the specification of the loop
expansion. It will be validated on fragment conversion. */
- if (opcode == XTENSA_UNDEFINED)
- as_fatal (_("get_expanded_loop_offset: undefined opcode"));
+ assert (opcode != XTENSA_UNDEFINED);
if (opcode == xtensa_loop_opcode)
return 0;
if (opcode == xtensa_loopnez_opcode)
@@ -4660,6 +5478,219 @@ set_literal_pool_location (seg, literal_pool_loc)
seg_info (seg)->tc_segment_info_data.literal_pool_loc = literal_pool_loc;
}
+
+/* Set frag assembly state should be called when a new frag is
+ opened and after a frag has been closed. */
+
+void
+xtensa_set_frag_assembly_state (fragP)
+ fragS *fragP;
+{
+ if (!density_supported)
+ fragP->tc_frag_data.is_no_density = TRUE;
+
+ /* This function is called from subsegs_finish, which is called
+ after xtensa_end, so we can't use "use_transform" or
+ "use_schedule" here. */
+ if (!directive_state[directive_transform])
+ fragP->tc_frag_data.is_no_transform = TRUE;
+ fragP->tc_frag_data.use_absolute_literals =
+ directive_state[directive_absolute_literals];
+ fragP->tc_frag_data.is_assembly_state_set = TRUE;
+}
+
+
+bfd_boolean
+relaxable_section (sec)
+ asection *sec;
+{
+ return (sec->flags & SEC_DEBUGGING) == 0;
+}
+
+
+static void
+xtensa_find_unmarked_state_frags ()
+{
+ segT *seclist;
+
+ /* Walk over each fragment of all of the current segments. For each
+ unmarked fragment, mark it with the same info as the previous
+ fragment. */
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+ segment_info_type *seginfo;
+ fragS *fragP;
+ flagword flags;
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ seginfo = seg_info (sec);
+ if (seginfo && seginfo->frchainP)
+ {
+ fragS *last_fragP = 0;
+ for (fragP = seginfo->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ if (fragP->fr_fix != 0
+ && !fragP->tc_frag_data.is_assembly_state_set)
+ {
+ if (last_fragP == 0)
+ {
+ as_warn_where (fragP->fr_file, fragP->fr_line,
+ _("assembly state not set for first frag in section %s"),
+ sec->name);
+ }
+ else
+ {
+ fragP->tc_frag_data.is_assembly_state_set = TRUE;
+ fragP->tc_frag_data.is_no_density =
+ last_fragP->tc_frag_data.is_no_density;
+ fragP->tc_frag_data.is_no_transform =
+ last_fragP->tc_frag_data.is_no_transform;
+ fragP->tc_frag_data.use_absolute_literals =
+ last_fragP->tc_frag_data.use_absolute_literals;
+ }
+ }
+ if (fragP->tc_frag_data.is_assembly_state_set)
+ last_fragP = fragP;
+ }
+ }
+ }
+}
+
+
+static void
+xtensa_find_unaligned_branch_targets (abfd, sec, unused)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ asection *sec;
+ PTR unused ATTRIBUTE_UNUSED;
+{
+ flagword flags = bfd_get_section_flags (abfd, sec);
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *frag = seginfo->frchainP->frch_root;
+
+ if (flags & SEC_CODE)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+ while (frag != NULL)
+ {
+ if (frag->tc_frag_data.is_branch_target)
+ {
+ int op_size;
+ int frag_addr;
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ frag_addr = frag->fr_address % xtensa_fetch_width;
+ if (frag_addr + op_size > (int) xtensa_fetch_width)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned branch target: %d bytes at 0x%lx"),
+ op_size, frag->fr_address);
+ }
+ frag = frag->fr_next;
+ }
+ xtensa_insnbuf_free (isa, insnbuf);
+ }
+}
+
+
+static void
+xtensa_find_unaligned_loops (abfd, sec, unused)
+ bfd *abfd ATTRIBUTE_UNUSED;
+ asection *sec;
+ PTR unused ATTRIBUTE_UNUSED;
+{
+ flagword flags = bfd_get_section_flags (abfd, sec);
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *frag = seginfo->frchainP->frch_root;
+ xtensa_isa isa = xtensa_default_isa;
+
+ if (flags & SEC_CODE)
+ {
+ xtensa_insnbuf insnbuf = xtensa_insnbuf_alloc (isa);
+ while (frag != NULL)
+ {
+ if (frag->tc_frag_data.is_first_loop_insn)
+ {
+ int op_size;
+ int frag_addr;
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, frag->fr_literal, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ op_size = xtensa_format_length (isa, fmt);
+ frag_addr = frag->fr_address % xtensa_fetch_width;
+
+ if (frag_addr + op_size > (signed) xtensa_fetch_width)
+ as_warn_where (frag->fr_file, frag->fr_line,
+ _("unaligned loop: %d bytes at 0x%lx"),
+ op_size, frag->fr_address);
+ }
+ frag = frag->fr_next;
+ }
+ xtensa_insnbuf_free (isa, insnbuf);
+ }
+}
+
+
+void
+xg_apply_tentative_value (fixP, val)
+ fixS *fixP;
+ valueT val;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ xtensa_format fmt;
+ int slot;
+ bfd_boolean alt_reloc;
+ xtensa_opcode opcode;
+ char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
+
+ (void) decode_reloc (fixP->fx_r_type, &slot, &alt_reloc);
+ if (alt_reloc)
+ as_fatal (_("unexpected fix"));
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, fixpos, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("undecodable fix"));
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+ if (opcode == XTENSA_UNDEFINED)
+ as_fatal (_("undecodable fix"));
+
+ /* CONST16 immediates are not PC-relative, despite the fact that we
+ reuse the normal PC-relative operand relocations for the low part
+ of a CONST16 operand. The code in tc_gen_reloc does not decode
+ the opcodes so it is more convenient to detect this special case
+ here. */
+ if (opcode == xtensa_const16_opcode)
+ return;
+
+ xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode,
+ get_relaxable_immed (opcode), val,
+ fixP->fx_file, fixP->fx_line);
+
+ xtensa_format_set_slot (isa, fmt, slot, insnbuf, slotbuf);
+ xtensa_insnbuf_to_chars (isa, insnbuf, fixpos, 0);
+}
+
/* External Functions and Other GAS Hooks. */
@@ -4702,21 +5733,22 @@ md_begin ()
int current_subsec = now_subseg;
xtensa_isa isa;
-#if STATIC_LIBISA
- isa = xtensa_isa_init ();
-#else
- /* ISA was already initialized by xtensa_init(). */
+ xtensa_default_isa = xtensa_isa_init (0, 0);
isa = xtensa_default_isa;
-#endif
- /* Set up the .literal, .fini.literal and .init.literal sections. */
+ linkrelax = 1;
+
+ /* Set up the .literal, .fini.literal and .init.literal sections. */
memset (&default_lit_sections, 0, sizeof (default_lit_sections));
default_lit_sections.init_lit_seg_name = INIT_LITERAL_SECTION_NAME;
default_lit_sections.fini_lit_seg_name = FINI_LITERAL_SECTION_NAME;
default_lit_sections.lit_seg_name = LITERAL_SECTION_NAME;
+ default_lit_sections.lit4_seg_name = LIT4_SECTION_NAME;
subseg_set (current_section, current_subsec);
+ xg_init_vinsn (&cur_vinsn);
+
xtensa_addi_opcode = xtensa_opcode_lookup (isa, "addi");
xtensa_addmi_opcode = xtensa_opcode_lookup (isa, "addmi");
xtensa_call0_opcode = xtensa_opcode_lookup (isa, "call0");
@@ -4727,21 +5759,43 @@ md_begin ()
xtensa_callx4_opcode = xtensa_opcode_lookup (isa, "callx4");
xtensa_callx8_opcode = xtensa_opcode_lookup (isa, "callx8");
xtensa_callx12_opcode = xtensa_opcode_lookup (isa, "callx12");
+ xtensa_const16_opcode = xtensa_opcode_lookup (isa, "const16");
xtensa_entry_opcode = xtensa_opcode_lookup (isa, "entry");
+ xtensa_movi_opcode = xtensa_opcode_lookup (isa, "movi");
+ xtensa_movi_n_opcode = xtensa_opcode_lookup (isa, "movi.n");
xtensa_isync_opcode = xtensa_opcode_lookup (isa, "isync");
- xtensa_j_opcode = xtensa_opcode_lookup (isa, "j");
xtensa_jx_opcode = xtensa_opcode_lookup (isa, "jx");
+ xtensa_l32r_opcode = xtensa_opcode_lookup (isa, "l32r");
xtensa_loop_opcode = xtensa_opcode_lookup (isa, "loop");
xtensa_loopnez_opcode = xtensa_opcode_lookup (isa, "loopnez");
xtensa_loopgtz_opcode = xtensa_opcode_lookup (isa, "loopgtz");
+ xtensa_nop_opcode = xtensa_opcode_lookup (isa, "nop");
xtensa_nop_n_opcode = xtensa_opcode_lookup (isa, "nop.n");
xtensa_or_opcode = xtensa_opcode_lookup (isa, "or");
xtensa_ret_opcode = xtensa_opcode_lookup (isa, "ret");
xtensa_ret_n_opcode = xtensa_opcode_lookup (isa, "ret.n");
xtensa_retw_opcode = xtensa_opcode_lookup (isa, "retw");
xtensa_retw_n_opcode = xtensa_opcode_lookup (isa, "retw.n");
- xtensa_rsr_opcode = xtensa_opcode_lookup (isa, "rsr");
+ xtensa_rsr_lcount_opcode = xtensa_opcode_lookup (isa, "rsr.lcount");
xtensa_waiti_opcode = xtensa_opcode_lookup (isa, "waiti");
+
+ init_op_placement_info_table ();
+
+ /* Set up the assembly state. */
+ if (!frag_now->tc_frag_data.is_assembly_state_set)
+ xtensa_set_frag_assembly_state (frag_now);
+}
+
+
+/* TC_INIT_FIX_DATA hook */
+
+void
+xtensa_init_fix_data (x)
+ fixS *x;
+{
+ x->tc_fix_data.slot = 0;
+ x->tc_fix_data.X_add_symbol = NULL;
+ x->tc_fix_data.X_add_number = 0;
}
@@ -4751,6 +5805,11 @@ void
xtensa_frob_label (sym)
symbolS *sym;
{
+ /* Since the label was already attached to a frag associated with the
+ previous basic block, it now needs to be reset to the current frag. */
+ symbol_set_frag (sym, frag_now);
+ S_SET_VALUE (sym, (valueT) frag_now_fix ());
+
if (generating_literals)
xtensa_add_literal_sym (sym);
else
@@ -4763,26 +5822,93 @@ xtensa_frob_label (sym)
/* No target aligning in the absolute section. */
if (now_seg != absolute_section
- && align_targets
+ && do_align_targets ()
&& !is_unaligned_label (sym)
- && !frag_now->tc_frag_data.is_literal)
- {
- /* frag_now->tc_frag_data.is_insn = TRUE; */
- frag_var (rs_machine_dependent, 4, 4,
+ && !generating_literals)
+ {
+ float freq = get_subseg_info (now_seg, now_subseg)->cur_target_freq;
+ xtensa_set_frag_assembly_state (frag_now);
+
+ /* The only time this type of frag grows is when there is a
+ negatable branch that needs to be relaxed as the last
+ instruction in a zero-overhead loop. Because alignment frags
+ are so common, marking them all as possibly growing four
+ bytes makes any worst-case analysis appear much worse than it
+ is. So, we make fr_var not actually reflect the amount of
+ memory allocated at the end of this frag, but rather the
+ amount of memory this frag might grow. The "4, 0" below
+ allocates four bytes at the end of the frag for room to grow
+ if we need to relax a loop end with a NOP. Frags prior to
+ this one might grow to align this one, but the frag itself
+ won't grow unless it meets the condition above. */
+
+#define RELAX_LOOP_END_BYTES 4
+
+ frag_var (rs_machine_dependent,
+ RELAX_LOOP_END_BYTES, (int) freq,
RELAX_DESIRE_ALIGN_IF_TARGET,
frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
xtensa_move_labels (frag_now, 0, TRUE);
+ }
+
+ /* We need to mark the following properties even if we aren't aligning. */
+
+ /* If the label is already known to be a branch target, i.e., a
+ forward branch, mark the frag accordingly. Backward branches
+ are handled by xg_add_branch_and_loop_targets. */
+ if (symbol_get_tc (sym)->is_branch_target)
+ symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+
+ /* Loops only go forward, so they can be identified here. */
+ if (symbol_get_tc (sym)->is_loop_target)
+ symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
+}
+
+
+/* tc_unrecognized_line hook */
- /* If the label is already known to be a branch target, i.e., a
- forward branch, mark the frag accordingly. Backward branches
- are handled by xg_add_branch_and_loop_targets. */
- if (symbol_get_tc (sym)->is_branch_target)
- symbol_get_frag (sym)->tc_frag_data.is_branch_target = TRUE;
+int
+xtensa_unrecognized_line (ch)
+ int ch;
+{
+ switch (ch)
+ {
+ case '{' :
+ if (cur_vinsn.inside_bundle == 0)
+ {
+ /* PR8110: Cannot emit line number info inside a FLIX bundle
+ when using --gstabs. Temporarily disable debug info. */
+ generate_lineno_debug ();
+ if (debug_type == DEBUG_STABS)
+ {
+ xt_saved_debug_type = debug_type;
+ debug_type = DEBUG_NONE;
+ }
+
+ cur_vinsn.inside_bundle = 1;
+ }
+ else
+ {
+ as_bad (_("extra opening brace"));
+ return 0;
+ }
+ break;
- /* Loops only go forward, so they can be identified here. */
- if (symbol_get_tc (sym)->is_loop_target)
- symbol_get_frag (sym)->tc_frag_data.is_loop_target = TRUE;
+ case '}' :
+ if (cur_vinsn.inside_bundle)
+ finish_vinsn (&cur_vinsn);
+ else
+ {
+ as_bad (_("extra closing brace"));
+ return 0;
+ }
+ break;
+ default:
+ as_bad (_("syntax error"));
+ return 0;
}
+ return 1;
}
@@ -4791,11 +5917,15 @@ xtensa_frob_label (sym)
void
xtensa_flush_pending_output ()
{
+ if (cur_vinsn.inside_bundle)
+ as_bad (_("missing closing brace"));
+
/* If there is a non-zero instruction fragment, close it. */
if (frag_now_fix () != 0 && frag_now->tc_frag_data.is_insn)
{
frag_wane (frag_now);
frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
}
frag_now->tc_frag_data.is_insn = FALSE;
@@ -4803,6 +5933,25 @@ xtensa_flush_pending_output ()
}
+/* We had an error while parsing an instruction. The string might look
+ like this: "insn arg1, arg2 }". If so, we need to see the closing
+ brace and reset some fields. Otherwise, the vinsn never gets closed
+ and the num_slots field will grow past the end of the array of slots,
+ and bad things happen. */
+
+static void
+error_reset_cur_vinsn ()
+{
+ if (cur_vinsn.inside_bundle)
+ {
+ if (*input_line_pointer == '}'
+ || *(input_line_pointer - 1) == '}'
+ || *(input_line_pointer - 2) == '}')
+ xg_clear_vinsn (&cur_vinsn);
+ }
+}
+
+
void
md_assemble (str)
char *str;
@@ -4811,25 +5960,10 @@ md_assemble (str)
char *opname;
unsigned opnamelen;
bfd_boolean has_underbar = FALSE;
- char *arg_strings[MAX_INSN_ARGS];
+ char *arg_strings[MAX_INSN_ARGS];
int num_args;
- IStack istack; /* Put instructions into here. */
TInsn orig_insn; /* Original instruction from the input. */
- int i;
- symbolS *lit_sym = NULL;
-
- if (frag_now->tc_frag_data.is_literal)
- {
- static bfd_boolean reported = 0;
- if (reported < 4)
- as_bad (_("cannot assemble '%s' into a literal fragment"), str);
- if (reported == 3)
- as_bad (_("..."));
- reported++;
- return;
- }
- istack_init (&istack);
tinsn_init (&orig_insn);
/* Split off the opcode. */
@@ -4857,41 +5991,31 @@ md_assemble (str)
orig_insn.insn_type = ITYPE_INSN;
orig_insn.ntok = 0;
- orig_insn.is_specific_opcode = (has_underbar || !use_generics ());
- specific_opcode = orig_insn.is_specific_opcode;
+ orig_insn.is_specific_opcode = (has_underbar || !use_transform ());
orig_insn.opcode = xtensa_opcode_lookup (isa, opname);
if (orig_insn.opcode == XTENSA_UNDEFINED)
{
- as_bad (_("unknown opcode %s"), opname);
- return;
- }
-
- if (frag_now_fix () != 0 && !frag_now->tc_frag_data.is_insn)
- {
- frag_wane (frag_now);
- frag_new (0);
- }
-
- if (software_a0_b_retw_interlock)
- {
- if ((get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
- && is_conditional_branch_opcode (orig_insn.opcode))
+ xtensa_format fmt = xtensa_format_lookup (isa, opname);
+ if (fmt == XTENSA_UNDEFINED)
{
- has_a0_b_retw = TRUE;
-
- /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
- After the first assembly pass we will check all of them and
- add a nop if needed. */
- frag_now->tc_frag_data.is_insn = TRUE;
- frag_var (rs_machine_dependent, 4, 4,
- RELAX_ADD_NOP_IF_A0_B_RETW,
- frag_now->fr_symbol, frag_now->fr_offset, NULL);
- frag_now->tc_frag_data.is_insn = TRUE;
- frag_var (rs_machine_dependent, 4, 4,
- RELAX_ADD_NOP_IF_A0_B_RETW,
- frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ as_bad (_("unknown opcode or format name '%s'"), opname);
+ error_reset_cur_vinsn ();
+ return;
+ }
+ if (!cur_vinsn.inside_bundle)
+ {
+ as_bad (_("format names only valid inside bundles"));
+ error_reset_cur_vinsn ();
+ return;
}
+ if (cur_vinsn.format != XTENSA_UNDEFINED)
+ as_warn (_("multiple formats specified for one bundle; using '%s'"),
+ opname);
+ cur_vinsn.format = fmt;
+ free (has_underbar ? opname - 1 : opname);
+ error_reset_cur_vinsn ();
+ return;
}
/* Special case: The call instructions should be marked "specific opcode"
@@ -4903,6 +6027,7 @@ md_assemble (str)
if (parse_arguments (&orig_insn, num_args, arg_strings))
{
as_bad (_("syntax error"));
+ error_reset_cur_vinsn ();
return;
}
@@ -4912,40 +6037,31 @@ md_assemble (str)
while (num_args-- > 0)
free (arg_strings[num_args]);
+ /* Get expressions for invisible operands. */
+ if (get_invisible_operands (&orig_insn))
+ {
+ error_reset_cur_vinsn ();
+ return;
+ }
+
/* Check for the right number and type of arguments. */
if (tinsn_check_arguments (&orig_insn))
- return;
-
- /* See if the instruction implies an aligned section. */
- if (is_entry_opcode (orig_insn.opcode) || is_loop_opcode (orig_insn.opcode))
- record_alignment (now_seg, 2);
-
- xg_add_branch_and_loop_targets (&orig_insn);
-
- /* Special cases for instructions that force an alignment... */
- if (!orig_insn.is_specific_opcode && is_loop_opcode (orig_insn.opcode))
{
- size_t max_fill;
-
- frag_now->tc_frag_data.is_insn = TRUE;
- frag_now->tc_frag_data.is_no_density = !code_density_available ();
- max_fill = get_text_align_max_fill_size
- (get_text_align_power (XTENSA_FETCH_WIDTH),
- TRUE, frag_now->tc_frag_data.is_no_density);
- frag_var (rs_machine_dependent, max_fill, max_fill,
- RELAX_ALIGN_NEXT_OPCODE, frag_now->fr_symbol,
- frag_now->fr_offset, NULL);
-
- xtensa_move_labels (frag_now, 0, FALSE);
+ error_reset_cur_vinsn ();
+ return;
}
+ dwarf2_where (&orig_insn.loc);
+
+ xg_add_branch_and_loop_targets (&orig_insn);
+
/* Special-case for "entry" instruction. */
if (is_entry_opcode (orig_insn.opcode))
{
- /* Check that the second opcode (#1) is >= 16. */
- if (orig_insn.ntok >= 2)
+ /* Check that the third opcode (#2) is >= 16. */
+ if (orig_insn.ntok >= 3)
{
- expressionS *exp = &orig_insn.tok[1];
+ expressionS *exp = &orig_insn.tok[2];
switch (exp->X_op)
{
case O_constant:
@@ -4957,117 +6073,63 @@ md_assemble (str)
as_warn (_("entry instruction with non-constant decrement"));
}
}
-
- if (!orig_insn.is_specific_opcode)
- {
- xtensa_mark_literal_pool_location ();
-
- /* Automatically align ENTRY instructions. */
- xtensa_move_labels (frag_now, 0, TRUE);
- frag_align (2, 0, 0);
- }
}
- /* Any extra alignment frags have been inserted now, and we're about to
- emit a new instruction so clear the list of labels. */
- xtensa_clear_insn_labels ();
-
- if (software_a0_b_retw_interlock)
- set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
- is_register_writer (&orig_insn, "a", 0));
-
- set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
- is_bad_loopend_opcode (&orig_insn));
-
/* Finish it off:
- assemble_tokens (opcode, tok, ntok);
- expand the tokens from the orig_insn into the
- stack of instructions that will not expand
+ assemble_tokens (opcode, tok, ntok);
+ expand the tokens from the orig_insn into the
+ stack of instructions that will not expand
unless required at relaxation time. */
- if (xg_expand_assembly_insn (&istack, &orig_insn))
- return;
- for (i = 0; i < istack.ninsn; i++)
+ if (!cur_vinsn.inside_bundle)
+ emit_single_op (&orig_insn);
+ else /* We are inside a bundle. */
{
- TInsn *insn = &istack.insn[i];
- if (insn->insn_type == ITYPE_LITERAL)
- {
- assert (lit_sym == NULL);
- lit_sym = xg_assemble_literal (insn);
- }
- else
- {
- if (lit_sym)
- xg_resolve_literals (insn, lit_sym);
- xg_assemble_tokens (insn);
- }
+ cur_vinsn.slots[cur_vinsn.num_slots] = orig_insn;
+ cur_vinsn.num_slots++;
+ if (*input_line_pointer == '}'
+ || *(input_line_pointer - 1) == '}'
+ || *(input_line_pointer - 2) == '}')
+ finish_vinsn (&cur_vinsn);
}
- /* Now, if the original opcode was a call... */
- if (align_targets && is_call_opcode (orig_insn.opcode))
- {
- frag_now->tc_frag_data.is_insn = TRUE;
- frag_var (rs_machine_dependent, 4, 4,
- RELAX_DESIRE_ALIGN,
- frag_now->fr_symbol,
- frag_now->fr_offset,
- NULL);
- }
+ /* We've just emitted a new instruction so clear the list of labels. */
+ xtensa_clear_insn_labels ();
}
-/* TC_CONS_FIX_NEW hook: Check for "@PLT" suffix on symbol references.
- If found, use an XTENSA_PLT reloc for 4-byte values. Otherwise, this
- is the same as the standard code in read.c. */
+/* HANDLE_ALIGN hook */
-void
-xtensa_cons_fix_new (frag, where, size, exp)
- fragS *frag;
- int where;
- int size;
- expressionS *exp;
-{
- bfd_reloc_code_real_type r;
- bfd_boolean plt = FALSE;
+/* For a .align directive, we mark the previous block with the alignment
+ information. This will be placed in the object file in the
+ property section corresponding to this section. */
- if (*input_line_pointer == '@')
+void
+xtensa_handle_align (fragP)
+ fragS *fragP;
+{
+ if (linkrelax
+ && !get_frag_is_literal (fragP)
+ && (fragP->fr_type == rs_align
+ || fragP->fr_type == rs_align_code)
+ && fragP->fr_address + fragP->fr_fix > 0
+ && fragP->fr_offset > 0
+ && now_seg != bss_section)
{
- if (!strncmp (input_line_pointer, PLT_SUFFIX, strlen (PLT_SUFFIX) - 1)
- && !strncmp (input_line_pointer, plt_suffix,
- strlen (plt_suffix) - 1))
- {
- as_bad (_("undefined @ suffix '%s', expected '%s'"),
- input_line_pointer, plt_suffix);
- ignore_rest_of_line ();
- return;
- }
-
- input_line_pointer += strlen (plt_suffix);
- plt = TRUE;
+ fragP->tc_frag_data.is_align = TRUE;
+ fragP->tc_frag_data.alignment = fragP->fr_offset;
}
- switch (size)
+ if (fragP->fr_type == rs_align_test)
{
- case 1:
- r = BFD_RELOC_8;
- break;
- case 2:
- r = BFD_RELOC_16;
- break;
- case 4:
- r = plt ? BFD_RELOC_XTENSA_PLT : BFD_RELOC_32;
- break;
- case 8:
- r = BFD_RELOC_64;
- break;
- default:
- as_bad (_("unsupported BFD relocation size %u"), size);
- r = BFD_RELOC_32;
- break;
+ int count;
+ count = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ if (count != 0)
+ as_bad_where (fragP->fr_file, fragP->fr_line,
+ _("unaligned entry instruction"));
}
- fix_new_exp (frag, where, size, exp, 0, r);
}
-
+
/* TC_FRAG_INIT hook */
@@ -5075,7 +6137,7 @@ void
xtensa_frag_init (frag)
fragS *frag;
{
- frag->tc_frag_data.is_no_density = !code_density_available ();
+ xtensa_set_frag_assembly_state (frag);
}
@@ -5104,11 +6166,15 @@ md_pcrel_from (fixP)
{
char *insn_p;
static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
int opnum;
- xtensa_operand operand;
+ uint32 opnd_value;
xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
xtensa_isa isa = xtensa_default_isa;
valueT addr = fixP->fx_where + fixP->fx_frag->fr_address;
+ bfd_boolean alt_reloc;
if (fixP->fx_done)
return addr;
@@ -5117,31 +6183,38 @@ md_pcrel_from (fixP)
return addr;
if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
insn_p = &fixP->fx_frag->fr_literal[fixP->fx_where];
- xtensa_insnbuf_from_chars (isa, insnbuf, insn_p);
- opcode = xtensa_decode_insn (isa, insnbuf);
-
- 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));
+ xtensa_insnbuf_from_chars (isa, insnbuf, insn_p, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("bad instruction format"));
+
+ if (decode_reloc (fixP->fx_r_type, &slot, &alt_reloc) != 0)
+ as_fatal (_("invalid relocation"));
+
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+ /* Check for "alternate" relocation (operand not specified). */
+ if (alt_reloc || opcode == xtensa_const16_opcode)
+ {
+ if (opcode != xtensa_l32r_opcode
+ && opcode != xtensa_const16_opcode)
+ as_fatal (_("invalid relocation for '%s' instruction"),
+ xtensa_opcode_name (isa, opcode));
return addr;
}
- if (!operand_is_pcrel_label (operand))
+ opnum = get_relaxable_immed (opcode);
+ opnd_value = 0;
+ if (xtensa_operand_is_PCrelative (isa, opcode, opnum) != 1
+ || xtensa_operand_do_reloc (isa, opcode, opnum, &opnd_value, addr))
{
as_bad_where (fixP->fx_file,
fixP->fx_line,
@@ -5149,27 +6222,74 @@ md_pcrel_from (fixP)
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 - opnd_value;
+}
+
+
+/* TC_FORCE_RELOCATION hook */
+
+int
+xtensa_force_relocation (fix)
+ fixS *fix;
+{
+ switch (fix->fx_r_type)
+ {
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ case BFD_RELOC_VTABLE_INHERIT:
+ case BFD_RELOC_VTABLE_ENTRY:
+ return 1;
+ default:
+ break;
}
- return 0 - xtensa_operand_do_reloc (operand, 0, addr);
+ if (linkrelax && fix->fx_addsy
+ && relaxable_section (S_GET_SEGMENT (fix->fx_addsy)))
+ return 1;
+
+ return generic_force_reloc (fix);
+}
+
+
+/* NO_PSEUDO_DOT hook */
+
+/* This function has nothing to do with pseudo dots, but this is the
+ nearest macro to where the check needs to take place. FIXME: This
+ seems wrong. */
+
+bfd_boolean
+xtensa_check_inside_bundle ()
+{
+ if (cur_vinsn.inside_bundle && input_line_pointer[-1] == '.')
+ as_bad (_("directives are not valid inside bundles"));
+
+ /* This function must always return FALSE because it is called via a
+ macro that has nothing to do with bundling. */
+ return FALSE;
}
-/* tc_symbol_new_hook */
+/* md_elf_section_change_hook */
void
-xtensa_symbol_new_hook (symbolP)
- symbolS *symbolP;
+xtensa_elf_section_change_hook ()
{
- symbol_get_tc (symbolP)->plt = 0;
+ /* Set up the assembly state. */
+ if (!frag_now->tc_frag_data.is_assembly_state_set)
+ xtensa_set_frag_assembly_state (frag_now);
}
@@ -5179,12 +6299,34 @@ bfd_boolean
xtensa_fix_adjustable (fixP)
fixS *fixP;
{
+ /* An offset is not allowed in combination with the difference of two
+ symbols, but that cannot be easily detected after a local symbol
+ has been adjusted to a (section+offset) form. Return 0 so that such
+ an fix will not be adjusted. */
+ if (fixP->fx_subsy && fixP->fx_addsy && fixP->fx_offset
+ && relaxable_section (S_GET_SEGMENT (fixP->fx_subsy)))
+ return 0;
+
/* We need the symbol name for the VTABLE entries. */
if (fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
|| fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
return 0;
+ if (fixP->fx_addsy
+ && (S_IS_EXTERNAL (fixP->fx_addsy) || S_IS_WEAK (fixP->fx_addsy)))
+ return 0;
+
+#if 0
+ /* We may someday want to enable this code to preserve relocations for
+ non-PC-relative fixes, possibly under control of a PIC flag. */
+ return (fixP->fx_pcrel
+ || (fixP->fx_subsy != NULL
+ && (S_GET_SEGMENT (fixP->fx_subsy)
+ == S_GET_SEGMENT (fixP->fx_addsy)))
+ || S_IS_LOCAL (fixP->fx_addsy));
+#else
return 1;
+#endif
}
@@ -5196,14 +6338,7 @@ md_apply_fix3 (fixP, valP, seg)
{
if (fixP->fx_pcrel == 0 && fixP->fx_addsy == 0)
{
- /* This happens when the relocation is within the current section.
- It seems this implies a PCREL operation. We'll catch it and error
- if not. */
-
char *const fixpos = fixP->fx_frag->fr_literal + fixP->fx_where;
- static xtensa_insnbuf insnbuf = NULL;
- xtensa_opcode opcode;
- xtensa_isa isa;
switch (fixP->fx_r_type)
{
@@ -5224,26 +6359,6 @@ md_apply_fix3 (fixP, valP, seg)
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;
@@ -5308,7 +6423,7 @@ md_estimate_size_before_relax (fragP, seg)
fragS *fragP;
segT seg ATTRIBUTE_UNUSED;
{
- return fragP->tc_frag_data.text_expansion;
+ return fragP->tc_frag_data.text_expansion[0];
}
@@ -5317,10 +6432,11 @@ md_estimate_size_before_relax (fragP, seg)
arelent *
tc_gen_reloc (section, fixp)
- asection *section ATTRIBUTE_UNUSED;
+ asection *section;
fixS *fixp;
{
arelent *reloc;
+ bfd_boolean apply_tentative_value = FALSE;
reloc = (arelent *) xmalloc (sizeof (arelent));
reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
@@ -5331,45 +6447,1346 @@ tc_gen_reloc (section, fixp)
They'd better have been fully resolved by this point. */
assert ((int) fixp->fx_r_type > 0);
+ if (linkrelax && fixp->fx_subsy
+ && (fixp->fx_r_type == BFD_RELOC_8
+ || fixp->fx_r_type == BFD_RELOC_16
+ || fixp->fx_r_type == BFD_RELOC_32))
+ {
+ int diff_size = 0;
+ bfd_vma diff_value, diff_mask = 0;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_8:
+ fixp->fx_r_type = BFD_RELOC_XTENSA_DIFF8;
+ diff_size = 1;
+ diff_mask = 0xff;
+ break;
+ case BFD_RELOC_16:
+ fixp->fx_r_type = BFD_RELOC_XTENSA_DIFF16;
+ diff_size = 2;
+ diff_mask = 0xffff;
+ break;
+ case BFD_RELOC_32:
+ fixp->fx_r_type = BFD_RELOC_XTENSA_DIFF32;
+ diff_size = 4;
+ diff_mask = 0xffffffff;
+ break;
+ default:
+ break;
+ }
+
+ /* An offset is only allowed when it results from adjusting a local
+ symbol into a section-relative offset. If the offset came from the
+ original expression, tc_fix_adjustable will have prevented the fix
+ from being converted to a section-relative form so that we can flag
+ the error here. */
+ if (fixp->fx_offset != 0 && !symbol_section_p (fixp->fx_addsy))
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("cannot represent subtraction with an offset"));
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
+ return NULL;
+ }
+
+ assert (S_GET_SEGMENT (fixp->fx_addsy)
+ == S_GET_SEGMENT (fixp->fx_subsy));
+
+ diff_value = (S_GET_VALUE (fixp->fx_addsy) + fixp->fx_offset
+ - S_GET_VALUE (fixp->fx_subsy));
+
+ /* Check for overflow. */
+ if ((diff_value & ~diff_mask) != 0)
+ {
+ as_bad_where (fixp->fx_file, fixp->fx_line,
+ _("value of %ld too large"), diff_value);
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
+ return NULL;
+ }
+
+ md_number_to_chars (fixp->fx_frag->fr_literal + fixp->fx_where,
+ diff_value, diff_size);
+ reloc->addend = fixp->fx_offset - diff_value;
+ }
+ else
+ {
+ reloc->addend = fixp->fx_offset;
+
+ switch (fixp->fx_r_type)
+ {
+ case BFD_RELOC_XTENSA_SLOT0_OP:
+ case BFD_RELOC_XTENSA_SLOT1_OP:
+ case BFD_RELOC_XTENSA_SLOT2_OP:
+ case BFD_RELOC_XTENSA_SLOT3_OP:
+ case BFD_RELOC_XTENSA_SLOT4_OP:
+ case BFD_RELOC_XTENSA_SLOT5_OP:
+ case BFD_RELOC_XTENSA_SLOT6_OP:
+ case BFD_RELOC_XTENSA_SLOT7_OP:
+ case BFD_RELOC_XTENSA_SLOT8_OP:
+ case BFD_RELOC_XTENSA_SLOT9_OP:
+ case BFD_RELOC_XTENSA_SLOT10_OP:
+ case BFD_RELOC_XTENSA_SLOT11_OP:
+ case BFD_RELOC_XTENSA_SLOT12_OP:
+ case BFD_RELOC_XTENSA_SLOT13_OP:
+ case BFD_RELOC_XTENSA_SLOT14_OP:
+ /* As a special case, the immediate value for a CONST16 opcode
+ should not be applied, since this kind of relocation is
+ handled specially for CONST16 and is not really PC-relative.
+ Rather than decode the opcode here, just wait and handle it
+ in xg_apply_tentative_value. */
+ apply_tentative_value = TRUE;
+ break;
+
+ case BFD_RELOC_XTENSA_SLOT0_ALT:
+ case BFD_RELOC_XTENSA_SLOT1_ALT:
+ case BFD_RELOC_XTENSA_SLOT2_ALT:
+ case BFD_RELOC_XTENSA_SLOT3_ALT:
+ case BFD_RELOC_XTENSA_SLOT4_ALT:
+ case BFD_RELOC_XTENSA_SLOT5_ALT:
+ case BFD_RELOC_XTENSA_SLOT6_ALT:
+ case BFD_RELOC_XTENSA_SLOT7_ALT:
+ case BFD_RELOC_XTENSA_SLOT8_ALT:
+ case BFD_RELOC_XTENSA_SLOT9_ALT:
+ case BFD_RELOC_XTENSA_SLOT10_ALT:
+ case BFD_RELOC_XTENSA_SLOT11_ALT:
+ case BFD_RELOC_XTENSA_SLOT12_ALT:
+ case BFD_RELOC_XTENSA_SLOT13_ALT:
+ case BFD_RELOC_XTENSA_SLOT14_ALT:
+ 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"));
+ }
+ }
+
reloc->howto = bfd_reloc_type_lookup (stdoutput, fixp->fx_r_type);
if (reloc->howto == NULL)
{
as_bad_where (fixp->fx_file, fixp->fx_line,
_("cannot represent `%s' relocation in object file"),
bfd_get_reloc_code_name (fixp->fx_r_type));
+ free (reloc->sym_ptr_ptr);
+ free (reloc);
return NULL;
}
if (!fixp->fx_pcrel != !reloc->howto->pc_relative)
+ as_fatal (_("internal error? cannot generate `%s' relocation"),
+ bfd_get_reloc_code_name (fixp->fx_r_type));
+
+ /* Write the tentative value of a PC-relative relocation to a local symbol
+ into the instruction. The value will be ignored by the linker, and it
+ makes the object file disassembly readable when the linkrelax flag is
+ set and all branch targets are encoded in relocations. */
+
+ if (linkrelax && apply_tentative_value && fixp->fx_pcrel)
{
- as_fatal (_("internal error? cannot generate `%s' relocation"),
- bfd_get_reloc_code_name (fixp->fx_r_type));
+ valueT val;
+ assert (fixp->fx_addsy);
+ if (S_GET_SEGMENT (fixp->fx_addsy) == section && !fixp->fx_plt
+ && !S_FORCE_RELOC (fixp->fx_addsy, 1))
+ {
+ val = (S_GET_VALUE (fixp->fx_addsy) + fixp->fx_offset
+ - md_pcrel_from (fixp));
+ xg_apply_tentative_value (fixp, val);
+ }
}
- assert (!fixp->fx_pcrel == !reloc->howto->pc_relative);
- reloc->addend = fixp->fx_offset;
+ return reloc;
+}
+
+
+/* md_post_relax_hook */
+
+#define XTENSA_INSN_SEC_NAME ".xt.insn"
+#define XTENSA_LIT_SEC_NAME ".xt.lit"
+#define XTENSA_PROP_SEC_NAME ".xt.prop"
+
+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);
- switch (fixp->fx_r_type)
+ xtensa_find_unmarked_state_frags ();
+
+ if (use_literal_section)
+ xtensa_create_property_segments (get_frag_is_literal,
+ NULL,
+ XTENSA_LIT_SEC_NAME,
+ xt_literal_sec);
+ xtensa_create_xproperty_segments (get_frag_property_flags,
+ XTENSA_PROP_SEC_NAME,
+ xt_prop_sec);
+
+ if (warn_unaligned_branch_targets)
+ bfd_map_over_sections (stdoutput, xtensa_find_unaligned_branch_targets, 0);
+ bfd_map_over_sections (stdoutput, xtensa_find_unaligned_loops, 0);
+}
+
+
+/* We have reached the end of a bundle; emit into the frag. */
+
+static void
+finish_vinsn (vinsn)
+ vliw_insn *vinsn;
+{
+ IStack slotstack;
+ int i;
+ char *file_name;
+ int line;
+
+ if (find_vinsn_conflicts (vinsn))
+ return;
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ if (vinsn->format == XTENSA_UNDEFINED)
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line,
+ _("couldn't find a valid instruction format"));
+ fprintf (stderr, _(" ops were: "));
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, _(" %s;"),
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, _("\n"));
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ if (vinsn->num_slots
+ != xtensa_format_num_slots (xtensa_default_isa, vinsn->format))
{
- 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;
+ as_bad (_("format '%s' allows %d slots, but there are %d opcodes"),
+ xtensa_format_name (xtensa_default_isa, vinsn->format),
+ xtensa_format_num_slots (xtensa_default_isa, vinsn->format),
+ vinsn->num_slots);
+ xg_clear_vinsn (vinsn);
+ return;
+ }
- case BFD_RELOC_XTENSA_ASM_SIMPLIFY:
- as_warn (_("emitting simplification relocation"));
- break;
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
- default:
- as_warn (_("emitting unknown relocation"));
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (vinsn->slots[i].opcode != XTENSA_UNDEFINED)
+ {
+ symbolS *lit_sym = NULL;
+ int j;
+ bfd_boolean e = FALSE;
+ bfd_boolean saved_density = density_supported;
+
+ /* We don't want to narrow ops inside multi-slot bundles. */
+ if (vinsn->num_slots > 1)
+ density_supported = FALSE;
+
+ istack_init (&slotstack);
+ if (vinsn->slots[i].opcode == xtensa_nop_opcode)
+ {
+ vinsn->slots[i].opcode =
+ xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+ vinsn->slots[i].ntok = 0;
+ }
+
+ if (xg_expand_assembly_insn (&slotstack, &vinsn->slots[i]))
+ {
+ e = TRUE;
+ continue;
+ }
+
+ density_supported = saved_density;
+
+ if (e)
+ {
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ for (j = 0; j < slotstack.ninsn - 1; j++)
+ {
+ TInsn *insn = &slotstack.insn[j];
+ 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);
+ emit_single_op (insn);
+ }
+ }
+
+ if (vinsn->num_slots > 1)
+ {
+ if (opcode_fits_format_slot
+ (slotstack.insn[slotstack.ninsn - 1].opcode,
+ vinsn->format, i))
+ {
+ vinsn->slots[i] = slotstack.insn[slotstack.ninsn - 1];
+ }
+ else
+ {
+ bundle_single_op (&slotstack.insn[slotstack.ninsn - 1]);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->slots[i].opcode = xtensa_nop_opcode;
+ else
+ vinsn->slots[i].opcode
+ = xtensa_format_slot_nop_opcode (xtensa_default_isa,
+ vinsn->format, i);
+
+ vinsn->slots[i].ntok = 0;
+ }
+ }
+ else
+ {
+ vinsn->slots[0] = slotstack.insn[slotstack.ninsn - 1];
+ vinsn->format = XTENSA_UNDEFINED;
+ }
+ }
}
- return reloc;
+ /* Now check resource conflicts on the modified bundle. */
+ if (resources_conflict (vinsn))
+ {
+ as_where (&file_name, &line);
+ as_bad_where (file_name, line, _("illegal resource usage in bundle"));
+ fprintf (stderr, " ops were: ");
+ for (i = 0; i < vinsn->num_slots; i++)
+ fprintf (stderr, " %s;",
+ xtensa_opcode_name (xtensa_default_isa,
+ vinsn->slots[i].opcode));
+ fprintf (stderr, "\n");
+ xg_clear_vinsn (vinsn);
+ return;
+ }
+
+ /* First, find a format that works. */
+ if (vinsn->format == XTENSA_UNDEFINED)
+ vinsn->format = xg_find_narrowest_format (vinsn);
+
+ xg_assemble_vliw_tokens (vinsn);
+
+ xg_clear_vinsn (vinsn);
+}
+
+
+/* Given an vliw instruction, what conflicts are there in register
+ usage and in writes to states and queues?
+
+ This function does two things:
+ 1. Reports an error when a vinsn contains illegal combinations
+ of writes to registers states or queues.
+ 2. Marks individual tinsns as not relaxable if the combination
+ contains antidependencies.
+
+ Job 2 handles things like swap semantics in instructions that need
+ to be relaxed. For example,
+
+ addi a0, a1, 100000
+
+ normally would be relaxed to
+
+ l32r a0, some_label
+ add a0, a1, a0
+
+ _but_, if the above instruction is bundled with an a0 reader, e.g.,
+
+ { addi a0, a1, 10000 ; add a2, a0, a4 ; }
+
+ then we can't relax it into
+
+ l32r a0, some_label
+ { add a0, a1, a0 ; add a2, a0, a4 ; }
+
+ because the value of a0 is trashed before the second add can read it. */
+
+static bfd_boolean
+find_vinsn_conflicts (vinsn)
+ vliw_insn *vinsn;
+{
+ int i, j;
+ int branches = 0;
+ xtensa_isa isa = xtensa_default_isa;
+
+ assert (!past_xtensa_end);
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+ if (op1->is_specific_opcode)
+ op1->keep_wide = TRUE;
+ else
+ op1->keep_wide = FALSE;
+ }
+
+ for (i = 0 ; i < vinsn->num_slots; i++)
+ {
+ TInsn *op1 = &vinsn->slots[i];
+
+ if (xtensa_opcode_is_branch (isa, op1->opcode) == 1)
+ branches++;
+
+ for (j = 0; j < vinsn->num_slots; j++)
+ {
+ if (i != j)
+ {
+ TInsn *op2 = &vinsn->slots[j];
+ char conflict_type = check_t1_t2_reads_and_writes (op1, op2);
+ switch (conflict_type)
+ {
+ case 'c':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same register"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'd':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same state"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'e':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) write the same queue"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ case 'f':
+ as_bad (_("opcodes '%s' (slot %d) and '%s' (slot %d) both have volatile queue accesses"),
+ xtensa_opcode_name (isa, op1->opcode), i,
+ xtensa_opcode_name (isa, op2->opcode), j);
+ return TRUE;
+ default:
+ /* Everything is OK. */
+ break;
+ }
+ op2->is_specific_opcode = (op2->is_specific_opcode
+ || conflict_type == 'a');
+ }
+ }
+ }
+
+ if (branches > 1)
+ {
+ as_bad (_("multiple branches or jumps in the same bundle"));
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+/* Check how the result registers of t1 and t2 relate.
+ Cases found are:
+
+ case A: t1 reads a register t2 writes (an antidependency within a bundle)
+ case B: no relationship between what is read and written (both could
+ read the same reg though)
+ case C: t1 writes a register t2 writes (a register conflict within a
+ bundle)
+ case D: t1 writes a state that t2 also writes
+ case E: t1 writes a tie queue that t2 also writes
+ case F: two volatile queue writes
+*/
+
+static char
+check_t1_t2_reads_and_writes (t1, t2)
+ TInsn *t1;
+ TInsn *t2;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_regfile t1_regfile, t2_regfile;
+ int t1_reg, t2_reg;
+ int t1_base_reg, t1_last_reg;
+ int t2_base_reg, t2_last_reg;
+ char t1_inout, t2_inout;
+ int i, j;
+ char conflict = 'b';
+ int t1_states;
+ int t2_states;
+ int t1_interfaces;
+ int t2_interfaces;
+ bfd_boolean t1_volatile = FALSE;
+ bfd_boolean t2_volatile = FALSE;
+
+ /* Check registers. */
+ for (j = 0; j < t2->ntok; j++)
+ {
+ if (xtensa_operand_is_register (isa, t2->opcode, j) != 1)
+ continue;
+
+ t2_regfile = xtensa_operand_regfile (isa, t2->opcode, j);
+ t2_base_reg = t2->tok[j].X_add_number;
+ t2_last_reg = t2_base_reg + xtensa_operand_num_regs (isa, t2->opcode, j);
+
+ for (i = 0; i < t1->ntok; i++)
+ {
+ if (xtensa_operand_is_register (isa, t1->opcode, i) != 1)
+ continue;
+
+ t1_regfile = xtensa_operand_regfile (isa, t1->opcode, i);
+
+ if (t1_regfile != t2_regfile)
+ continue;
+
+ t1_inout = xtensa_operand_inout (isa, t1->opcode, i);
+ t2_inout = xtensa_operand_inout (isa, t2->opcode, j);
+
+ if (xtensa_operand_is_known_reg (isa, t1->opcode, i) == 0
+ || xtensa_operand_is_known_reg (isa, t2->opcode, j) == 0)
+ {
+ if (t1_inout == 'm' || t1_inout == 'o'
+ || t2_inout == 'm' || t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+ }
+
+ t1_base_reg = t1->tok[i].X_add_number;
+ t1_last_reg = (t1_base_reg
+ + xtensa_operand_num_regs (isa, t1->opcode, i));
+
+ for (t1_reg = t1_base_reg; t1_reg < t1_last_reg; t1_reg++)
+ {
+ for (t2_reg = t2_base_reg; t2_reg < t2_last_reg; t2_reg++)
+ {
+ if (t1_reg != t2_reg)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'c';
+ }
+ }
+ }
+ }
+
+ /* Check states. */
+ t1_states = xtensa_opcode_num_stateOperands (isa, t1->opcode);
+ t2_states = xtensa_opcode_num_stateOperands (isa, t2->opcode);
+ for (j = 0; j < t2_states; j++)
+ {
+ xtensa_state t2_so = xtensa_stateOperand_state (isa, t2->opcode, j);
+ t2_inout = xtensa_stateOperand_inout (isa, t2->opcode, j);
+ for (i = 0; i < t1_states; i++)
+ {
+ xtensa_state t1_so = xtensa_stateOperand_state (isa, t1->opcode, i);
+ t1_inout = xtensa_stateOperand_inout (isa, t1->opcode, i);
+ if (t1_so != t2_so)
+ continue;
+
+ if (t2_inout == 'i' && (t1_inout == 'm' || t1_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && (t2_inout == 'm' || t2_inout == 'o'))
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'd';
+ }
+ }
+
+ /* Check tieports. */
+ t1_interfaces = xtensa_opcode_num_interfaceOperands (isa, t1->opcode);
+ t2_interfaces = xtensa_opcode_num_interfaceOperands (isa, t2->opcode);
+ for (j = 0; j < t2_interfaces; j++)
+ {
+ xtensa_interface t2_int
+ = xtensa_interfaceOperand_interface (isa, t2->opcode, j);
+ t2_inout = xtensa_interface_inout (isa, j);
+ if (xtensa_interface_has_side_effect (isa, t2_int) == 1
+ && t2_inout != 'i')
+ t2_volatile = TRUE;
+ for (i = 0; i < t1_interfaces; i++)
+ {
+ xtensa_interface t1_int
+ = xtensa_interfaceOperand_interface (isa, t1->opcode, j);
+ t1_inout = xtensa_interface_inout (isa, i);
+ if (xtensa_interface_has_side_effect (isa, t1_int) == 1
+ && t1_inout != 'i')
+ t1_volatile = TRUE;
+
+ if (t1_int != t2_int)
+ continue;
+
+ if (t2_inout == 'i' && t1_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout == 'i' && t2_inout == 'o')
+ {
+ conflict = 'a';
+ continue;
+ }
+
+ if (t1_inout != 'i' && t2_inout != 'i')
+ return 'e';
+ }
+ }
+
+ if (t1_volatile && t2_volatile)
+ return 'f';
+
+ return conflict;
+}
+
+
+/* The func unit stuff could be implemented as bit-vectors rather
+ than the iterative approach here. If it ends up being too
+ slow, we will switch it. */
+
+resource_table *
+new_resource_table (data, cycles, nu, uncf, onuf, ouuf, ousf)
+ void *data;
+ int cycles;
+ int nu;
+ unit_num_copies_func uncf;
+ opcode_num_units_func onuf;
+ opcode_funcUnit_use_unit_func ouuf;
+ opcode_funcUnit_use_stage_func ousf;
+{
+ int i;
+ resource_table *rt = (resource_table *) xmalloc (sizeof (resource_table));
+ rt->data = data;
+ rt->cycles = cycles;
+ rt->allocated_cycles = cycles;
+ rt->num_units = nu;
+ rt->unit_num_copies = uncf;
+ rt->opcode_num_units = onuf;
+ rt->opcode_unit_use = ouuf;
+ rt->opcode_unit_stage = ousf;
+
+ rt->units = (char **) xcalloc (cycles, sizeof (char *));
+ for (i = 0; i < cycles; i++)
+ rt->units[i] = (char *) xcalloc (nu, sizeof (char));
+
+ return rt;
+}
+
+
+void
+clear_resource_table (rt)
+ resource_table *rt;
+{
+ int i, j;
+ for (i = 0; i < rt->allocated_cycles; i++)
+ for (j = 0; j < rt->num_units; j++)
+ rt->units[i][j] = 0;
+}
+
+
+/* We never shrink it, just fake it into thinking so. */
+
+void
+resize_resource_table (rt, cycles)
+ resource_table *rt;
+ int cycles;
+{
+ int i, old_cycles;
+
+ rt->cycles = cycles;
+ if (cycles <= rt->allocated_cycles)
+ return;
+
+ old_cycles = rt->allocated_cycles;
+ rt->allocated_cycles = cycles;
+
+ rt->units = xrealloc (rt->units, sizeof (char *) * rt->allocated_cycles);
+ for (i = 0; i < old_cycles; i++)
+ rt->units[i] = xrealloc (rt->units[i], sizeof (char) * rt->num_units);
+ for (i = old_cycles; i < cycles; i++)
+ rt->units[i] = xcalloc (rt->num_units, sizeof (char));
+}
+
+
+bfd_boolean
+resources_available (rt, opcode, cycle)
+ resource_table *rt;
+ xtensa_opcode opcode;
+ int cycle;
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ int copies_in_use = rt->units[stage + cycle][unit];
+ int copies = (rt->unit_num_copies) (rt->data, unit);
+ if (copies_in_use >= copies)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+void
+reserve_resources (rt, opcode, cycle)
+ resource_table *rt;
+ xtensa_opcode opcode;
+ int cycle;
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ /* Note that this allows resources to be oversubscribed. That's
+ essential to the way the optional scheduler works.
+ resources_available reports when a resource is over-subscribed,
+ so it's easy to tell. */
+ rt->units[stage + cycle][unit]++;
+ }
+}
+
+
+void
+release_resources (rt, opcode, cycle)
+ resource_table *rt;
+ xtensa_opcode opcode;
+ int cycle;
+{
+ int i;
+ int uses = (rt->opcode_num_units) (rt->data, opcode);
+
+ for (i = 0; i < uses; i++)
+ {
+ xtensa_funcUnit unit = (rt->opcode_unit_use) (rt->data, opcode, i);
+ int stage = (rt->opcode_unit_stage) (rt->data, opcode, i);
+ rt->units[stage + cycle][unit]--;
+ assert (rt->units[stage + cycle][unit] >= 0);
+ }
+}
+
+
+/* Wrapper functions make parameterized resource reservation
+ more convenient. */
+
+int
+opcode_funcUnit_use_unit (data, opcode, idx)
+ void *data;
+ xtensa_opcode opcode;
+ int idx;
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->unit;
+}
+
+
+int
+opcode_funcUnit_use_stage (data, opcode, idx)
+ void *data;
+ xtensa_opcode opcode;
+ int idx;
+{
+ xtensa_funcUnit_use *use = xtensa_opcode_funcUnit_use (data, opcode, idx);
+ return use->stage;
+}
+
+
+/* Note that this function does not check issue constraints, but
+ solely whether the hardware is available to execute the given
+ instructions together. It also doesn't check if the tinsns
+ write the same state, or access the same tieports. That is
+ checked by check_t1_t2_read_write. */
+
+static bfd_boolean
+resources_conflict (vinsn)
+ vliw_insn *vinsn;
+{
+ int i;
+ static resource_table *rt = NULL;
+
+ /* This is the most common case by far. Optimize it. */
+ if (vinsn->num_slots == 1)
+ return FALSE;
+
+ if (rt == NULL)
+ {
+ xtensa_isa isa = xtensa_default_isa;
+ rt = new_resource_table
+ (isa, xtensa_isa_num_pipe_stages (isa),
+ xtensa_isa_num_funcUnits (isa),
+ (unit_num_copies_func) xtensa_funcUnit_num_copies,
+ (opcode_num_units_func) xtensa_opcode_num_funcUnit_uses,
+ opcode_funcUnit_use_unit,
+ opcode_funcUnit_use_stage);
+ }
+
+ clear_resource_table (rt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ if (!resources_available (rt, vinsn->slots[i].opcode, 0))
+ return TRUE;
+ reserve_resources (rt, vinsn->slots[i].opcode, 0);
+ }
+
+ return FALSE;
+}
+
+
+static xtensa_format
+xg_find_narrowest_format (vinsn)
+ vliw_insn *vinsn;
+{
+ /* Right now we assume that the ops within the vinsn are properly
+ ordered for the slots that the programmer wanted them in. In
+ other words, we don't rearrange the ops in hopes of finding a
+ better format. The scheduler handles that. */
+
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format format;
+ vliw_insn v_copy = *vinsn;
+ xtensa_opcode nop_opcode = xtensa_nop_opcode;
+
+ for (format = 0; format < xtensa_isa_num_formats (isa); format++)
+ {
+ v_copy = *vinsn;
+ if (xtensa_format_num_slots (isa, format) == v_copy.num_slots)
+ {
+ int slot;
+ int fit = 0;
+ for (slot = 0; slot < v_copy.num_slots; slot++)
+ {
+ if (v_copy.slots[slot].opcode == nop_opcode)
+ {
+ v_copy.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, format, slot);
+ v_copy.slots[slot].ntok = 0;
+ }
+
+ if (opcode_fits_format_slot (v_copy.slots[slot].opcode,
+ format, slot))
+ fit++;
+ else
+ {
+ if (v_copy.num_slots > 1)
+ {
+ TInsn widened;
+ /* Try the widened version. */
+ if (!v_copy.slots[slot].keep_wide
+ && !v_copy.slots[slot].is_specific_opcode
+ && xg_is_narrow_insn (&v_copy.slots[slot])
+ && !xg_expand_narrow (&widened, &v_copy.slots[slot])
+ && opcode_fits_format_slot (widened.opcode,
+ format, slot))
+ {
+ /* The xg_is_narrow clause requires some explanation:
+
+ addi can be "widened" to an addmi, which is then
+ expanded to an addmi/addi pair if the immediate
+ requires it, but here we must have a single widen
+ only.
+
+ xg_is_narrow tells us that addi isn't really
+ narrow. The widen_spec_list says that there are
+ other cases. */
+
+ v_copy.slots[slot] = widened;
+ fit++;
+ }
+ }
+ }
+ }
+ if (fit == v_copy.num_slots)
+ {
+ *vinsn = v_copy;
+ xtensa_format_encode (isa, format, vinsn->insnbuf);
+ vinsn->format = format;
+ break;
+ }
+ }
+ }
+
+ if (format == xtensa_isa_num_formats (isa))
+ return XTENSA_UNDEFINED;
+
+ return format;
+}
+
+
+/* Return the additional space needed in a frag
+ for possible relaxations of any ops in a VLIW insn.
+ Also fill out the relaxations that might be required of
+ each tinsn in the vinsn. */
+
+static int
+relaxation_requirements (vinsn)
+ vliw_insn *vinsn;
+{
+ int extra_space = 0;
+ int slot;
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ if (!tinsn_has_symbolic_operands (tinsn))
+ {
+ /* A narrow instruction could be widened later to help
+ alignment issues. */
+ if (xg_is_narrow_insn (tinsn)
+ && !tinsn->is_specific_opcode
+ && vinsn->num_slots == 1)
+ {
+ /* Difference in bytes between narrow and wide insns... */
+ extra_space += 1;
+ tinsn->subtype = RELAX_NARROW;
+ tinsn->record_fix = TRUE;
+ break;
+ }
+ else
+ {
+ tinsn->record_fix = FALSE;
+ /* No extra_space needed. */
+ }
+ }
+ else
+ {
+ if (workaround_b_j_loop_end && is_jx_opcode (tinsn->opcode)
+ && use_transform ())
+ {
+ /* Add 2 of these. */
+ extra_space += 3; /* for the nop size */
+ tinsn->subtype = RELAX_ADD_NOP_IF_PRE_LOOP_END;
+ }
+
+ /* Need to assemble it with space for the relocation. */
+ if (xg_is_relaxable_insn (tinsn, 0)
+ && !tinsn->is_specific_opcode)
+ {
+ int max_size = xg_get_max_insn_widen_size (tinsn->opcode);
+ int max_literal_size =
+ xg_get_max_insn_widen_literal_size (tinsn->opcode);
+
+ tinsn->literal_space = max_literal_size;
+
+ tinsn->subtype = RELAX_IMMED;
+ tinsn->record_fix = FALSE;
+ extra_space += max_size;
+ }
+ else
+ {
+ tinsn->record_fix = TRUE;
+ /* No extra space needed. */
+ }
+ }
+ }
+ return extra_space;
+}
+
+
+static void
+bundle_single_op (orig_insn)
+ TInsn *orig_insn;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ vliw_insn v;
+ int slot;
+
+ xg_init_vinsn (&v);
+ v.format = op_placement_table[orig_insn->opcode].narrowest;
+ assert (v.format != XTENSA_UNDEFINED);
+ v.num_slots = xtensa_format_num_slots (isa, v.format);
+
+ for (slot = 0;
+ !opcode_fits_format_slot (orig_insn->opcode, v.format, slot);
+ slot++)
+ {
+ v.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, v.format, slot);
+ v.slots[slot].ntok = 0;
+ v.slots[slot].insn_type = ITYPE_INSN;
+ }
+
+ v.slots[slot] = *orig_insn;
+ slot++;
+
+ for ( ; slot < v.num_slots; slot++)
+ {
+ v.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, v.format, slot);
+ v.slots[slot].ntok = 0;
+ v.slots[slot].insn_type = ITYPE_INSN;
+ }
+
+ finish_vinsn (&v);
+ xg_free_vinsn (&v);
+}
+
+
+static bfd_boolean
+emit_single_op (orig_insn)
+ TInsn *orig_insn;
+{
+ int i;
+ IStack istack; /* put instructions into here */
+ symbolS *lit_sym = NULL;
+ symbolS *label_sym = NULL;
+
+ istack_init (&istack);
+
+ /* Special-case for "movi aX, foo" which is guaranteed to need relaxing.
+ Because the scheduling and bundling characteristics of movi and
+ l32r or const16 are so different, we can do much better if we relax
+ it prior to scheduling and bundling, rather than after. */
+ if (is_movi_opcode (orig_insn->opcode) && !cur_vinsn.inside_bundle
+ && (orig_insn->tok[1].X_op == O_symbol
+ || orig_insn->tok[1].X_op == O_pltrel))
+ xg_assembly_relax (&istack, orig_insn, now_seg, frag_now, 0, 1, 0);
+ else
+ if (xg_expand_assembly_insn (&istack, orig_insn))
+ return TRUE;
+
+ for (i = 0; i < istack.ninsn; i++)
+ {
+ TInsn *insn = &istack.insn[i];
+ switch (insn->insn_type)
+ {
+ case ITYPE_LITERAL:
+ assert (lit_sym == NULL);
+ lit_sym = xg_assemble_literal (insn);
+ break;
+ case ITYPE_LABEL:
+ {
+ static int relaxed_sym_idx = 0;
+ char *label = xmalloc (strlen (FAKE_LABEL_NAME) + 12);
+ sprintf (label, "%s_rl_%x", FAKE_LABEL_NAME, relaxed_sym_idx++);
+ colon (label);
+ assert (label_sym == NULL);
+ label_sym = symbol_find_or_make (label);
+ assert (label_sym);
+ free (label);
+ }
+ break;
+ case ITYPE_INSN:
+ if (lit_sym)
+ xg_resolve_literals (insn, lit_sym);
+ if (label_sym)
+ xg_resolve_labels (insn, label_sym);
+ bundle_single_op (insn);
+ break;
+ default:
+ assert (0);
+ break;
+ }
+ }
+ return FALSE;
+}
+
+
+/* Emit a vliw instruction to the current fragment. */
+
+void
+xg_assemble_vliw_tokens (vinsn)
+ vliw_insn *vinsn;
+{
+ bfd_boolean finish_frag = FALSE;
+ bfd_boolean is_jump = FALSE;
+ bfd_boolean is_branch = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
+ int i;
+ int insn_size;
+ int extra_space;
+ char *f = NULL;
+ int slot;
+ struct dwarf2_line_info best_loc;
+
+ best_loc.line = INT_MAX;
+
+ if (generating_literals)
+ {
+ static int reported = 0;
+ if (reported < 4)
+ as_bad_where (frag_now->fr_file, frag_now->fr_line,
+ _("cannot assemble into a literal fragment"));
+ if (reported == 3)
+ as_bad (_("..."));
+ reported++;
+ return;
+ }
+
+ if (frag_now_fix () != 0
+ && (!get_frag_is_insn (frag_now)
+ || (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ || !use_transform () != get_frag_is_no_transform (frag_now)
+ || (directive_state[directive_absolute_literals]
+ != frag_now->tc_frag_data.use_absolute_literals)))
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (workaround_a0_b_retw
+ && vinsn->num_slots == 1
+ && (get_last_insn_flags (now_seg, now_subseg) & FLAG_IS_A0_WRITER) != 0
+ && xtensa_opcode_is_branch (isa, vinsn->slots[0].opcode) == 1
+ && use_transform ())
+ {
+ has_a0_b_retw = TRUE;
+
+ /* Mark this fragment with the special RELAX_ADD_NOP_IF_A0_B_RETW.
+ After the first assembly pass we will check all of them and
+ add a nop if needed. */
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_A0_B_RETW,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ /* See if the instruction implies an aligned section. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[i].opcode) == 1)
+ record_alignment (now_seg, 2);
+
+ /* Also determine the best line number for debug info. */
+ best_loc = vinsn->slots[i].loc.line < best_loc.line
+ ? vinsn->slots[i].loc : best_loc;
+ }
+
+ /* Special cases for instructions that force an alignment... */
+ /* None of these opcodes are bundle-able. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode) == 1)
+ {
+ size_t max_fill;
+
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+
+ max_fill = get_text_align_max_fill_size
+ (get_text_align_power (xtensa_fetch_width),
+ TRUE, frag_now->tc_frag_data.is_no_density);
+
+ if (use_transform ())
+ frag_var (rs_machine_dependent, max_fill, max_fill,
+ RELAX_ALIGN_NEXT_OPCODE,
+ frag_now->fr_symbol,
+ frag_now->fr_offset,
+ NULL);
+ else
+ frag_var (rs_machine_dependent, 0, 0,
+ RELAX_CHECK_ALIGN_NEXT_OPCODE, 0, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+
+ xtensa_move_labels (frag_now, 0, FALSE);
+ }
+
+ if (is_entry_opcode (vinsn->slots[0].opcode)
+ && !vinsn->slots[0].is_specific_opcode)
+ {
+ xtensa_mark_literal_pool_location ();
+ xtensa_move_labels (frag_now, 0, TRUE);
+ frag_var (rs_align_test, 1, 1, 0, NULL, 2, NULL);
+ }
+
+ if (vinsn->num_slots == 1)
+ {
+ if (workaround_a0_b_retw && use_transform ())
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_A0_WRITER,
+ is_register_writer (&vinsn->slots[0], "a", 0));
+
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND,
+ is_bad_loopend_opcode (&vinsn->slots[0]));
+ }
+ else
+ set_last_insn_flags (now_seg, now_subseg, FLAG_IS_BAD_LOOPEND, FALSE);
+
+ insn_size = xtensa_format_length (isa, vinsn->format);
+
+ extra_space = relaxation_requirements (vinsn);
+
+ /* vinsn_to_insnbuf will produce the error. */
+ if (vinsn->format != XTENSA_UNDEFINED)
+ {
+ f = (char *) frag_more (insn_size + extra_space);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ }
+
+ vinsn_to_insnbuf (vinsn, f, frag_now, TRUE);
+ if (vinsn->format == XTENSA_UNDEFINED)
+ return;
+
+ xtensa_insnbuf_to_chars (isa, vinsn->insnbuf, f, 0);
+
+ xtensa_dwarf2_emit_insn (insn_size - extra_space, &best_loc);
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ frag_now->tc_frag_data.slot_subtypes[slot] = tinsn->subtype;
+ frag_now->tc_frag_data.slot_symbols[slot] = tinsn->symbol;
+ frag_now->tc_frag_data.slot_sub_symbols[slot] = tinsn->sub_symbol;
+ frag_now->tc_frag_data.slot_offsets[slot] = tinsn->offset;
+ frag_now->tc_frag_data.literal_frags[slot] = tinsn->literal_frag;
+ if (tinsn->literal_space != 0)
+ xg_assemble_literal_space (tinsn->literal_space, slot);
+
+ if (tinsn->subtype == RELAX_NARROW)
+ assert (vinsn->num_slots == 1);
+ if (xtensa_opcode_is_jump (isa, tinsn->opcode) == 1)
+ is_jump = TRUE;
+ if (xtensa_opcode_is_branch (isa, tinsn->opcode) == 1)
+ is_branch = TRUE;
+
+ if (tinsn->subtype || tinsn->symbol || tinsn->record_fix
+ || tinsn->offset || tinsn->literal_frag || is_jump || is_branch)
+ finish_frag = TRUE;
+ }
+
+ if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ set_frag_is_specific_opcode (frag_now, TRUE);
+
+ if (finish_frag)
+ {
+ frag_variant (rs_machine_dependent,
+ extra_space, extra_space, RELAX_SLOTS,
+ frag_now->fr_symbol, frag_now->fr_offset, f);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ /* Special cases for loops:
+ close_loop_end should be inserted AFTER short_loop.
+ Make sure that CLOSE loops are processed BEFORE short_loops
+ when converting them. */
+
+ /* "short_loop": Add a NOP if the loop is < 4 bytes. */
+ if (xtensa_opcode_is_loop (isa, vinsn->slots[0].opcode)
+ && !vinsn->slots[0].is_specific_opcode)
+ {
+ if (workaround_short_loop && use_transform ())
+ {
+ maybe_has_short_loop = TRUE;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, 4,
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ }
+
+ /* "close_loop_end": Add up to 12 bytes of NOPs to keep a
+ loop at least 12 bytes away from another loop's end. */
+ if (workaround_close_loop_end && use_transform ())
+ {
+ maybe_has_close_loop_end = TRUE;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 12, 12,
+ RELAX_ADD_NOP_IF_CLOSE_LOOP_END,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ }
+ }
+
+ if (use_transform ())
+ {
+ if (is_jump)
+ {
+ assert (finish_frag);
+ frag_var (rs_machine_dependent,
+ UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+ RELAX_UNREACHABLE,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+ else if (is_branch && align_targets)
+ {
+ assert (finish_frag);
+ frag_var (rs_machine_dependent,
+ UNREACHABLE_MAX_WIDTH, UNREACHABLE_MAX_WIDTH,
+ RELAX_MAYBE_UNREACHABLE,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ frag_var (rs_machine_dependent,
+ 0, 0,
+ RELAX_MAYBE_DESIRE_ALIGN,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+ }
+
+ /* Now, if the original opcode was a call... */
+ if (do_align_targets ()
+ && xtensa_opcode_is_call (isa, vinsn->slots[0].opcode) == 1)
+ {
+ float freq = get_subseg_info (now_seg, now_subseg)->cur_total_freq;
+ frag_now->tc_frag_data.is_insn = TRUE;
+ frag_var (rs_machine_dependent, 4, (int) freq, RELAX_DESIRE_ALIGN,
+ frag_now->fr_symbol, frag_now->fr_offset, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
+
+ if (vinsn_has_specific_opcodes (vinsn) && use_transform ())
+ {
+ frag_wane (frag_now);
+ frag_new (0);
+ xtensa_set_frag_assembly_state (frag_now);
+ }
}
@@ -5377,22 +7794,28 @@ void
xtensa_end ()
{
directive_balance ();
+ xtensa_flush_pending_output ();
+
+ past_xtensa_end = TRUE;
+
xtensa_move_literals ();
xtensa_reorder_segments ();
xtensa_cleanup_align_frags ();
xtensa_fix_target_frags ();
- if (software_a0_b_retw_interlock && has_a0_b_retw)
+ if (workaround_a0_b_retw && has_a0_b_retw)
xtensa_fix_a0_b_retw_frags ();
- if (software_avoid_b_j_loop_end && maybe_has_b_j_loop_end)
+ if (workaround_b_j_loop_end)
xtensa_fix_b_j_loop_end_frags ();
/* "close_loop_end" should be processed BEFORE "short_loop". */
- if (software_avoid_close_loop_end && maybe_has_close_loop_end)
+ if (workaround_close_loop_end && maybe_has_close_loop_end)
xtensa_fix_close_loop_end_frags ();
- if (software_avoid_short_loop && maybe_has_short_loop)
+ if (workaround_short_loop && maybe_has_short_loop)
xtensa_fix_short_loop_frags ();
+ xtensa_mark_narrow_branches ();
+ xtensa_mark_zcl_first_insns ();
xtensa_sanity_check ();
}
@@ -5406,7 +7829,6 @@ xtensa_cleanup_align_frags ()
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)
{
@@ -5417,16 +7839,28 @@ xtensa_cleanup_align_frags ()
|| fragP->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)))
&& fragP->fr_fix == 0)
{
- fragS * next = fragP->fr_next;
+ fragS *next = fragP->fr_next;
while (next
- && next->fr_type == rs_machine_dependent
- && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
+ && next->fr_fix == 0
+ && next->fr_type == rs_machine_dependent
+ && next->fr_subtype == RELAX_DESIRE_ALIGN_IF_TARGET)
{
frag_wane (next);
next = next->fr_next;
}
}
+ /* If we don't widen branch targets, then they
+ will be easier to align. */
+ if (fragP->tc_frag_data.is_branch_target
+ && fragP->fr_opcode == fragP->fr_literal
+ && fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ frag_wane (fragP);
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_UNREACHABLE)
+ fragP->tc_frag_data.is_unreachable = TRUE;
}
}
}
@@ -5460,18 +7894,21 @@ xtensa_fix_target_frags ()
if (next_frag_is_loop_target (fragP))
{
if (prev_frag_can_negate_branch)
- fragP->fr_subtype = RELAX_LOOP_END;
+ {
+ fragP->fr_subtype = RELAX_LOOP_END;
+ /* See the comment near the frag_var with a
+ RELAX_DESIRE_ALIGN to see why we do this. */
+ fragP->fr_var = RELAX_LOOP_END_BYTES;
+ }
else
{
- if (!align_only_targets ||
- next_frag_is_branch_target (fragP))
+ if (next_frag_is_branch_target (fragP))
fragP->fr_subtype = RELAX_DESIRE_ALIGN;
else
frag_wane (fragP);
}
}
- else if (!align_only_targets
- || next_frag_is_branch_target (fragP))
+ else if (next_frag_is_branch_target (fragP))
fragP->fr_subtype = RELAX_DESIRE_ALIGN;
else
frag_wane (fragP);
@@ -5489,18 +7926,142 @@ static bfd_boolean
frag_can_negate_branch (fragP)
fragS *fragP;
{
- if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_IMMED)
+ xtensa_isa isa = xtensa_default_isa;
+ vliw_insn vinsn;
+ int slot;
+
+ if (fragP->fr_type != rs_machine_dependent
+ || fragP->fr_subtype != RELAX_SLOTS)
+ return FALSE;
+
+ vinsn_from_chars (&vinsn, fragP->fr_opcode);
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, vinsn.format); slot++)
{
- TInsn t_insn;
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- if (is_negatable_branch (&t_insn))
+ if ((fragP->tc_frag_data.slot_subtypes[slot] == RELAX_IMMED)
+ && xtensa_opcode_is_branch (isa, vinsn.slots[slot].opcode) == 1)
return TRUE;
}
+
+ return FALSE;
+}
+
+
+static void
+xtensa_mark_narrow_branches ()
+{
+ frchainS *frchP;
+
+ for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_IMMED)
+ {
+ vliw_insn vinsn;
+ const expressionS *expr;
+ symbolS *symbolP;
+
+ vinsn_from_chars (&vinsn, fragP->fr_opcode);
+ tinsn_immed_from_frag (&vinsn.slots[0], fragP, 0);
+
+ expr = &vinsn.slots[0].tok[1];
+ symbolP = expr->X_add_symbol;
+
+ if (vinsn.num_slots == 1
+ && xtensa_opcode_is_branch (xtensa_default_isa,
+ vinsn.slots[0].opcode)
+ && xg_get_single_size (vinsn.slots[0].opcode) == 2
+ && is_narrow_branch_guaranteed_in_range (fragP,
+ &vinsn.slots[0]))
+ {
+ fragP->fr_subtype = RELAX_SLOTS;
+ fragP->tc_frag_data.slot_subtypes[0] = RELAX_NARROW;
+ }
+ }
+ }
+ }
+}
+
+
+/* A branch is typically widened only when its target is out of
+ range. However, we would like to widen them to align a subsequent
+ branch target when possible.
+
+ Because the branch relaxation code is so convoluted, the optimal solution
+ (combining the two cases) is difficult to get right in all circumstances.
+ We therefore go with an "almost as good" solution, where we only
+ use for alignment narrow branches that definitely will not expand to a
+ jump and a branch. These functions find and mark these cases. */
+
+/* the range in bytes of a bnez.n and beqz.n */
+#define MAX_IMMED6 68
+
+static bfd_boolean
+is_narrow_branch_guaranteed_in_range (fragP, tinsn)
+ fragS *fragP;
+ TInsn *tinsn;
+{
+ const expressionS *expr = &tinsn->tok[1];
+ symbolS *symbolP = expr->X_add_symbol;
+ fragS *target_frag = symbol_get_frag (symbolP);
+ size_t max_distance = expr->X_add_number;
+ max_distance += (S_GET_VALUE (symbolP) - target_frag->fr_address);
+ if (is_branch_jmp_to_next (tinsn, fragP))
+ return FALSE;
+
+ /* The branch doesn't branch over it's own frag,
+ but over the subsequent ones. */
+ fragP = fragP->fr_next;
+ while (fragP != NULL && fragP != target_frag && max_distance <= MAX_IMMED6)
+ {
+ max_distance += unrelaxed_frag_max_size (fragP);
+ fragP = fragP->fr_next;
+ }
+ if (max_distance <= MAX_IMMED6 && fragP == target_frag)
+ return TRUE;
return FALSE;
}
+static void
+xtensa_mark_zcl_first_insns ()
+{
+ frchainS *frchP;
+
+ for (frchP = frchain_root; frchP; frchP = frchP->frch_next)
+ {
+ fragS *fragP;
+ /* Walk over all of the fragments in a subsection. */
+ for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ if (fragP->fr_type == rs_machine_dependent
+ && (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE
+ || fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE))
+ {
+ /* Find the loop frag. */
+ fragS *targ_frag = next_non_empty_frag (fragP);
+ /* Find the first insn frag. */
+ targ_frag = next_non_empty_frag (targ_frag);
+
+ /* Of course, sometimes (mostly for toy test cases) a
+ zero-cost loop instruction is the last in a section. */
+ if (targ_frag)
+ {
+ targ_frag->tc_frag_data.is_first_loop_insn = TRUE;
+ if (fragP->fr_subtype == RELAX_CHECK_ALIGN_NEXT_OPCODE)
+ frag_wane (fragP);
+ }
+ }
+ }
+ }
+}
+
+
/* Re-process all of the fragments looking to convert all of the
RELAX_ADD_NOP_IF_A0_B_RETW. If the next instruction is a
conditional branch or a retw/retw.n, convert this frag to one that
@@ -5524,9 +8085,13 @@ xtensa_fix_a0_b_retw_frags ()
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_A0_B_RETW)
{
if (next_instrs_are_b_retw (fragP))
- relax_frag_add_nop (fragP);
- else
- frag_wane (fragP);
+ {
+ if (get_frag_is_no_transform (fragP))
+ as_bad (_("instruction sequence (write a0, branch, retw) may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
}
}
}
@@ -5535,42 +8100,71 @@ xtensa_fix_a0_b_retw_frags ()
bfd_boolean
next_instrs_are_b_retw (fragP)
- fragS * fragP;
+ fragS *fragP;
{
xtensa_opcode opcode;
+ xtensa_format fmt;
const fragS *next_fragP = next_non_empty_frag (fragP);
static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
xtensa_isa isa = xtensa_default_isa;
int offset = 0;
+ int slot;
+ bfd_boolean branch_seen = FALSE;
if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
if (next_fragP == NULL)
return FALSE;
/* Check for the conditional branch. */
- xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
- opcode = xtensa_decode_insn (isa, insnbuf);
+ xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset], 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return FALSE;
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ xtensa_format_get_slot (isa, fmt, slot, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+
+ branch_seen = (branch_seen
+ || xtensa_opcode_is_branch (isa, opcode) == 1);
+ }
- if (!is_conditional_branch_opcode (opcode))
+ if (!branch_seen)
return FALSE;
- offset += xtensa_insn_length (isa, opcode);
+ offset += xtensa_format_length (isa, fmt);
if (offset == next_fragP->fr_fix)
{
next_fragP = next_non_empty_frag (next_fragP);
offset = 0;
}
+
if (next_fragP == NULL)
return FALSE;
/* Check for the retw/retw.n. */
- xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset]);
- opcode = xtensa_decode_insn (isa, insnbuf);
+ xtensa_insnbuf_from_chars (isa, insnbuf, &next_fragP->fr_literal[offset], 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ /* Because RETW[.N] is not bundleable, a VLIW bundle here means that we
+ have no problems. */
+ if (fmt == XTENSA_UNDEFINED
+ || xtensa_format_num_slots (isa, fmt) != 1)
+ return FALSE;
+
+ xtensa_format_get_slot (isa, fmt, 0, insnbuf, slotbuf);
+ opcode = xtensa_opcode_decode (isa, fmt, 0, slotbuf);
if (is_windowed_return_opcode (opcode))
return TRUE;
+
return FALSE;
}
@@ -5598,9 +8192,13 @@ xtensa_fix_b_j_loop_end_frags ()
&& fragP->fr_subtype == RELAX_ADD_NOP_IF_PRE_LOOP_END)
{
if (next_instr_is_loop_end (fragP))
- relax_frag_add_nop (fragP);
- else
- frag_wane (fragP);
+ {
+ if (get_frag_is_no_transform (fragP))
+ as_bad (_("branching or jumping to a loop end may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
}
}
}
@@ -5656,21 +8254,22 @@ xtensa_fix_close_loop_end_frags ()
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_IMMED)
+ && ((fragP->fr_subtype == RELAX_IMMED)
+ || ((fragP->fr_subtype == RELAX_SLOTS)
+ && (fragP->tc_frag_data.slot_subtypes[0]
+ == RELAX_IMMED))))
{
/* Read it. If the instruction is a loop, get the target. */
- xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode);
- if (is_loop_opcode (opcode))
+ TInsn t_insn;
+ tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
{
- TInsn t_insn;
-
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
-
/* Get the current fragment target. */
- if (fragP->fr_symbol)
+ if (fragP->tc_frag_data.slot_symbols[0])
{
- current_target = symbol_get_frag (fragP->fr_symbol);
+ symbolS *sym = fragP->tc_frag_data.slot_symbols[0];
+ current_target = symbol_get_frag (sym);
current_offset = fragP->fr_offset;
}
}
@@ -5691,32 +8290,39 @@ xtensa_fix_close_loop_end_frags ()
if (min_bytes < REQUIRED_LOOP_DIVIDING_BYTES)
{
- while (min_bytes + bytes_added
- < REQUIRED_LOOP_DIVIDING_BYTES)
+ if (get_frag_is_no_transform (fragP))
+ as_bad (_("loop end too close to another loop end may trigger hardware errata"));
+ else
{
- int length = 3;
-
- if (fragP->fr_var < length)
- as_warn (_("fr_var %lu < length %d; ignoring"),
- fragP->fr_var, length);
- else
+ while (min_bytes + bytes_added
+ < REQUIRED_LOOP_DIVIDING_BYTES)
{
- assemble_nop (length,
- fragP->fr_literal + fragP->fr_fix);
- fragP->fr_fix += length;
- fragP->fr_var -= length;
+ int length = 3;
+
+ if (fragP->fr_var < length)
+ as_fatal (_("fr_var %lu < length %d"),
+ fragP->fr_var, length);
+ else
+ {
+ assemble_nop (length,
+ fragP->fr_literal + fragP->fr_fix);
+ fragP->fr_fix += length;
+ fragP->fr_var -= length;
+ }
+ bytes_added += length;
}
- bytes_added += length;
}
}
frag_wane (fragP);
}
+ assert (fragP->fr_type != rs_machine_dependent
+ || fragP->fr_subtype != RELAX_ADD_NOP_IF_CLOSE_LOOP_END);
}
}
}
-size_t
+static size_t
min_bytes_to_other_loop_end (fragP, current_target, current_offset, max_size)
fragS *fragP;
fragS *current_target;
@@ -5743,9 +8349,9 @@ min_bytes_to_other_loop_end (fragP, current_target, current_offset, max_size)
}
-size_t
+static size_t
unrelaxed_frag_min_size (fragP)
- fragS * fragP;
+ fragS *fragP;
{
size_t size = fragP->fr_fix;
@@ -5757,6 +8363,44 @@ unrelaxed_frag_min_size (fragP)
}
+static size_t
+unrelaxed_frag_max_size (fragP)
+ fragS *fragP;
+{
+ size_t size = fragP->fr_fix;
+ switch (fragP->fr_type)
+ {
+ case 0:
+ /* Empty frags created by the obstack allocation scheme
+ end up with type 0. */
+ break;
+ case rs_fill:
+ case rs_org:
+ case rs_space:
+ size += fragP->fr_offset;
+ break;
+ case rs_align:
+ case rs_align_code:
+ case rs_align_test:
+ case rs_leb128:
+ case rs_cfa:
+ case rs_dwarf2dbg:
+ /* No further adjustments needed. */
+ break;
+ case rs_machine_dependent:
+ if (fragP->fr_subtype != RELAX_DESIRE_ALIGN)
+ size += fragP->fr_var;
+ break;
+ default:
+ /* We had darn well better know how big it is. */
+ assert (0);
+ break;
+ }
+
+ return size;
+}
+
+
/* Re-process all of the fragments looking to convert all
of the RELAX_ADD_NOP_IF_SHORT_LOOP. If:
@@ -5766,7 +8410,7 @@ unrelaxed_frag_min_size (fragP)
2) loop has a jump or branch in it
or B)
- 1) software_avoid_all_short_loops is true
+ 1) workaround_all_short_loops is TRUE
2) The generating loop was a 'loopgtz' or 'loopnez'
3) the instruction size count to the loop end label is too short
(<= 2 instructions)
@@ -5790,25 +8434,27 @@ xtensa_fix_short_loop_frags ()
/* Walk over all of the fragments in a subsection. */
for (fragP = frchP->frch_root; fragP; fragP = fragP->fr_next)
{
- /* check on the current loop */
+ /* Check on the current loop. */
if (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_IMMED)
+ && ((fragP->fr_subtype == RELAX_IMMED)
+ || ((fragP->fr_subtype == RELAX_SLOTS)
+ && (fragP->tc_frag_data.slot_subtypes[0]
+ == RELAX_IMMED))))
{
+ TInsn t_insn;
+
/* Read it. If the instruction is a loop, get the target. */
- xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_opcode);
- if (is_loop_opcode (opcode))
+ tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
{
- TInsn t_insn;
-
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
-
/* Get the current fragment target. */
- if (fragP->fr_symbol)
+ if (fragP->tc_frag_data.slot_symbols[0])
{
- current_target = symbol_get_frag (fragP->fr_symbol);
+ symbolS *sym = fragP->tc_frag_data.slot_symbols[0];
+ current_target = symbol_get_frag (sym);
current_offset = fragP->fr_offset;
- current_opcode = opcode;
+ current_opcode = t_insn.opcode;
}
}
}
@@ -5820,19 +8466,23 @@ xtensa_fix_short_loop_frags ()
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
+ || (workaround_all_short_loops
&& current_opcode != XTENSA_UNDEFINED
&& !is_the_loop_opcode (current_opcode))))
- relax_frag_add_nop (fragP);
- else
- frag_wane (fragP);
+ {
+ if (get_frag_is_no_transform (fragP))
+ as_bad (_("loop containing less than three instructions may trigger hardware errata"));
+ else
+ relax_frag_add_nop (fragP);
+ }
+ frag_wane (fragP);
}
}
}
}
-size_t
+static size_t
count_insns_to_loop_end (base_fragP, count_relax_add, max_count)
fragS *base_fragP;
bfd_boolean count_relax_add;
@@ -5867,26 +8517,35 @@ count_insns_to_loop_end (base_fragP, count_relax_add, max_count)
}
-size_t
+static size_t
unrelaxed_frag_min_insn_count (fragP)
fragS *fragP;
{
+ xtensa_isa isa = xtensa_default_isa;
+ static xtensa_insnbuf insnbuf = NULL;
size_t insn_count = 0;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return insn_count;
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
/* Decode the fixed instructions. */
while (offset < fragP->fr_fix)
{
- xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
- if (opcode == XTENSA_UNDEFINED)
+ xtensa_format fmt;
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, fragP->fr_literal + offset, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+
+ if (fmt == XTENSA_UNDEFINED)
{
as_fatal (_("undecodable instruction in instruction frag"));
return insn_count;
}
- offset += xtensa_insn_length (xtensa_default_isa, opcode);
+ offset += xtensa_format_length (isa, fmt);
insn_count++;
}
@@ -5894,7 +8553,7 @@ unrelaxed_frag_min_insn_count (fragP)
}
-bfd_boolean
+static bfd_boolean
branch_before_loop_end (base_fragP)
fragS *base_fragP;
{
@@ -5911,28 +8570,40 @@ branch_before_loop_end (base_fragP)
}
-bfd_boolean
+static bfd_boolean
unrelaxed_frag_has_b_j (fragP)
fragS *fragP;
{
- size_t insn_count = 0;
+ static xtensa_insnbuf insnbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
int offset = 0;
if (!fragP->tc_frag_data.is_insn)
return FALSE;
+ if (!insnbuf)
+ insnbuf = xtensa_insnbuf_alloc (isa);
+
/* Decode the fixed instructions. */
while (offset < fragP->fr_fix)
{
- xtensa_opcode opcode = get_opcode_from_buf (fragP->fr_literal + offset);
- if (opcode == XTENSA_UNDEFINED)
+ xtensa_format fmt;
+ int slot;
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, fragP->fr_literal + offset, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ return FALSE;
+
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
{
- as_fatal (_("undecodable instruction in instruction frag"));
- return insn_count;
+ xtensa_opcode opcode =
+ get_opcode_from_buf (fragP->fr_literal + offset, slot);
+ if (xtensa_opcode_is_branch (isa, opcode) == 1
+ || xtensa_opcode_is_jump (isa, opcode) == 1)
+ return TRUE;
}
- if (is_branch_or_jump_opcode (opcode))
- return TRUE;
- offset += xtensa_insn_length (xtensa_default_isa, opcode);
+ offset += xtensa_format_length (isa, fmt);
}
return FALSE;
}
@@ -5967,10 +8638,11 @@ xtensa_sanity_check ()
{
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
+ tinsn_from_chars (&t_insn, fragP->fr_opcode, 0);
+ tinsn_immed_from_frag (&t_insn, fragP, 0);
- if (is_loop_opcode (t_insn.opcode))
+ if (xtensa_opcode_is_loop (xtensa_default_isa,
+ t_insn.opcode) == 1)
{
if (is_empty_loop (&t_insn, fragP))
{
@@ -5994,7 +8666,7 @@ xtensa_sanity_check ()
#define LOOP_IMMED_OPN 1
-/* Return true if the loop target is the next non-zero fragment. */
+/* Return TRUE if the loop target is the next non-zero fragment. */
bfd_boolean
is_empty_loop (insn, fragP)
@@ -6008,7 +8680,7 @@ is_empty_loop (insn, fragP)
if (insn->insn_type != ITYPE_INSN)
return FALSE;
- if (!is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) != 1)
return FALSE;
if (insn->ntok <= LOOP_IMMED_OPN)
@@ -6031,6 +8703,7 @@ is_empty_loop (insn, fragP)
/* 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)
@@ -6056,7 +8729,7 @@ is_local_forward_loop (insn, fragP)
if (insn->insn_type != ITYPE_INSN)
return FALSE;
- if (!is_loop_opcode (insn->opcode))
+ if (xtensa_opcode_is_loop (xtensa_default_isa, insn->opcode) == 0)
return FALSE;
if (insn->ntok <= LOOP_IMMED_OPN)
@@ -6076,11 +8749,14 @@ is_local_forward_loop (insn, fragP)
/* Walk through fragments until we find the target.
If we do not find the target, then this is an invalid loop. */
+
for (next_fragP = fragP->fr_next;
next_fragP != NULL;
next_fragP = next_fragP->fr_next)
- if (next_fragP == symbol_get_frag (symbolP))
- return TRUE;
+ {
+ if (next_fragP == symbol_get_frag (symbolP))
+ return TRUE;
+ }
return FALSE;
}
@@ -6088,7 +8764,7 @@ is_local_forward_loop (insn, fragP)
/* Alignment Functions. */
-size_t
+static size_t
get_text_align_power (target_size)
int target_size;
{
@@ -6098,12 +8774,12 @@ get_text_align_power (target_size)
if (target_size <= (1 << i))
return i;
}
- as_fatal (_("get_text_align_power: argument too large"));
+ assert (0);
return 0;
}
-addressT
+static addressT
get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
int align_pow;
bfd_boolean use_nops;
@@ -6119,7 +8795,7 @@ get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
/* get_text_align_fill_size ()
-
+
Desired alignments:
give the address
target_size = size of next instruction
@@ -6129,6 +8805,7 @@ get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
Loop alignments:
address = current address + loop instruction size;
target_size = 3 (for 2 or 3 byte target)
+ = 4 (for 4 byte target)
= 8 (for 8 byte target)
align_pow = get_text_align_power (target_size);
use_nops = 1
@@ -6140,7 +8817,7 @@ get_text_align_max_fill_size (align_pow, use_nops, use_no_density)
use_nops = 0
use_no_density = 0. */
-addressT
+static addressT
get_text_align_fill_size (address, align_pow, target_size,
use_nops, use_no_density)
addressT address;
@@ -6156,25 +8833,41 @@ get_text_align_fill_size (address, align_pow, target_size,
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_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. */
+ Usually 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.
+
+ Two cases:
+
+ (1) aligning an instruction properly, but without using NOPs.
+ E.G.: a 3-byte instruction can go on any address where address mod 4
+ is zero or one. The aligner uses this case to find the optimal
+ number of fill bytes for relax_frag_for_align.
+
+ (2) aligning an instruction properly, but where we might need to use
+ extra NOPs. E.G.: when the aligner couldn't find enough widenings
+ or similar to get the optimal location. */
size_t alignment = (1 << align_pow);
+
+ assert (target_size != 0);
+
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;
+ unsigned fill_bytes;
+ for (fill_bytes = 0; fill_bytes < alignment; fill_bytes++)
+ {
+ addressT end_address = address + target_size - 1 + fill_bytes;
+ addressT start_address = address + fill_bytes;
+ if ((end_address >> align_pow) == (start_address >> align_pow))
+ return fill_bytes;
+ }
+ assert (0);
}
/* This is the slightly harder case. */
@@ -6187,8 +8880,8 @@ get_text_align_fill_size (address, align_pow, target_size,
{
if (i == 1)
continue;
- if ((address + i) >> align_pow ==
- (address + i + target_size - 1) >> align_pow)
+ if ((address + i) >> align_pow
+ == (address + i + target_size - 1) >> align_pow)
return i;
}
}
@@ -6199,8 +8892,8 @@ get_text_align_fill_size (address, align_pow, target_size,
/* 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)
+ if ((address + i) >> align_pow
+ == (address + i + target_size - 1) >> align_pow)
return i;
}
}
@@ -6274,212 +8967,154 @@ get_noop_aligned_address (fragP, address)
fragS *fragP;
addressT address;
{
- static xtensa_insnbuf insnbuf = NULL;
+ /* The rule is: get next fragment's FIRST instruction. Find
+ the smallest number of bytes that need to be added to
+ ensure that the next fragment's FIRST instruction will fit
+ in a single word.
+
+ E.G., 2 bytes : 0, 1, 2 mod 4
+ 3 bytes: 0, 1 mod 4
+
+ If the FIRST instruction MIGHT be relaxed,
+ assume that it will become a 3-byte instruction.
+
+ Note again here that LOOP instructions are not bundleable,
+ and this relaxation only applies to LOOP opcodes. */
+
size_t fill_size = 0;
+ int first_insn_size;
+ int loop_insn_size;
+ addressT pre_opcode_bytes;
+ size_t alignment;
+ fragS *first_insn;
+ xtensa_opcode opcode;
+ bfd_boolean is_loop;
- 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;
+ assert (fragP->fr_type == rs_machine_dependent);
+ assert (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE);
- if (opcode == XTENSA_UNDEFINED)
- {
- as_bad_where (fragP->fr_file, fragP->fr_line,
- _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
- as_fatal (_("cannot continue"));
- }
+ /* Find the loop frag. */
+ first_insn = next_non_empty_frag (fragP);
+ /* Now find the first insn frag. */
+ first_insn = next_non_empty_frag (first_insn);
- target_insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
+ is_loop = next_frag_opcode_is_loop (fragP, &opcode);
+ assert (is_loop);
+ loop_insn_size = xg_get_single_size (opcode);
- pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
+ pre_opcode_bytes = next_frag_pre_opcode_bytes (fragP);
+ pre_opcode_bytes += loop_insn_size;
- 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;
+ /* For loops, the alignment depends on the size of the
+ instruction following the loop, not the LOOP instruction. */
- pre_opcode_bytes += target_insn_size;
+ if (first_insn == NULL)
+ return address;
- /* 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);
+ assert (first_insn->tc_frag_data.is_first_loop_insn);
- if (target_insn_size == 2)
- target_insn_size = 3; /* ISA specifies this. */
- }
+ first_insn_size = frag_format_size (first_insn);
- /* If it was 8, then we'll need a larger alignment
- for the section. */
- alignment = get_text_align_power (target_insn_size);
+ if (first_insn_size == 2 || first_insn_size == XTENSA_UNDEFINED)
+ first_insn_size = 3; /* ISA specifies this */
- /* Is Now_seg valid */
- record_alignment (now_seg, alignment);
- }
- else
- as_fatal (_("expected loop opcode in relax align next target"));
+ /* If it was 8, then we'll need a larger alignment for the section. */
+ alignment = get_text_align_power (first_insn_size);
- 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"));
- }
+ /* Is now_seg valid? */
+ record_alignment (now_seg, alignment);
+
+ fill_size = get_text_align_fill_size
+ (address + pre_opcode_bytes,
+ get_text_align_power (first_insn_size),
+ first_insn_size, TRUE, fragP->tc_frag_data.is_no_density);
return address + fill_size;
}
-/* 3 mechanisms for relaxing an alignment:
-
- Align to a power of 2.
- Align so the next fragment's instruction does not cross a word boundary.
- Align the current instruction so that if the next instruction
- were 3 bytes, it would not cross a word boundary.
-
+/* 3 mechanisms for relaxing an alignment:
+
+ Align to a power of 2.
+ Align so the next fragment's instruction does not cross a word boundary.
+ Align the current instruction so that if the next instruction
+ were 3 bytes, it would not cross a word boundary.
+
We can align with:
- zeros - This is easy; always insert zeros.
- nops - 3 and 2 byte instructions
- 2 - 2 byte nop
- 3 - 3 byte nop
- 4 - 2, 2-byte nops
- >=5 : 3 byte instruction + fn(n-3)
+ zeros - This is easy; always insert zeros.
+ nops - 3-byte and 2-byte instructions
+ 2 - 2-byte nop
+ 3 - 3-byte nop
+ 4 - 2 2-byte nops
+ >=5 : 3-byte instruction + fn (n-3)
widening - widen previous instructions. */
static addressT
-get_widen_aligned_address (fragP, address)
+get_aligned_diff (fragP, address, max_diff)
fragS *fragP;
addressT address;
+ addressT *max_diff;
{
- addressT align_pow, new_address, loop_insn_offset;
- fragS *next_frag;
- int insn_size;
- xtensa_opcode opcode, next_opcode;
- static xtensa_insnbuf insnbuf = NULL;
-
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
- if (fragP->fr_type == rs_align || fragP->fr_type == rs_align_code)
- {
- align_pow = fragP->fr_offset;
- new_address = ((address + ((1 << align_pow) - 1))
- << align_pow) >> align_pow;
- return new_address;
- }
+ addressT target_address, loop_insn_offset;
+ int target_size;
+ xtensa_opcode loop_opcode;
+ bfd_boolean is_loop;
+ int text_align_power;
+ addressT opt_diff;
- if (fragP->fr_type == rs_machine_dependent)
+ assert (fragP->fr_type == rs_machine_dependent);
+ switch (fragP->fr_subtype)
{
- switch (fragP->fr_subtype)
- {
- case RELAX_DESIRE_ALIGN:
-
- /* The rule is: get the next fragment's FIRST instruction.
- Find the smallest number of bytes needed to be added
- in order to ensure that the next fragment is FIRST
- instruction will fit in a single word.
- i.e. 2 bytes : 0, 1, 2. mod 4
- 3 bytes: 0, 1 mod 4
- If the FIRST instruction MIGHT be relaxed,
- assume that it will become a 3-byte instruction. */
-
- insn_size = 3;
- /* Check to see if it might be 2 bytes. */
- next_opcode = next_frag_opcode (fragP);
- if (next_opcode != XTENSA_UNDEFINED
- && xtensa_insn_length (xtensa_default_isa, next_opcode) == 2)
- insn_size = 2;
-
- assert (insn_size <= 4);
- for (new_address = address; new_address < address + 4; new_address++)
- {
- if (new_address >> 2 == (new_address + insn_size - 1) >> 2)
- return new_address;
- }
- as_bad (_("internal error aligning"));
- return address;
-
- case RELAX_ALIGN_NEXT_OPCODE:
- /* The rule is: get next fragment's FIRST instruction.
- Find the smallest number of bytes needed to be added
- in order to ensure that the next fragment's FIRST
- instruction will fit in a single word.
- i.e. 2 bytes : 0, 1, 2. mod 4
- 3 bytes: 0, 1 mod 4
- If the FIRST instruction MIGHT be relaxed,
- assume that it will become a 3 byte instruction. */
-
- opcode = next_frag_opcode (fragP);
- if (opcode == XTENSA_UNDEFINED)
- {
- as_bad_where (fragP->fr_file, fragP->fr_line,
- _("invalid opcode for RELAX_ALIGN_NEXT_OPCODE"));
- as_fatal (_("cannot continue"));
- }
- insn_size = xtensa_insn_length (xtensa_default_isa, opcode);
- assert (insn_size <= 4);
- assert (is_loop_opcode (opcode));
-
- loop_insn_offset = 0;
- next_frag = next_non_empty_frag (fragP);
-
- /* If the loop has been expanded then the loop
- instruction could be at an offset from this fragment. */
- if (next_frag->fr_subtype != RELAX_IMMED)
- loop_insn_offset = get_expanded_loop_offset (opcode);
+ case RELAX_DESIRE_ALIGN:
+ target_size = next_frag_format_size (fragP);
+ if (target_size == XTENSA_UNDEFINED)
+ target_size = 3;
+ text_align_power = get_text_align_power (xtensa_fetch_width);
+ opt_diff = get_text_align_fill_size (address, text_align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = opt_diff + xtensa_fetch_width
+ - (target_size + ((address + opt_diff) % xtensa_fetch_width));
+ assert (*max_diff >= opt_diff);
+ return opt_diff;
- for (new_address = address; new_address < address + 4; new_address++)
- {
- if ((new_address + loop_insn_offset + insn_size) >> 2 ==
- (new_address + loop_insn_offset + insn_size + 2) >> 2)
- return new_address;
- }
- as_bad (_("internal error aligning"));
- return address;
+ case RELAX_ALIGN_NEXT_OPCODE:
+ target_size = next_frag_format_size (fragP);
+ loop_insn_offset = 0;
+ is_loop = next_frag_opcode_is_loop (fragP, &loop_opcode);
+ assert (is_loop);
+
+ /* If the loop has been expanded then the LOOP instruction
+ could be at an offset from this fragment. */
+ if (next_non_empty_frag(fragP)->tc_frag_data.slot_subtypes[0]
+ != RELAX_IMMED)
+ loop_insn_offset = get_expanded_loop_offset (loop_opcode);
+
+ if (target_size == 2)
+ target_size = 3; /* ISA specifies this */
+
+ /* In an ideal world, which is what we are shooting for here,
+ we wouldn't need to use any NOPs immediately prior to the
+ LOOP instruction. If this approach fails, relax_frag_loop_align
+ will call get_noop_aligned_address. */
+ target_address =
+ address + loop_insn_offset + xg_get_single_size (loop_opcode);
+ text_align_power = get_text_align_power (target_size),
+ opt_diff = get_text_align_fill_size (target_address, text_align_power,
+ target_size, FALSE, FALSE);
+
+ *max_diff = xtensa_fetch_width
+ - ((target_address + opt_diff) % xtensa_fetch_width)
+ - target_size + opt_diff;
+ assert (*max_diff >= opt_diff);
+ return opt_diff;
- default:
- as_bad (_("internal error aligning"));
- return address;
- }
+ default:
+ break;
}
- as_bad (_("internal error aligning"));
- return address;
+ assert (0);
+ return 0;
}
@@ -6494,10 +9129,14 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
long stretch;
int *stretched_p;
{
+ xtensa_isa isa = xtensa_default_isa;
int unreported = fragP->tc_frag_data.unreported_expansion;
long new_stretch = 0;
char *file_name;
int line, lit_size;
+ static xtensa_insnbuf vbuf = NULL;
+ int slot, num_slots;
+ xtensa_format fmt;
as_where (&file_name, &line);
new_logical_line (fragP->fr_file, fragP->fr_line);
@@ -6508,7 +9147,8 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
{
case RELAX_ALIGN_NEXT_OPCODE:
/* Always convert. */
- new_stretch = relax_frag_text_align (fragP, stretch);
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch = relax_frag_loop_align (fragP, stretch);
break;
case RELAX_LOOP_END:
@@ -6518,11 +9158,11 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
case RELAX_LOOP_END_ADD_NOP:
/* Add a NOP and switch to .fill 0. */
new_stretch = relax_frag_add_nop (fragP);
+ frag_wane (fragP);
break;
case RELAX_DESIRE_ALIGN:
- /* We REALLY want to change the relaxation order here. This
- should do NOTHING. The narrowing before it will either align
+ /* Do nothing. The narrowing before this frag will either align
it or not. */
break;
@@ -6540,35 +9180,71 @@ xtensa_relax_frag (fragP, stretch, stretched_p)
new_stretch = 4;
break;
- case RELAX_NARROW:
- new_stretch = relax_frag_narrow (fragP, stretch);
- break;
+ case RELAX_SLOTS:
+ if (vbuf == NULL)
+ vbuf = xtensa_insnbuf_alloc (isa);
+
+ xtensa_insnbuf_from_chars (isa, vbuf, fragP->fr_opcode, 0);
+ fmt = xtensa_format_decode (isa, vbuf);
+ num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (slot = 0; slot < num_slots; slot++)
+ {
+ switch (fragP->tc_frag_data.slot_subtypes[slot])
+ {
+ case RELAX_NARROW:
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch += relax_frag_for_align (fragP, stretch);
+ break;
+
+ case RELAX_IMMED:
+ case RELAX_IMMED_STEP1:
+ case RELAX_IMMED_STEP2:
+ /* Place the immediate. */
+ new_stretch += relax_frag_immed
+ (now_seg, fragP, stretch,
+ fragP->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+ fmt, slot, stretched_p, FALSE);
+ break;
- case RELAX_IMMED:
- case RELAX_IMMED_STEP1:
- case RELAX_IMMED_STEP2:
- /* Place the immediate. */
- new_stretch = relax_frag_immed (now_seg, fragP, stretch,
- fragP->fr_subtype - RELAX_IMMED,
- stretched_p);
+ default:
+ /* This is OK; see the note in xg_assemble_vliw_tokens. */
+ break;
+ }
+ }
break;
case RELAX_LITERAL_POOL_BEGIN:
case RELAX_LITERAL_POOL_END:
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
/* No relaxation required. */
break;
+ case RELAX_FILL_NOP:
+ case RELAX_UNREACHABLE:
+ if (fragP->tc_frag_data.relax_seen)
+ new_stretch += relax_frag_for_align (fragP, stretch);
+ break;
+
default:
as_bad (_("bad relaxation state"));
}
+ /* Tell gas we need another relaxation pass. */
+ if (! fragP->tc_frag_data.relax_seen)
+ {
+ fragP->tc_frag_data.relax_seen = TRUE;
+ *stretched_p = 1;
+ }
+
new_logical_line (file_name, line);
return new_stretch;
}
static long
-relax_frag_text_align (fragP, stretch)
+relax_frag_loop_align (fragP, stretch)
fragS *fragP;
long stretch;
{
@@ -6576,18 +9252,14 @@ relax_frag_text_align (fragP, stretch)
addressT new_address, new_next_address, new_size;
addressT growth;
- /* Overview of the relaxation procedure for alignment
- inside an executable section:
-
- The old size is stored in the tc_frag_data.text_expansion field.
-
- Calculate the new address, fix up the text_expansion and
- return the growth. */
+ /* All the frags with relax_frag_for_alignment prior to this one in the
+ section have been done, hopefully eliminating the need for a NOP here.
+ But, this will put it in if necessary. */
/* Calculate the old address of this fragment and the next fragment. */
old_address = fragP->fr_address - stretch;
old_next_address = (fragP->fr_address - stretch + fragP->fr_fix +
- fragP->tc_frag_data.text_expansion);
+ fragP->tc_frag_data.text_expansion[0]);
old_size = old_next_address - old_address;
/* Calculate the new address of this fragment and the next fragment. */
@@ -6599,198 +9271,521 @@ relax_frag_text_align (fragP, stretch)
growth = new_size - old_size;
/* Fix up the text_expansion field and return the new growth. */
- fragP->tc_frag_data.text_expansion += growth;
+ fragP->tc_frag_data.text_expansion[0] += growth;
return growth;
}
-/* Add a NOP (i.e., "or a1, a1, a1"). Use the 3-byte one because we
- don't know about the availability of density yet. TODO: When the
- flags are stored per fragment, use NOP.N when possible. */
+/* Add a NOP instruction. */
static long
relax_frag_add_nop (fragP)
fragS *fragP;
{
- static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn;
char *nop_buf = fragP->fr_literal + fragP->fr_fix;
- int length;
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
- tinsn_init (&t_insn);
- t_insn.opcode = xtensa_or_opcode;
- assert (t_insn.opcode != XTENSA_UNDEFINED);
-
- t_insn.ntok = 3;
- set_expr_const (&t_insn.tok[0], 1);
- set_expr_const (&t_insn.tok[1], 1);
- set_expr_const (&t_insn.tok[2], 1);
-
- tinsn_to_insnbuf (&t_insn, insnbuf);
+ int length = fragP->tc_frag_data.is_no_density ? 3 : 2;
+ assemble_nop (length, nop_buf);
fragP->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars (xtensa_default_isa, insnbuf, nop_buf);
- length = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
if (fragP->fr_var < length)
{
- as_warn (_("fr_var (%ld) < length (%d); ignoring"),
- fragP->fr_var, length);
- frag_wane (fragP);
+ as_fatal (_("fr_var (%ld) < length (%d)"), fragP->fr_var, length);
return 0;
}
fragP->fr_fix += length;
fragP->fr_var -= length;
- frag_wane (fragP);
return length;
}
static long
-relax_frag_narrow (fragP, stretch)
+relax_frag_for_align (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. */
+ /* Overview of the relaxation procedure for alignment:
+ We can widen with NOPs or by widening instructions or by filling
+ bytes after jump instructions. Find the opportune places and widen
+ them if necessary. */
- assert (fragP->fr_type == rs_machine_dependent
- && fragP->fr_subtype == RELAX_NARROW);
+ long stretch_me;
+ long diff;
- if (!future_alignment_required (fragP, 0))
+ assert (fragP->fr_subtype == RELAX_FILL_NOP
+ || fragP->fr_subtype == RELAX_UNREACHABLE
+ || (fragP->fr_subtype == RELAX_SLOTS
+ && fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW));
+
+ stretch_me = future_alignment_required (fragP, stretch);
+ diff = stretch_me - fragP->tc_frag_data.text_expansion[0];
+ if (diff == 0)
+ return 0;
+
+ if (diff < 0)
{
- /* If already expanded but no longer needed because of a prior
- stretch, it is SAFE to unexpand because the next fragment will
- NEVER start at an address > the previous time through the
- relaxation. */
- if (fragP->tc_frag_data.text_expansion)
+ /* We expanded on a previous pass. Can we shrink now? */
+ long shrink = fragP->tc_frag_data.text_expansion[0] - stretch_me;
+ if (shrink <= stretch && stretch > 0)
{
- if (stretch > 0)
- {
- fragP->tc_frag_data.text_expansion = 0;
- return -1;
- }
- /* Otherwise we have to live with this bad choice. */
- return 0;
+ fragP->tc_frag_data.text_expansion[0] = stretch_me;
+ return -shrink;
}
return 0;
}
- if (fragP->tc_frag_data.text_expansion == 0)
- {
- fragP->tc_frag_data.text_expansion = 1;
- return 1;
- }
+ /* Below here, diff > 0. */
+ fragP->tc_frag_data.text_expansion[0] = stretch_me;
- return 0;
+ return diff;
}
-static bfd_boolean
-future_alignment_required (fragP, stretch)
- fragS *fragP;
- long stretch;
+/* Return the address of the next frag that should be aligned.
+
+ By "address" we mean the address it _would_ be at if there
+ is no action taken to align it between here and the target frag.
+ In other words, if no narrows and no fill nops are used between
+ here and the frag to align, _even_if_ some of the frags we use
+ to align targets have already expanded on a previous relaxation
+ pass.
+
+ Also, count each frag that may be used to help align the target.
+
+ Return 0 if there are no frags left in the chain that need to be
+ aligned. */
+
+static addressT
+find_address_of_next_align_frag (fragPP, wide_nops, narrow_nops,
+ widens, paddable)
+ fragS **fragPP;
+ int *wide_nops;
+ int *narrow_nops;
+ int *widens;
+ bfd_boolean *paddable;
{
- long address = fragP->fr_address + stretch;
- int num_widens = 0;
- addressT aligned_address;
- offsetT desired_diff;
+ fragS *fragP = *fragPP;
+ addressT address = fragP->fr_address;
+
+ /* Do not reset the counts to 0. */
while (fragP)
{
/* Limit this to a small search. */
- if (num_widens > 8)
- return FALSE;
+ if (*widens > 8)
+ {
+ *fragPP = fragP;
+ return 0;
+ }
address += fragP->fr_fix;
- switch (fragP->fr_type)
+ if (fragP->fr_type == rs_fill)
+ address += fragP->fr_offset * fragP->fr_var;
+ else if (fragP->fr_type == rs_machine_dependent)
{
- case rs_fill:
- address += fragP->fr_offset * fragP->fr_var;
- break;
-
- case rs_machine_dependent:
switch (fragP->fr_subtype)
{
- case RELAX_NARROW:
- /* address += fragP->fr_fix; */
- num_widens++;
+ case RELAX_UNREACHABLE:
+ *paddable = TRUE;
+ break;
+
+ case RELAX_FILL_NOP:
+ (*wide_nops)++;
+ if (!fragP->tc_frag_data.is_no_density)
+ (*narrow_nops)++;
+ break;
+
+ case RELAX_SLOTS:
+ if (fragP->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ {
+ (*widens)++;
+ break;
+ }
+ /* FIXME: shouldn't this add the expansion of all slots? */
+ address += fragP->tc_frag_data.text_expansion[0];
break;
case RELAX_IMMED:
- address += (/* fragP->fr_fix + */
- fragP->tc_frag_data.text_expansion);
+ address += fragP->tc_frag_data.text_expansion[0];
break;
case RELAX_ALIGN_NEXT_OPCODE:
case RELAX_DESIRE_ALIGN:
- /* address += fragP->fr_fix; */
- aligned_address = get_widen_aligned_address (fragP, address);
- desired_diff = aligned_address - address;
- assert (desired_diff >= 0);
- /* If there are enough wideners in between do it. */
- /* return (num_widens == desired_diff); */
- if (num_widens == desired_diff)
- return TRUE;
- if (fragP->fr_subtype == RELAX_ALIGN_NEXT_OPCODE)
- return FALSE;
+ *fragPP = fragP;
+ return address;
+
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ /* Do nothing. */
break;
default:
- return FALSE;
+ /* Just punt if we don't know the type. */
+ *fragPP = fragP;
+ return 0;
}
- break;
+ }
+ else
+ {
+ /* Just punt if we don't know the type. */
+ *fragPP = fragP;
+ return 0;
+ }
+ fragP = fragP->fr_next;
+ }
+ *fragPP = fragP;
+ return 0;
+}
+
+
+/* Undefine LOOKAHEAD_ALIGNER to get the older behavior.
+ I'll leave this in until I am more confident this works. */
+
+#define LOOKAHEAD_ALIGNER 1
+
+static long
+future_alignment_required (fragP, stretch)
+ fragS *fragP;
+ long stretch ATTRIBUTE_UNUSED;
+{
+ fragS *this_frag = fragP;
+ long address;
+ int num_widens = 0;
+ int wide_nops = 0;
+ int narrow_nops = 0;
+ bfd_boolean paddable = FALSE;
+ offsetT local_opt_diff;
+ offsetT opt_diff;
+ offsetT max_diff;
+ int stretch_amount = 0;
+ int local_stretch_amount;
+ int global_stretch_amount;
+
+ address
+ = find_address_of_next_align_frag (&fragP, &wide_nops, &narrow_nops,
+ &num_widens, &paddable);
+
+ if (address)
+ {
+ local_opt_diff = get_aligned_diff (fragP, address, &max_diff);
+ opt_diff = local_opt_diff;
+ assert (opt_diff >= 0);
+ assert (max_diff >= opt_diff);
+ if (max_diff == 0)
+ return 0;
+#ifdef LOOKAHEAD_ALIGNER
+ if (fragP)
+ fragP = fragP->fr_next;
+
+ while (fragP && opt_diff < max_diff && address)
+ {
+ /* We only use these to determine if we can exit early
+ because there will be plenty of ways to align future
+ align frags. */
+ unsigned int glob_widens = 0;
+ int dnn = 0;
+ int dw = 0;
+ bfd_boolean glob_pad = 0;
+ address =
+ find_address_of_next_align_frag (&fragP, &glob_widens,
+ &dnn, &dw, &glob_pad);
+ /* If there is a padable portion, then skip. */
+ if (glob_pad || (glob_widens >= xtensa_fetch_width))
+ break;
+
+ if (address)
+ {
+ offsetT next_m_diff;
+ offsetT next_o_diff;
+
+ /* Downrange frags haven't had stretch added to them yet. */
+ address += stretch;
+
+ /* The address also includes any text expansion from this
+ frag in a previous pass, but we don't want that. */
+ address -= this_frag->tc_frag_data.text_expansion[0];
+
+ /* Assume we are going to move at least opt_diff. In
+ reality, we might not be able to, but assuming that
+ we will helps catch cases where moving opt_diff pushes
+ the next target from aligned to unaligned. */
+ address += opt_diff;
+
+ next_o_diff = get_aligned_diff (fragP, address, &next_m_diff);
+
+ /* Now cleanup for the adjustments to address. */
+ next_o_diff += opt_diff;
+ next_m_diff += opt_diff;
+ if (next_o_diff <= max_diff && next_o_diff > opt_diff)
+ opt_diff = next_o_diff;
+ if (next_m_diff < max_diff)
+ max_diff = next_m_diff;
+ fragP = fragP->fr_next;
+ }
+ }
+#endif /* LOOKAHEAD_ALIGNER */
+ /* If there are enough wideners in between, do it. */
+ if (paddable)
+ {
+ if (this_frag->fr_subtype == RELAX_UNREACHABLE)
+ {
+ assert (opt_diff <= UNREACHABLE_MAX_WIDTH);
+ return opt_diff;
+ }
+ return 0;
+ }
+ local_stretch_amount
+ = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+ num_widens, local_opt_diff);
+#ifdef LOOKAHEAD_ALIGNER
+ global_stretch_amount
+ = bytes_to_stretch (this_frag, wide_nops, narrow_nops,
+ num_widens, opt_diff);
+ /* If the condition below is true, then the frag couldn't
+ stretch the correct amount for the global case, so we just
+ optimize locally. We'll rely on the subsequent frags to get
+ the correct alignment in the global case. */
+ if (global_stretch_amount < local_stretch_amount)
+ stretch_amount = local_stretch_amount;
+ else
+ stretch_amount = global_stretch_amount;
+#else /* ! LOOKAHEAD_ALIGNER */
+ stretch_amount = local_stretch_amount;
+#endif /* ! LOOKAHEAD_ALIGNER */
+ if (this_frag->fr_subtype == RELAX_SLOTS
+ && this_frag->tc_frag_data.slot_subtypes[0] == RELAX_NARROW)
+ assert (stretch_amount <= 1);
+ else if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ {
+ if (this_frag->tc_frag_data.is_no_density)
+ assert (stretch_amount == 3 || stretch_amount == 0);
+ else
+ assert (stretch_amount <= 3);
+ }
+ }
+ return stretch_amount;
+}
+
+
+/* The idea: widen everything you can to get a target or loop aligned,
+ then start using NOPs.
+
+ When we must have a NOP, here is a table of how we decide
+ (so you don't have to fight through the control flow below):
+
+ wide_nops = the number of wide NOPs available for aligning
+ narrow_nops = the number of narrow NOPs available for aligning
+ (a subset of wide_nops)
+ widens = the number of narrow instructions that should be widened
+
+ Desired wide narrow
+ Diff nop nop widens
+ 1 0 0 1
+ 2 0 1 0
+ 3a 1 0 0
+ b 0 1 1 (case 3a makes this case unnecessary)
+ 4a 1 0 1
+ b 0 2 0
+ c 0 1 2 (case 4a makes this case unnecessary)
+ 5a 1 0 2
+ b 1 1 0
+ c 0 2 1 (case 5b makes this case unnecessary)
+ 6a 2 0 0
+ b 1 0 3
+ c 0 1 4 (case 6b makes this case unneccesary)
+ d 1 1 1 (case 6a makes this case unnecessary)
+ e 0 2 2 (case 6a makes this case unnecessary)
+ f 0 3 0 (case 6a makes this case unnecessary)
+ 7a 1 0 4
+ b 2 0 1
+ c 1 1 2 (case 7b makes this case unnecessary)
+ d 0 1 5 (case 7a makes this case unnecessary)
+ e 0 2 3 (case 7b makes this case unnecessary)
+ f 0 3 1 (case 7b makes this case unnecessary)
+ g 1 2 1 (case 7b makes this case unnecessary)
+*/
+
+static long
+bytes_to_stretch (this_frag, wide_nops, narrow_nops, num_widens, desired_diff)
+ fragS *this_frag;
+ int wide_nops;
+ int narrow_nops;
+ int num_widens;
+ int desired_diff;
+{
+ int bytes_short = desired_diff - num_widens;
+
+ assert (desired_diff >= 0 && desired_diff < 8);
+ if (desired_diff == 0)
+ return 0;
+
+ assert (wide_nops > 0 || num_widens > 0);
+
+ /* Always prefer widening to NOP-filling. */
+ if (bytes_short < 0)
+ {
+ /* There are enough RELAX_NARROW frags after this one
+ to align the target without widening this frag in any way. */
+ return 0;
+ }
+
+ if (bytes_short == 0)
+ {
+ /* Widen every narrow between here and the align target
+ and the align target will be properly aligned. */
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ return 0;
+ else
+ return 1;
+ }
+
+ /* From here we will need at least one NOP to get an alignment.
+ However, we may not be able to align at all, in which case,
+ don't widen. */
+ if (this_frag->fr_subtype == RELAX_FILL_NOP)
+ {
+ switch (desired_diff)
+ {
+ case 1:
+ return 0;
+ case 2:
+ if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 1)
+ return 2; /* case 2 */
+ return 0;
+ case 3:
+ if (wide_nops > 1)
+ return 0;
+ else
+ return 3; /* case 3a */
+ case 4:
+ if (num_widens >= 1 && wide_nops == 1)
+ return 3; /* case 4a */
+ if (!this_frag->tc_frag_data.is_no_density && narrow_nops == 2)
+ return 2; /* case 4b */
+ return 0;
+ case 5:
+ if (num_widens >= 2 && wide_nops == 1)
+ return 3; /* case 5a */
+ /* We will need two nops. Are there enough nops
+ between here and the align target? */
+ if (wide_nops < 2 || narrow_nops == 0)
+ return 0;
+ /* Are there other nops closer that can serve instead? */
+ if (wide_nops > 2 && narrow_nops > 1)
+ return 0;
+ /* Take the density one first, because there might not be
+ another density one available. */
+ if (!this_frag->tc_frag_data.is_no_density)
+ return 2; /* case 5b narrow */
+ else
+ return 3; /* case 5b wide */
+ return 0;
+ case 6:
+ if (wide_nops == 2)
+ return 3; /* case 6a */
+ else if (num_widens >= 3 && wide_nops == 1)
+ return 3; /* case 6b */
+ return 0;
+ case 7:
+ if (wide_nops == 1 && num_widens >= 4)
+ return 3; /* case 7a */
+ else if (wide_nops == 2 && num_widens >= 1)
+ return 3; /* case 7b */
+ return 0;
default:
- return FALSE;
+ assert (0);
}
- fragP = fragP->fr_next;
}
+ else
+ {
+ /* We will need a NOP no matter what, but should we widen
+ this instruction to help?
- return FALSE;
+ This is a RELAX_FRAG_NARROW frag. */
+ switch (desired_diff)
+ {
+ case 1:
+ assert (0);
+ return 0;
+ case 2:
+ case 3:
+ return 0;
+ case 4:
+ if (wide_nops >= 1 && num_widens == 1)
+ return 1; /* case 4a */
+ return 0;
+ case 5:
+ if (wide_nops >= 1 && num_widens == 2)
+ return 1; /* case 5a */
+ return 0;
+ case 6:
+ if (wide_nops >= 2)
+ return 0; /* case 6a */
+ else if (wide_nops >= 1 && num_widens == 3)
+ return 1; /* case 6b */
+ return 0;
+ case 7:
+ if (wide_nops >= 1 && num_widens == 4)
+ return 1; /* case 7a */
+ else if (wide_nops >= 2 && num_widens == 1)
+ return 1; /* case 7b */
+ return 0;
+ default:
+ assert (0);
+ return 0;
+ }
+ }
+ assert (0);
+ return 0;
}
static long
-relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
+relax_frag_immed (segP, fragP, stretch, min_steps, fmt, slot, stretched_p,
+ estimate_only)
segT segP;
fragS *fragP;
long stretch;
int min_steps;
+ xtensa_format fmt;
+ int slot;
int *stretched_p;
+ bfd_boolean estimate_only;
{
- static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn;
+ TInsn tinsn;
+ vliw_insn orig_vinsn;
int old_size;
bfd_boolean negatable_branch = FALSE;
bfd_boolean branch_jmp_to_next = FALSE;
+ bfd_boolean wide_insn = FALSE;
+ xtensa_isa isa = xtensa_default_isa;
IStack istack;
offsetT frag_offset;
int num_steps;
fragS *lit_fragP;
int num_text_bytes, num_literal_bytes;
- int literal_diff, text_diff;
+ int literal_diff, total_text_diff, this_text_diff, first;
assert (fragP->fr_opcode != NULL);
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ xg_init_vinsn (&orig_vinsn);
+ vinsn_from_chars (&orig_vinsn, fragP->fr_opcode);
+ if (xtensa_format_num_slots (isa, fmt) > 1)
+ wide_insn = TRUE;
+
+ tinsn = orig_vinsn.slots[slot];
+ tinsn_immed_from_frag (&tinsn, fragP, slot);
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
+ if (estimate_only && xtensa_opcode_is_loop (isa, tinsn.opcode))
+ return 0;
- negatable_branch = is_negatable_branch (&t_insn);
+ if (workaround_b_j_loop_end && !get_frag_is_no_transform (fragP))
+ branch_jmp_to_next = is_branch_jmp_to_next (&tinsn, fragP);
- old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
+ negatable_branch = (xtensa_opcode_is_branch (isa, tinsn.opcode) == 1);
- if (software_avoid_b_j_loop_end)
- branch_jmp_to_next = is_branch_jmp_to_next (&t_insn, fragP);
+ old_size = xtensa_format_length (isa, fmt);
/* Special case: replace a branch to the next instruction with a NOP.
This is required to work around a hardware bug in T1040.0 and also
@@ -6807,7 +9802,7 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
frag_offset = fragP->fr_opcode - fragP->fr_literal;
istack_init (&istack);
- num_steps = xg_assembly_relax (&istack, &t_insn, segP, fragP, frag_offset,
+ num_steps = xg_assembly_relax (&istack, &tinsn, segP, fragP, frag_offset,
min_steps, stretch);
if (num_steps < min_steps)
{
@@ -6821,26 +9816,40 @@ relax_frag_immed (segP, fragP, stretch, min_steps, stretched_p)
return 0;
}
- fragP->fr_subtype = (int) RELAX_IMMED + num_steps;
+ fragP->tc_frag_data.slot_subtypes[slot] = (int) RELAX_IMMED + num_steps;
/* Figure out the number of bytes needed. */
lit_fragP = 0;
- num_text_bytes = get_num_stack_text_bytes (&istack) - old_size;
num_literal_bytes = get_num_stack_literal_bytes (&istack);
- literal_diff = num_literal_bytes - fragP->tc_frag_data.literal_expansion;
- text_diff = num_text_bytes - fragP->tc_frag_data.text_expansion;
+ literal_diff =
+ num_literal_bytes - fragP->tc_frag_data.literal_expansion[slot];
+ first = 0;
+ while (istack.insn[first].opcode == XTENSA_UNDEFINED)
+ first++;
+ num_text_bytes = get_num_stack_text_bytes (&istack);
+ if (wide_insn)
+ {
+ num_text_bytes += old_size;
+ if (opcode_fits_format_slot (istack.insn[first].opcode, fmt, slot))
+ num_text_bytes -= xg_get_single_size (istack.insn[first].opcode);
+ }
+ total_text_diff = num_text_bytes - old_size;
+ this_text_diff = total_text_diff - fragP->tc_frag_data.text_expansion[slot];
/* It MUST get larger. If not, we could get an infinite loop. */
- know (num_text_bytes >= 0);
- know (literal_diff >= 0 && text_diff >= 0);
+ assert (num_text_bytes >= 0);
+ assert (literal_diff >= 0);
+ assert (total_text_diff >= 0);
- fragP->tc_frag_data.text_expansion = num_text_bytes;
- fragP->tc_frag_data.literal_expansion = num_literal_bytes;
+ fragP->tc_frag_data.text_expansion[slot] = total_text_diff;
+ fragP->tc_frag_data.literal_expansion[slot] = num_literal_bytes;
+ assert (fragP->tc_frag_data.text_expansion[slot] >= 0);
+ assert (fragP->tc_frag_data.literal_expansion[slot] >= 0);
/* Find the associated expandable literal for this. */
if (literal_diff != 0)
{
- lit_fragP = fragP->tc_frag_data.literal_frag;
+ lit_fragP = fragP->tc_frag_data.literal_frags[slot];
if (lit_fragP)
{
assert (literal_diff == 4);
@@ -6858,16 +9867,15 @@ relax_frag_immed (segP, fragP, stretch, min_steps, 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);
- }
+ /* FIXME: When a negatable branch expands and then contracts in a
+ subsequent pass, update_next_frag_state correctly updates the
+ type of the frag to RELAX_MAYBE_UNREACHABLE, but it doesn't undo
+ any expansion relax_frag_for_align may have expected it to. For
+ now, change back to only call it when the branch expands. */
+ if (negatable_branch && istack.ninsn > 1)
+ update_next_frag_state (fragP, FALSE /* istack.ninsn > 1 */);
- return text_diff;
+ return this_text_diff;
}
@@ -6879,6 +9887,11 @@ md_convert_frag (abfd, sec, fragp)
segT sec;
fragS *fragp;
{
+ static xtensa_insnbuf vbuf = NULL;
+ xtensa_isa isa = xtensa_default_isa;
+ int slot;
+ int num_slots;
+ xtensa_format fmt;
char *file_name;
int line;
@@ -6900,16 +9913,54 @@ md_convert_frag (abfd, sec, fragp)
case RELAX_LITERAL_FINAL:
break;
- case RELAX_NARROW:
- /* No conversion. */
- convert_frag_narrow (fragp);
+ case RELAX_SLOTS:
+ if (vbuf == NULL)
+ vbuf = xtensa_insnbuf_alloc (isa);
+
+ xtensa_insnbuf_from_chars (isa, vbuf, fragp->fr_opcode, 0);
+ fmt = xtensa_format_decode (isa, vbuf);
+ num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (slot = 0; slot < num_slots; slot++)
+ {
+ switch (fragp->tc_frag_data.slot_subtypes[slot])
+ {
+ case RELAX_NARROW:
+ convert_frag_narrow (sec, fragp, fmt, slot);
+ break;
+
+ case RELAX_IMMED:
+ case RELAX_IMMED_STEP1:
+ case RELAX_IMMED_STEP2:
+ /* Place the immediate. */
+ convert_frag_immed
+ (sec, fragp,
+ fragp->tc_frag_data.slot_subtypes[slot] - RELAX_IMMED,
+ fmt, slot);
+ break;
+
+ default:
+ /* This is OK because some slots could have
+ relaxations and others have none. */
+ break;
+ }
+ }
+ break;
+
+ case RELAX_UNREACHABLE:
+ memset (&fragp->fr_literal[fragp->fr_fix], 0, fragp->fr_var);
+ fragp->fr_fix += fragp->tc_frag_data.text_expansion[0];
+ fragp->fr_var -= fragp->tc_frag_data.text_expansion[0];
+ frag_wane (fragp);
break;
- case RELAX_IMMED:
- case RELAX_IMMED_STEP1:
- case RELAX_IMMED_STEP2:
- /* Place the immediate. */
- convert_frag_immed (sec, fragp, fragp->fr_subtype - RELAX_IMMED);
+ case RELAX_MAYBE_UNREACHABLE:
+ case RELAX_MAYBE_DESIRE_ALIGN:
+ frag_wane (fragp);
+ break;
+
+ case RELAX_FILL_NOP:
+ convert_frag_fill_nop (fragp);
break;
case RELAX_LITERAL_NR:
@@ -6979,15 +10030,30 @@ convert_frag_align_next_opcode (fragp)
static void
-convert_frag_narrow (fragP)
+convert_frag_narrow (segP, fragP, fmt, slot)
+ segT segP;
fragS *fragP;
+ xtensa_format fmt;
+ int slot;
{
- static xtensa_insnbuf insnbuf = NULL;
- TInsn t_insn, single_target;
+ TInsn tinsn, single_target;
+ xtensa_format single_fmt;
int size, old_size, diff, error_val;
offsetT frag_offset;
- if (fragP->tc_frag_data.text_expansion == 0)
+ assert (slot == 0);
+ tinsn_from_chars (&tinsn, fragP->fr_opcode, 0);
+
+ if (xtensa_opcode_is_branch (xtensa_default_isa, tinsn.opcode) == 1)
+ {
+ assert (fragP->tc_frag_data.text_expansion[0] == 1
+ || fragP->tc_frag_data.text_expansion[0] == 0);
+ convert_frag_immed (segP, fragP, fragP->tc_frag_data.text_expansion[0],
+ fmt, slot);
+ return;
+ }
+
+ if (fragP->tc_frag_data.text_expansion[0] == 0)
{
/* No conversion. */
fragP->fr_var = 0;
@@ -6996,25 +10062,28 @@ convert_frag_narrow (fragP)
assert (fragP->fr_opcode != NULL);
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
-
- tinsn_from_chars (&t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&t_insn, fragP);
+ /* Frags in this relaxation state should only contain
+ single instruction bundles. */
+ tinsn_immed_from_frag (&tinsn, fragP, 0);
/* Just convert it to a wide form.... */
size = 0;
- old_size = xtensa_insn_length (xtensa_default_isa, t_insn.opcode);
+ old_size = xg_get_single_size (tinsn.opcode);
tinsn_init (&single_target);
frag_offset = fragP->fr_opcode - fragP->fr_literal;
- error_val = xg_expand_narrow (&single_target, &t_insn);
+ error_val = xg_expand_narrow (&single_target, &tinsn);
if (error_val)
- as_bad (_("unable to widen instruction"));
+ {
+ as_bad (_("unable to widen instruction"));
+ return;
+ }
- size = xtensa_insn_length (xtensa_default_isa, single_target.opcode);
- xg_emit_insn_to_buf (&single_target, fragP->fr_opcode,
+ size = xg_get_single_size (single_target.opcode);
+ single_fmt = xg_get_single_format (single_target.opcode);
+
+ xg_emit_insn_to_buf (&single_target, single_fmt, fragP->fr_opcode,
fragP, frag_offset, TRUE);
diff = size - old_size;
@@ -7029,81 +10098,129 @@ convert_frag_narrow (fragP)
static void
-convert_frag_immed (segP, fragP, min_steps)
+convert_frag_fill_nop (fragP)
+ fragS *fragP;
+{
+ char *loc = &fragP->fr_literal[fragP->fr_fix];
+ int size = fragP->tc_frag_data.text_expansion[0];
+ assert ((unsigned) size == (fragP->fr_next->fr_address
+ - fragP->fr_address - fragP->fr_fix));
+ if (size == 0)
+ {
+ /* No conversion. */
+ fragP->fr_var = 0;
+ return;
+ }
+ assemble_nop (size, loc);
+ fragP->tc_frag_data.is_insn = TRUE;
+ fragP->fr_var -= size;
+ fragP->fr_fix += size;
+ frag_wane (fragP);
+}
+
+
+static void
+convert_frag_immed (segP, fragP, min_steps, fmt, slot)
segT segP;
fragS *fragP;
int min_steps;
+ xtensa_format fmt;
+ int slot;
{
char *immed_instr = fragP->fr_opcode;
- static xtensa_insnbuf insnbuf = NULL;
- TInsn orig_t_insn;
+ TInsn orig_tinsn;
bfd_boolean expanded = FALSE;
- char *fr_opcode = fragP->fr_opcode;
bfd_boolean branch_jmp_to_next = FALSE;
- int size;
+ char *fr_opcode = fragP->fr_opcode;
+ vliw_insn orig_vinsn;
+ xtensa_isa isa = xtensa_default_isa;
+ bfd_boolean wide_insn = FALSE;
+ int bytes;
+ bfd_boolean is_loop;
- assert (fragP->fr_opcode != NULL);
+ assert (fr_opcode != NULL);
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (xtensa_default_isa);
+ xg_init_vinsn (&orig_vinsn);
- tinsn_from_chars (&orig_t_insn, fragP->fr_opcode);
- tinsn_immed_from_frag (&orig_t_insn, fragP);
+ vinsn_from_chars (&orig_vinsn, fr_opcode);
+ if (xtensa_format_num_slots (isa, fmt) > 1)
+ wide_insn = TRUE;
- /* Here is the fun stuff: Get the immediate field from this
- instruction. If it fits, we're done. If not, find the next
- instruction sequence that fits. */
+ orig_tinsn = orig_vinsn.slots[slot];
+ tinsn_immed_from_frag (&orig_tinsn, fragP, slot);
+
+ is_loop = xtensa_opcode_is_loop (xtensa_default_isa, orig_tinsn.opcode) == 1;
- if (software_avoid_b_j_loop_end)
- branch_jmp_to_next = is_branch_jmp_to_next (&orig_t_insn, fragP);
+ if (workaround_b_j_loop_end && !get_frag_is_no_transform (fragP))
+ branch_jmp_to_next = is_branch_jmp_to_next (&orig_tinsn, fragP);
if (branch_jmp_to_next && !next_frag_is_loop_target (fragP))
{
/* Conversion just inserts a NOP and marks the fix as completed. */
- size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
- assemble_nop (size, fragP->fr_opcode);
+ bytes = xtensa_format_length (isa, fmt);
+ if (bytes >= 4)
+ {
+ orig_vinsn.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, orig_vinsn.format, slot);
+ orig_vinsn.slots[slot].ntok = 0;
+ }
+ else
+ {
+ bytes += fragP->tc_frag_data.text_expansion[0];
+ assert (bytes == 2 || bytes == 3);
+ build_nop (&orig_vinsn.slots[0], bytes);
+ fragP->fr_fix += fragP->tc_frag_data.text_expansion[0];
+ }
+ vinsn_to_insnbuf (&orig_vinsn, fr_opcode, frag_now, FALSE);
+ xtensa_insnbuf_to_chars (isa, orig_vinsn.insnbuf, fr_opcode, 0);
fragP->fr_var = 0;
}
- else
+ else if (!orig_tinsn.is_specific_opcode)
{
+ /* Here is the fun stuff: Get the immediate field from this
+ instruction. If it fits, we're done. If not, find the next
+ instruction sequence that fits. */
+
IStack istack;
int i;
symbolS *lit_sym = NULL;
int total_size = 0;
+ int target_offset = 0;
int old_size;
int diff;
symbolS *gen_label = NULL;
offsetT frag_offset;
+ bfd_boolean first = TRUE;
+ bfd_boolean last_is_jump;
- /* It does not fit. Find something that does and
+ /* It does not fit. Find something that does and
convert immediately. */
- frag_offset = fragP->fr_opcode - fragP->fr_literal;
+ frag_offset = fr_opcode - fragP->fr_literal;
istack_init (&istack);
- xg_assembly_relax (&istack, &orig_t_insn,
+ xg_assembly_relax (&istack, &orig_tinsn,
segP, fragP, frag_offset, min_steps, 0);
- old_size = xtensa_insn_length (xtensa_default_isa, orig_t_insn.opcode);
+ old_size = xtensa_format_length (isa, fmt);
/* Assemble this right inline. */
/* First, create the mapping from a label name to the REAL label. */
- total_size = 0;
+ target_offset = 0;
for (i = 0; i < istack.ninsn; i++)
{
- TInsn *t_insn = &istack.insn[i];
- int size = 0;
+ TInsn *tinsn = &istack.insn[i];
fragS *lit_frag;
- switch (t_insn->insn_type)
+ switch (tinsn->insn_type)
{
case ITYPE_LITERAL:
if (lit_sym != NULL)
as_bad (_("multiple literals in expansion"));
/* First find the appropriate space in the literal pool. */
- lit_frag = fragP->tc_frag_data.literal_frag;
+ lit_frag = fragP->tc_frag_data.literal_frags[slot];
if (lit_frag == NULL)
as_bad (_("no registered fragment for literal"));
- if (t_insn->ntok != 1)
+ if (tinsn->ntok != 1)
as_bad (_("number of literal tokens != 1"));
/* Set the literal symbol and add a fixup. */
@@ -7111,53 +10228,148 @@ convert_frag_immed (segP, fragP, min_steps)
break;
case ITYPE_LABEL:
+ if (align_targets && !is_loop)
+ {
+ fragS *unreach = fragP->fr_next;
+ while (!(unreach->fr_type == rs_machine_dependent
+ && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || unreach->fr_subtype == RELAX_UNREACHABLE)))
+ {
+ unreach = unreach->fr_next;
+ }
+
+ assert (unreach->fr_type == rs_machine_dependent
+ && (unreach->fr_subtype == RELAX_MAYBE_UNREACHABLE
+ || unreach->fr_subtype == RELAX_UNREACHABLE));
+
+ target_offset += unreach->tc_frag_data.text_expansion[0];
+ }
assert (gen_label == NULL);
gen_label = symbol_new (FAKE_LABEL_NAME, now_seg,
- fragP->fr_opcode - fragP->fr_literal +
- total_size, fragP);
+ fr_opcode - fragP->fr_literal
+ + target_offset, fragP);
break;
case ITYPE_INSN:
- size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
- total_size += size;
+ if (first && wide_insn)
+ {
+ target_offset += xtensa_format_length (isa, fmt);
+ first = FALSE;
+ if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ target_offset += xg_get_single_size (tinsn->opcode);
+ }
+ else
+ target_offset += xg_get_single_size (tinsn->opcode);
break;
}
}
total_size = 0;
+ first = TRUE;
+ last_is_jump = FALSE;
for (i = 0; i < istack.ninsn; i++)
{
- TInsn *t_insn = &istack.insn[i];
+ TInsn *tinsn = &istack.insn[i];
fragS *lit_frag;
int size;
segT target_seg;
+ bfd_reloc_code_real_type reloc_type;
- switch (t_insn->insn_type)
+ switch (tinsn->insn_type)
{
case ITYPE_LITERAL:
- lit_frag = fragP->tc_frag_data.literal_frag;
- /* already checked */
+ lit_frag = fragP->tc_frag_data.literal_frags[slot];
+ /* Already checked. */
assert (lit_frag != NULL);
assert (lit_sym != NULL);
- assert (t_insn->ntok == 1);
- /* add a fixup */
+ assert (tinsn->ntok == 1);
+ /* Add a fixup. */
target_seg = S_GET_SEGMENT (lit_sym);
assert (target_seg);
+ if (tinsn->tok[0].X_op == O_pltrel)
+ reloc_type = BFD_RELOC_XTENSA_PLT;
+ else
+ reloc_type = BFD_RELOC_32;
fix_new_exp_in_seg (target_seg, 0, lit_frag, 0, 4,
- &t_insn->tok[0], FALSE, BFD_RELOC_32);
+ &tinsn->tok[0], FALSE, reloc_type);
break;
case ITYPE_LABEL:
break;
case ITYPE_INSN:
- xg_resolve_labels (t_insn, gen_label);
- xg_resolve_literals (t_insn, lit_sym);
- size = xtensa_insn_length (xtensa_default_isa, t_insn->opcode);
- total_size += size;
- xg_emit_insn_to_buf (t_insn, immed_instr, fragP,
- immed_instr - fragP->fr_literal, TRUE);
+ xg_resolve_labels (tinsn, gen_label);
+ xg_resolve_literals (tinsn, lit_sym);
+ if (wide_insn && first)
+ {
+ first = FALSE;
+ if (opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ {
+ tinsn->record_fix = TRUE;
+ orig_vinsn.slots[slot] = *tinsn;
+ }
+ else
+ {
+ orig_vinsn.slots[slot].opcode =
+ xtensa_format_slot_nop_opcode (isa, fmt, slot);
+ orig_vinsn.slots[slot].ntok = 0;
+ orig_vinsn.slots[slot].record_fix = FALSE;
+ }
+ vinsn_to_insnbuf (&orig_vinsn, immed_instr, fragP, TRUE);
+ xtensa_insnbuf_to_chars (isa, orig_vinsn.insnbuf,
+ immed_instr, 0);
+ fragP->tc_frag_data.is_insn = TRUE;
+ size = xtensa_format_length (isa, fmt);
+ if (!opcode_fits_format_slot (tinsn->opcode, fmt, slot))
+ {
+ xtensa_format single_fmt =
+ xg_get_single_format (tinsn->opcode);
+
+ xg_emit_insn_to_buf
+ (tinsn, single_fmt, immed_instr + size, fragP,
+ immed_instr - fragP->fr_literal + size, TRUE);
+ size += xg_get_single_size (tinsn->opcode);
+ }
+ }
+ else
+ {
+ xtensa_format single_format;
+ size = xg_get_single_size (tinsn->opcode);
+ single_format = xg_get_single_format (tinsn->opcode);
+ xg_emit_insn_to_buf (tinsn, single_format, immed_instr,
+ fragP,
+ immed_instr - fragP->fr_literal, TRUE);
+#if 0
+ /* Code to recognize branch-around expansion
+ so the fragment is properly marked as ending in a
+ jump. */
+ if ((((i == istack.ninsn - 2)
+ && (istack.insn[istack.ninsn-1].insn_type
+ == ITYPE_LABEL))
+ || i == istack.ninsn -1)
+ && xtensa_opcode_is_jump (xtensa_default_isa,
+ tinsn->opcode) == 1
+ && fragP->fr_next != NULL
+ && ! fragP->fr_next->tc_frag_data.is_unreachable)
+ {
+ /* Create a new unreachable frag of zero size. */
+ size_t frag_size = sizeof (fragS);
+ fragS *new_fragP = (fragS *) xmalloc (frag_size);
+ memset (new_fragP, 0, frag_size);
+ new_fragP->fr_address = fragP->fr_next->fr_address;
+ new_fragP->fr_next = fragP->fr_next;
+ new_fragP->fr_fix = 0;
+ new_fragP->fr_var = 0;
+ new_fragP->fr_type = rs_fill;
+ new_fragP->tc_frag_data.is_unreachable = TRUE;
+ /* The rest are zeros.... */
+ /* Link it in to the chain. */
+ fragP->fr_next = new_fragP;
+ }
+#endif
+ }
immed_instr += size;
+ total_size += size;
break;
}
}
@@ -7172,19 +10384,19 @@ convert_frag_immed (segP, fragP, min_steps)
}
/* Clean it up. */
- fragP->fr_var = 0;
+ xg_free_vinsn (&orig_vinsn);
/* Check for undefined immediates in LOOP instructions. */
- if (is_loop_opcode (orig_t_insn.opcode))
+ if (is_loop)
{
symbolS *sym;
- sym = orig_t_insn.tok[1].X_add_symbol;
+ sym = orig_tinsn.tok[1].X_add_symbol;
if (sym != NULL && !S_IS_DEFINED (sym))
{
as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
return;
}
- sym = orig_t_insn.tok[1].X_op_symbol;
+ sym = orig_tinsn.tok[1].X_op_symbol;
if (sym != NULL && !S_IS_DEFINED (sym))
{
as_bad (_("unresolved loop target symbol: %s"), S_GET_NAME (sym));
@@ -7192,16 +10404,15 @@ convert_frag_immed (segP, fragP, min_steps)
}
}
- if (expanded && is_loop_opcode (orig_t_insn.opcode))
- convert_frag_immed_finish_loop (segP, fragP, &orig_t_insn);
+ if (expanded && xtensa_opcode_is_loop (isa, orig_tinsn.opcode) == 1)
+ convert_frag_immed_finish_loop (segP, fragP, &orig_tinsn);
- if (expanded && is_direct_call_opcode (orig_t_insn.opcode))
+ if (expanded && is_direct_call_opcode (orig_tinsn.opcode))
{
/* Add an expansion note on the expanded instruction. */
fix_new_exp_in_seg (now_seg, 0, fragP, fr_opcode - fragP->fr_literal, 4,
- &orig_t_insn.tok[0], TRUE,
+ &orig_tinsn.tok[0], TRUE,
BFD_RELOC_XTENSA_ASM_EXPAND);
-
}
}
@@ -7224,43 +10435,38 @@ fix_new_exp_in_seg (new_seg, new_subseg,
fixS *new_fix;
segT seg = now_seg;
subsegT subseg = now_subseg;
+
assert (new_seg != 0);
subseg_set (new_seg, new_subseg);
- if (r_type == BFD_RELOC_32
- && exp->X_add_symbol
- && symbol_get_tc (exp->X_add_symbol)->plt == 1)
- {
- r_type = BFD_RELOC_XTENSA_PLT;
- }
-
new_fix = fix_new_exp (frag, where, size, exp, pcrel, r_type);
subseg_set (seg, subseg);
return new_fix;
}
-/* Relax a loop instruction so that it can span loop >256 bytes. */
-/*
- loop as, .L1
- .L0:
- rsr as, LEND
- wsr as, LBEG
- addi as, as, lo8(label-.L1)
- addmi as, as, mid8(label-.L1)
- wsr as, LEND
- isync
- rsr as, LCOUNT
- addi as, as, 1
- .L1:
- <<body>>
- label: */
+/* Relax a loop instruction so that it can span loop >256 bytes.
+
+ loop as, .L1
+ .L0:
+ rsr as, LEND
+ wsr as, LBEG
+ addi as, as, lo8 (label-.L1)
+ addmi as, as, mid8 (label-.L1)
+ wsr as, LEND
+ isync
+ rsr as, LCOUNT
+ addi as, as, 1
+ .L1:
+ <<body>>
+ label:
+*/
static void
-convert_frag_immed_finish_loop (segP, fragP, t_insn)
+convert_frag_immed_finish_loop (segP, fragP, tinsn)
segT segP;
fragS *fragP;
- TInsn *t_insn;
+ TInsn *tinsn;
{
TInsn loop_insn;
TInsn addi_insn;
@@ -7272,26 +10478,27 @@ convert_frag_immed_finish_loop (segP, fragP, t_insn)
addressT loop_offset;
addressT addi_offset = 9;
addressT addmi_offset = 12;
+ fragS *next_fragP;
+ size_t target_count;
if (!insnbuf)
insnbuf = xtensa_insnbuf_alloc (isa);
/* Get the loop offset. */
- loop_offset = get_expanded_loop_offset (t_insn->opcode);
- /* Validate that there really is a LOOP at the loop_offset. */
- tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset);
+ loop_offset = get_expanded_loop_offset (tinsn->opcode);
- if (!is_loop_opcode (loop_insn.opcode))
- {
- as_bad_where (fragP->fr_file, fragP->fr_line,
- _("loop relaxation specification does not correspond"));
- assert (0);
- }
+ /* Validate that there really is a LOOP at the loop_offset. Because
+ loops are not bundleable, we can assume that the instruction will be
+ in slot 0. */
+ tinsn_from_chars (&loop_insn, fragP->fr_opcode + loop_offset, 0);
+ tinsn_immed_from_frag (&loop_insn, fragP, 0);
+
+ assert (xtensa_opcode_is_loop (isa, loop_insn.opcode) == 1);
addi_offset += loop_offset;
addmi_offset += loop_offset;
- assert (t_insn->ntok == 2);
- target = get_expression_value (segP, &t_insn->tok[1]);
+ assert (tinsn->ntok == 2);
+ target = get_expression_value (segP, &tinsn->tok[1]);
know (symbolP);
know (symbolP->sy_frag);
@@ -7307,27 +10514,43 @@ convert_frag_immed_finish_loop (segP, fragP, t_insn)
loop_length_hi += 256;
}
- /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
+ /* Because addmi sign-extends the immediate, 'loop_length_hi' can be at most
32512. If the loop is larger than that, then we just fail. */
if (loop_length_hi > 32512)
as_bad_where (fragP->fr_file, fragP->fr_line,
_("loop too long for LOOP instruction"));
- tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset);
+ tinsn_from_chars (&addi_insn, fragP->fr_opcode + addi_offset, 0);
assert (addi_insn.opcode == xtensa_addi_opcode);
- tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset);
+ tinsn_from_chars (&addmi_insn, fragP->fr_opcode + addmi_offset, 0);
assert (addmi_insn.opcode == xtensa_addmi_opcode);
set_expr_const (&addi_insn.tok[2], loop_length_lo);
tinsn_to_insnbuf (&addi_insn, insnbuf);
-
+
fragP->tc_frag_data.is_insn = TRUE;
- xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addi_offset);
+ xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addi_offset, 0);
set_expr_const (&addmi_insn.tok[2], loop_length_hi);
tinsn_to_insnbuf (&addmi_insn, insnbuf);
- xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addmi_offset);
+ xtensa_insnbuf_to_chars (isa, insnbuf, fragP->fr_opcode + addmi_offset, 0);
+
+ /* Walk through all of the frags from here to the loop end
+ and mark them as no_transform to keep them from being modified
+ by the linker. If we ever have a relocation for the
+ addi/addmi of the difference of two symbols we can remove this. */
+
+ target_count = 0;
+ for (next_fragP = fragP; next_fragP != NULL;
+ next_fragP = next_fragP->fr_next)
+ {
+ set_frag_is_no_transform (next_fragP, TRUE);
+ if (next_fragP->tc_frag_data.is_loop_target)
+ target_count++;
+ if (target_count == 2)
+ break;
+ }
}
@@ -7353,23 +10576,6 @@ get_expression_value (segP, exp)
}
-/* 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;
@@ -7378,40 +10584,46 @@ 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;
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
+ return subseg_e->flags;
}
-static void
-set_last_insn_flags (seg, subseg, fl, val)
+static subseg_map *
+get_subseg_info (seg, subseg)
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 (seg == subseg_e->seg && subseg == subseg_e->subseg)
+ return 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;
+ /* Start off considering every branch target very important. */
+ subseg_e->cur_target_freq = 1.0;
+ subseg_e->cur_total_freq = 1.0;
+ subseg_e->next = sseg_map;
+ sseg_map = subseg_e;
+
+ return subseg_e;
+}
+static void
+set_last_insn_flags (seg, subseg, fl, val)
+ segT seg;
+ subsegT subseg;
+ unsigned fl;
+ bfd_boolean val;
+{
+ subseg_map *subseg_e = get_subseg_info (seg, subseg);
if (val)
subseg_e->flags |= fl;
else
@@ -7519,31 +10731,33 @@ xtensa_move_literals ()
prevents us from making a segment with an frchain without any
frags in it. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
last_frag = frag_now;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
- while (search_frag != frag_now)
+ while (search_frag != frag_now)
{
next_frag = search_frag->fr_next;
- /* First, move the frag out of the literal section and
+ /* First, move the frag out of the literal section and
to the appropriate place. */
if (search_frag->tc_frag_data.literal_frag)
{
literal_pool = search_frag->tc_frag_data.literal_frag;
assert (literal_pool->fr_subtype == RELAX_LITERAL_POOL_BEGIN);
- /* Note that we set this fr_var to be a fix
+ /* Note that we set this fr_var to be a fix
chain when we created the literal pool location
as RELAX_LITERAL_POOL_BEGIN. */
frchain_to = (frchainS *) literal_pool->fr_var;
}
insert_after = literal_pool;
-
+
while (insert_after->fr_next->fr_subtype != RELAX_LITERAL_POOL_END)
insert_after = insert_after->fr_next;
dest_seg = (segT) insert_after->fr_next->fr_var;
-
+
*frag_splice = next_frag;
search_frag->fr_next = insert_after->fr_next;
insert_after->fr_next = search_frag;
@@ -7588,7 +10802,8 @@ xtensa_move_literals ()
{
symbolS *lit_sym = lit->sym;
segT dest_seg = symbol_get_frag (lit_sym)->tc_frag_data.lit_seg;
- S_SET_SEGMENT (lit_sym, dest_seg);
+ if (dest_seg)
+ S_SET_SEGMENT (lit_sym, dest_seg);
}
}
@@ -7690,6 +10905,26 @@ void
xtensa_switch_to_literal_fragment (result)
emit_state *result;
{
+ if (directive_state[directive_absolute_literals])
+ {
+ cache_literal_section (0, default_lit_sections.lit4_seg_name,
+ &default_lit_sections.lit4_seg, FALSE);
+ xtensa_switch_section_emit_state (result,
+ default_lit_sections.lit4_seg, 0);
+ }
+ else
+ xtensa_switch_to_non_abs_literal_fragment (result);
+
+ /* Do a 4-byte align here. */
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+}
+
+
+void
+xtensa_switch_to_non_abs_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
@@ -7703,15 +10938,13 @@ xtensa_switch_to_literal_fragment (result)
bfd_boolean is_fini =
(now_seg && !strcmp (segment_name (now_seg), FINI_SECTION_NAME));
-
- if (pool_location == NULL
- && !use_literal_section
+ if (pool_location == NULL
+ && !use_literal_section
&& !recursive
&& !is_init && ! is_fini)
{
- as_warn (_("inlining literal pool; "
- "specify location with .literal_position."));
+ as_bad (_("literal pool location required for text-section-literals; specify with .literal_position"));
recursive = TRUE;
xtensa_mark_literal_pool_location ();
recursive = FALSE;
@@ -7725,7 +10958,7 @@ xtensa_switch_to_literal_fragment (result)
{
cache_literal_section (init_literal_head,
default_lit_sections.init_lit_seg_name,
- &default_lit_sections.init_lit_seg);
+ &default_lit_sections.init_lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.init_lit_seg, 0);
}
@@ -7733,31 +10966,30 @@ xtensa_switch_to_literal_fragment (result)
{
cache_literal_section (fini_literal_head,
default_lit_sections.fini_lit_seg_name,
- &default_lit_sections.fini_lit_seg);
+ &default_lit_sections.fini_lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.fini_lit_seg, 0);
}
- else
+ else
{
cache_literal_section (literal_head,
default_lit_sections.lit_seg_name,
- &default_lit_sections.lit_seg);
+ &default_lit_sections.lit_seg, TRUE);
xtensa_switch_section_emit_state (result,
default_lit_sections.lit_seg, 0);
}
- if (!use_literal_section &&
- !is_init && !is_fini &&
- get_literal_pool_location (now_seg) != pool_location)
+ if (!use_literal_section
+ && !is_init && !is_fini
+ && get_literal_pool_location (now_seg) != pool_location)
{
/* Close whatever frag is there. */
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
frag_now->tc_frag_data.literal_frag = pool_location;
frag_variant (rs_fill, 0, 0, 0, NULL, 0, NULL);
+ xtensa_set_frag_assembly_state (frag_now);
}
-
- /* Do a 4 byte align here. */
- frag_align (2, 0, 0);
}
@@ -7795,17 +11027,18 @@ xtensa_restore_emit_state (state)
present, return it; otherwise, create a new one. */
static void
-cache_literal_section (head, name, seg)
+cache_literal_section (head, name, seg, is_code)
seg_list *head;
const char *name;
segT *seg;
+ bfd_boolean is_code;
{
segT current_section = now_seg;
int current_subsec = now_subseg;
if (*seg != 0)
return;
- *seg = retrieve_literal_seg (head, name);
+ *seg = retrieve_literal_seg (head, name, is_code);
subseg_set (current_section, current_subsec);
}
@@ -7814,21 +11047,22 @@ cache_literal_section (head, name, seg)
present, return it; otherwise, create a new one. */
static segT
-retrieve_literal_seg (head, name)
+retrieve_literal_seg (head, name, is_code)
seg_list *head;
const char *name;
+ bfd_boolean is_code;
{
segT ret = 0;
- assert (head);
-
ret = seg_present (name);
if (!ret)
{
ret = subseg_new (name, (subsegT) 0);
- add_seg_list (head, ret);
+ if (head)
+ add_seg_list (head, ret);
bfd_set_section_flags (stdoutput, ret, SEC_HAS_CONTENTS |
- SEC_READONLY | SEC_ALLOC | SEC_LOAD | SEC_CODE);
+ SEC_READONLY | SEC_ALLOC | SEC_LOAD
+ | (is_code ? SEC_CODE : SEC_DATA));
bfd_set_section_alignment (stdoutput, ret, 2);
}
@@ -7873,74 +11107,93 @@ add_seg_list (head, seg)
}
-/* 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);
-}
+/* Property Tables Stuff. */
+/* This function is only meaningful after xtensa_move_literals. */
static bfd_boolean
get_frag_is_literal (fragP)
const fragS *fragP;
{
assert (fragP != NULL);
- return (fragP->tc_frag_data.is_literal);
+ 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);
+ return fragP->tc_frag_data.is_insn;
+}
+
+
+bfd_boolean
+get_frag_is_no_transform (fragP)
+ fragS *fragP;
+{
+ return fragP->tc_frag_data.is_no_transform;
+}
+
+
+void
+set_frag_is_specific_opcode (fragP, is_specific_opcode)
+ fragS *fragP;
+ bfd_boolean is_specific_opcode;
+{
+ fragP->tc_frag_data.is_specific_opcode = is_specific_opcode;
+}
+
+
+void
+set_frag_is_no_transform (fragP, is_no_transform)
+ fragS *fragP;
+ bfd_boolean is_no_transform;
+{
+ fragP->tc_frag_data.is_no_transform = is_no_transform;
}
static void
-xtensa_create_property_segments (property_function, section_name_base,
- sec_type)
+xtensa_create_property_segments (property_function, end_property_function,
+ section_name_base, sec_type)
frag_predicate property_function;
- const char * section_name_base;
+ frag_predicate end_property_function;
+ const char *section_name_base;
xt_section_type sec_type;
{
segT *seclist;
/* Walk over all of the current segments.
Walk over each fragment
- For each fragment that has instructions
- Build an instruction record (append where possible). */
+ For each non-empty fragment,
+ Build a property record (append where possible). */
for (seclist = &stdoutput->sections;
seclist && *seclist;
seclist = &(*seclist)->next)
{
segT sec = *seclist;
+ flagword flags;
+
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
if (section_has_property (sec, property_function))
{
char *property_section_name =
xtensa_get_property_section_name (sec, section_name_base);
segT insn_sec = retrieve_xtensa_section (property_section_name);
segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
- xtensa_block_info **xt_blocks =
+ xtensa_block_info **xt_blocks =
&xt_seg_info->tc_segment_info_data.blocks[sec_type];
/* Walk over all of the frchains here and add new sections. */
- add_xt_block_frags (sec, insn_sec, xt_blocks, property_function);
+ add_xt_block_frags (sec, insn_sec, xt_blocks, property_function,
+ end_property_function);
}
}
@@ -7953,6 +11206,7 @@ xtensa_create_property_segments (property_function, section_name_base,
segment_info_type *seginfo;
xtensa_block_info *block;
segT sec = *seclist;
+
seginfo = seg_info (sec);
block = seginfo->tc_segment_info_data.blocks[sec_type];
@@ -7960,7 +11214,7 @@ xtensa_create_property_segments (property_function, section_name_base,
{
xtensa_block_info *cur_block;
/* This is a section with some data. */
- size_t num_recs = 0;
+ int num_recs = 0;
size_t rec_size;
for (cur_block = block; cur_block; cur_block = cur_block->next)
@@ -7981,7 +11235,7 @@ xtensa_create_property_segments (property_function, section_name_base,
size_t frag_size;
fixS *fixes;
frchainS *frchainP;
- size_t i;
+ int i;
char *frag_data;
frag_size = sizeof (fragS) + rec_size;
@@ -7993,7 +11247,7 @@ xtensa_create_property_segments (property_function, section_name_base,
fragP->fr_fix = rec_size;
fragP->fr_var = 0;
fragP->fr_type = rs_fill;
- /* the rest are zeros */
+ /* The rest are zeros. */
frchainP = seginfo->frchainP;
frchainP->frch_root = fragP;
@@ -8037,6 +11291,143 @@ xtensa_create_property_segments (property_function, section_name_base,
}
+void
+xtensa_create_xproperty_segments (flag_fn, section_name_base, sec_type)
+ frag_flags_fn flag_fn;
+ const char *section_name_base;
+ xt_section_type sec_type;
+{
+ segT *seclist;
+
+ /* Walk over all of the current segments.
+ Walk over each fragment.
+ For each fragment that has instructions,
+ build an instruction record (append where possible). */
+
+ for (seclist = &stdoutput->sections;
+ seclist && *seclist;
+ seclist = &(*seclist)->next)
+ {
+ segT sec = *seclist;
+ flagword flags;
+
+ flags = bfd_get_section_flags (stdoutput, sec);
+ if (flags & SEC_DEBUGGING)
+ continue;
+ if (!(flags & SEC_ALLOC))
+ continue;
+
+ if (section_has_xproperty (sec, flag_fn))
+ {
+ char *property_section_name =
+ xtensa_get_property_section_name (sec, section_name_base);
+ segT insn_sec = retrieve_xtensa_section (property_section_name);
+ segment_info_type *xt_seg_info = retrieve_segment_info (insn_sec);
+ xtensa_block_info **xt_blocks =
+ &xt_seg_info->tc_segment_info_data.blocks[sec_type];
+ /* Walk over all of the frchains here and add new sections. */
+ add_xt_prop_frags (sec, insn_sec, xt_blocks, flag_fn);
+ }
+ }
+
+ /* 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. */
+ int 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 + 4);
+ bfd_set_section_size (stdoutput, sec, rec_size);
+
+ /* elf_section_data (sec)->this_hdr.sh_entsize = 12; */
+
+ /* In order to make this work with the assembler, we have to build
+ some frags then build the "fixups" for it. It would be easier to
+ just set the contents then set the arlents. */
+
+ if (num_recs)
+ {
+ /* Allocate a fragment and (unfortunately) leak it. */
+ fragS *fragP;
+ size_t frag_size;
+ fixS *fixes;
+ frchainS *frchainP;
+ int i;
+ char *frag_data;
+
+ frag_size = sizeof (fragS) + rec_size;
+ fragP = (fragS *) xmalloc (frag_size);
+
+ memset (fragP, 0, frag_size);
+ fragP->fr_address = 0;
+ fragP->fr_next = NULL;
+ fragP->fr_fix = rec_size;
+ fragP->fr_var = 0;
+ fragP->fr_type = rs_fill;
+ /* 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 + 4);
+ fix->fx_addsy = section_symbol (cur_block->sec);
+ fix->fx_offset = cur_block->offset;
+ fix->fx_r_type = BFD_RELOC_32;
+ fix->fx_file = "Internal Assembly";
+ fix->fx_line = 0;
+
+ /* Write the length. */
+ md_number_to_chars (&frag_data[4 + (8+4) * i],
+ cur_block->size, 4);
+ md_number_to_chars (&frag_data[8 + (8+4) * i],
+ frag_flags_to_number (&cur_block->flags),
+ 4);
+ cur_block = cur_block->next;
+ }
+ }
+ }
+ }
+}
+
+
segment_info_type *
retrieve_segment_info (seg)
segT seg;
@@ -8132,14 +11523,38 @@ section_has_property (sec, property_function)
}
+bfd_boolean
+section_has_xproperty (sec, property_function)
+ segT sec;
+ frag_flags_fn property_function;
+{
+ segment_info_type *seginfo = seg_info (sec);
+ fragS *fragP;
+
+ if (seginfo && seginfo->frchainP)
+ {
+ for (fragP = seginfo->frchainP->frch_root; fragP; fragP = fragP->fr_next)
+ {
+ frag_flags prop_flags;
+ property_function (fragP, &prop_flags);
+ if (!xtensa_frag_flags_is_empty (&prop_flags))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+
/* Two types of block sections exist right now: literal and insns. */
void
-add_xt_block_frags (sec, xt_block_sec, xt_block, property_function)
+add_xt_block_frags (sec, xt_block_sec, xt_block, property_function,
+ end_property_function)
segT sec;
segT xt_block_sec;
xtensa_block_info **xt_block;
frag_predicate property_function;
+ frag_predicate end_property_function;
{
segment_info_type *seg_info;
segment_info_type *xt_seg_info;
@@ -8182,6 +11597,276 @@ add_xt_block_frags (sec, xt_block_sec, xt_block, property_function)
new_block->offset = fragP->fr_address;
new_block->size = fragP->fr_fix;
new_block->next = NULL;
+ xtensa_frag_flags_init (&new_block->flags);
+ *xt_block = new_block;
+ }
+ if (end_property_function
+ && end_property_function (fragP))
+ {
+ xt_block = &((*xt_block)->next);
+ }
+ }
+ }
+ }
+}
+
+
+/* Break the encapsulation of add_xt_prop_frags here. */
+
+bfd_boolean
+xtensa_frag_flags_is_empty (prop_flags)
+ const frag_flags *prop_flags;
+{
+ if (prop_flags->is_literal
+ || prop_flags->is_insn
+ || prop_flags->is_data
+ || prop_flags->is_unreachable)
+ return FALSE;
+ return TRUE;
+}
+
+
+void
+xtensa_frag_flags_init (prop_flags)
+ frag_flags *prop_flags;
+{
+ memset (prop_flags, 0, sizeof (frag_flags));
+}
+
+
+void
+get_frag_property_flags (fragP, prop_flags)
+ const fragS *fragP;
+ frag_flags *prop_flags;
+{
+ xtensa_frag_flags_init (prop_flags);
+ if (fragP->tc_frag_data.is_literal)
+ prop_flags->is_literal = TRUE;
+ if (fragP->tc_frag_data.is_unreachable)
+ {
+ prop_flags->is_unreachable = TRUE;
+ }
+ else if (fragP->tc_frag_data.is_insn)
+ {
+ prop_flags->is_insn = TRUE;
+ if (fragP->tc_frag_data.is_loop_target)
+ prop_flags->insn.is_loop_target = TRUE;
+ if (fragP->tc_frag_data.is_branch_target)
+ prop_flags->insn.is_branch_target = TRUE;
+ if (fragP->tc_frag_data.is_specific_opcode
+ || fragP->tc_frag_data.is_no_transform)
+ prop_flags->insn.is_no_transform = TRUE;
+ if (fragP->tc_frag_data.is_no_density)
+ prop_flags->insn.is_no_density = TRUE;
+ if (fragP->tc_frag_data.use_absolute_literals)
+ prop_flags->insn.is_abslit = TRUE;
+ }
+ if (fragP->tc_frag_data.is_align)
+ {
+ prop_flags->is_align = TRUE;
+ prop_flags->alignment = fragP->tc_frag_data.alignment;
+ if (xtensa_frag_flags_is_empty (prop_flags))
+ prop_flags->is_data = TRUE;
+ }
+}
+
+
+bfd_vma
+frag_flags_to_number (prop_flags)
+ const frag_flags *prop_flags;
+{
+ bfd_vma num = 0;
+ if (prop_flags->is_literal)
+ num |= XTENSA_PROP_LITERAL;
+ if (prop_flags->is_insn)
+ num |= XTENSA_PROP_INSN;
+ if (prop_flags->is_data)
+ num |= XTENSA_PROP_DATA;
+ if (prop_flags->is_unreachable)
+ num |= XTENSA_PROP_UNREACHABLE;
+ if (prop_flags->insn.is_loop_target)
+ num |= XTENSA_PROP_INSN_LOOP_TARGET;
+ if (prop_flags->insn.is_branch_target)
+ {
+ num |= XTENSA_PROP_INSN_BRANCH_TARGET;
+ num = SET_XTENSA_PROP_BT_ALIGN (num, prop_flags->insn.bt_align_priority);
+ }
+
+ if (prop_flags->insn.is_no_density)
+ num |= XTENSA_PROP_INSN_NO_DENSITY;
+ if (prop_flags->insn.is_no_transform)
+ num |= XTENSA_PROP_INSN_NO_TRANSFORM;
+ if (prop_flags->insn.is_no_reorder)
+ num |= XTENSA_PROP_INSN_NO_REORDER;
+ if (prop_flags->insn.is_abslit)
+ num |= XTENSA_PROP_INSN_ABSLIT;
+
+ if (prop_flags->is_align)
+ {
+ num |= XTENSA_PROP_ALIGN;
+ num = SET_XTENSA_PROP_ALIGNMENT (num, prop_flags->alignment);
+ }
+
+ return num;
+}
+
+
+static bfd_boolean
+xtensa_frag_flags_combinable (prop_flags_1, prop_flags_2)
+ const frag_flags *prop_flags_1;
+ const frag_flags *prop_flags_2;
+{
+ /* Cannot combine with an end marker. */
+
+ if (prop_flags_1->is_literal != prop_flags_2->is_literal)
+ return FALSE;
+ if (prop_flags_1->is_insn != prop_flags_2->is_insn)
+ return FALSE;
+ if (prop_flags_1->is_data != prop_flags_2->is_data)
+ return FALSE;
+
+ if (prop_flags_1->is_insn)
+ {
+ /* Properties of the beginning of the frag. */
+ if (prop_flags_2->insn.is_loop_target)
+ return FALSE;
+ if (prop_flags_2->insn.is_branch_target)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_density !=
+ prop_flags_2->insn.is_no_density)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_transform !=
+ prop_flags_2->insn.is_no_transform)
+ return FALSE;
+ if (prop_flags_1->insn.is_no_reorder !=
+ prop_flags_2->insn.is_no_reorder)
+ return FALSE;
+ if (prop_flags_1->insn.is_abslit !=
+ prop_flags_2->insn.is_abslit)
+ return FALSE;
+ }
+
+ if (prop_flags_1->is_align)
+ return FALSE;
+
+ return TRUE;
+}
+
+
+bfd_vma
+xt_block_aligned_size (xt_block)
+ const xtensa_block_info *xt_block;
+{
+ bfd_vma end_addr;
+ size_t align_bits;
+
+ if (!xt_block->flags.is_align)
+ return xt_block->size;
+
+ end_addr = xt_block->offset + xt_block->size;
+ align_bits = xt_block->flags.alignment;
+ end_addr = ((end_addr + ((1 << align_bits) -1)) >> align_bits) << align_bits;
+ return end_addr - xt_block->offset;
+}
+
+
+static bfd_boolean
+xtensa_xt_block_combine (xt_block, xt_block_2)
+ xtensa_block_info *xt_block;
+ const xtensa_block_info *xt_block_2;
+{
+ if (xt_block->sec != xt_block_2->sec)
+ return FALSE;
+ if (xt_block->offset + xt_block_aligned_size (xt_block)
+ != xt_block_2->offset)
+ return FALSE;
+
+ if (xt_block_2->size == 0
+ && (!xt_block_2->flags.is_unreachable
+ || xt_block->flags.is_unreachable))
+ {
+ if (xt_block_2->flags.is_align
+ && xt_block->flags.is_align)
+ {
+ /* Nothing needed. */
+ if (xt_block->flags.alignment >= xt_block_2->flags.alignment)
+ return TRUE;
+ }
+ else
+ {
+ if (xt_block_2->flags.is_align)
+ {
+ /* Push alignment to previous entry. */
+ xt_block->flags.is_align = xt_block_2->flags.is_align;
+ xt_block->flags.alignment = xt_block_2->flags.alignment;
+ }
+ return TRUE;
+ }
+ }
+ if (!xtensa_frag_flags_combinable (&xt_block->flags,
+ &xt_block_2->flags))
+ return FALSE;
+
+ xt_block->size += xt_block_2->size;
+
+ if (xt_block_2->flags.is_align)
+ {
+ xt_block->flags.is_align = TRUE;
+ xt_block->flags.alignment = xt_block_2->flags.alignment;
+ }
+
+ return TRUE;
+}
+
+
+void
+add_xt_prop_frags (sec, xt_block_sec, xt_block, property_function)
+ segT sec;
+ segT xt_block_sec;
+ xtensa_block_info **xt_block;
+ frag_flags_fn property_function;
+{
+ segment_info_type *seg_info;
+ segment_info_type *xt_seg_info;
+ bfd_vma seg_offset;
+ fragS *fragP;
+
+ xt_seg_info = retrieve_segment_info (xt_block_sec);
+ seg_info = retrieve_segment_info (sec);
+ /* Build it if needed. */
+ while (*xt_block != NULL)
+ {
+ xt_block = &(*xt_block)->next;
+ }
+ /* We are either at NULL at the beginning or at the end. */
+
+ /* Walk through the frags. */
+ seg_offset = 0;
+
+ if (seg_info->frchainP)
+ {
+ for (fragP = seg_info->frchainP->frch_root; fragP;
+ fragP = fragP->fr_next)
+ {
+ xtensa_block_info tmp_block;
+ tmp_block.sec = sec;
+ tmp_block.offset = fragP->fr_address;
+ tmp_block.size = fragP->fr_fix;
+ tmp_block.next = NULL;
+ property_function (fragP, &tmp_block.flags);
+
+ if (!xtensa_frag_flags_is_empty (&tmp_block.flags))
+ /* && fragP->fr_fix != 0) */
+ {
+ if ((*xt_block) == NULL
+ || !xtensa_xt_block_combine (*xt_block, &tmp_block))
+ {
+ xtensa_block_info *new_block;
+ if ((*xt_block) != NULL)
+ xt_block = &(*xt_block)->next;
+ new_block = (xtensa_block_info *)
+ xmalloc (sizeof (xtensa_block_info));
+ *new_block = tmp_block;
*xt_block = new_block;
}
}
@@ -8190,6 +11875,110 @@ add_xt_block_frags (sec, xt_block_sec, xt_block, property_function)
}
+/* op_placement_info_table */
+
+/* op_placement_info makes it easier to determine which
+ ops can go in which slots. */
+
+static void
+init_op_placement_info_table ()
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_insnbuf ibuf = xtensa_insnbuf_alloc (isa);
+ xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
+ int num_opcodes = xtensa_isa_num_opcodes (isa);
+
+ op_placement_table = (op_placement_info_table)
+ xmalloc (sizeof (op_placement_info) * num_opcodes);
+ assert (xtensa_isa_num_formats (isa) < MAX_FORMATS);
+
+ for (opcode = 0; opcode < num_opcodes; opcode++)
+ {
+ op_placement_info *opi = &op_placement_table[opcode];
+ /* FIXME: Make tinsn allocation dynamic. */
+ if (xtensa_opcode_num_operands (isa, opcode) >= MAX_INSN_ARGS)
+ as_fatal (_("too many operands in instruction"));
+ opi->single = XTENSA_UNDEFINED;
+ opi->single_size = 0;
+ opi->widest = XTENSA_UNDEFINED;
+ opi->widest_size = 0;
+ opi->narrowest = XTENSA_UNDEFINED;
+ opi->narrowest_size = 0x7F;
+ opi->formats = 0;
+ opi->num_formats = 0;
+ opi->issuef = 0;
+ for (fmt = 0; fmt < xtensa_isa_num_formats (isa); fmt++)
+ {
+ opi->slots[fmt] = 0;
+ for (slot = 0; slot < xtensa_format_num_slots (isa, fmt); slot++)
+ {
+ if (xtensa_opcode_encode (isa, fmt, slot, ibuf, opcode) == 0)
+ {
+ int fmt_length = xtensa_format_length (isa, fmt);
+ opi->issuef++;
+ set_bit (fmt, opi->formats);
+ set_bit (slot, opi->slots[fmt]);
+ /* opi->slot_count[fmt]++; */
+ if (fmt_length < opi->narrowest_size)
+ {
+ opi->narrowest = fmt;
+ opi->narrowest_size = fmt_length;
+ }
+ if (fmt_length > opi->widest_size)
+ {
+ opi->widest = fmt;
+ opi->widest_size = fmt_length;
+ }
+ if (xtensa_format_num_slots (isa, fmt) == 1)
+ {
+ if (opi->single_size == 0
+ || fmt_length < opi->single_size)
+ {
+ opi->single = fmt;
+ opi->single_size = fmt_length;
+ }
+ }
+ }
+ }
+ if (opi->formats)
+ opi->num_formats++;
+ }
+ }
+ xtensa_insnbuf_free (isa, ibuf);
+}
+
+
+bfd_boolean
+opcode_fits_format_slot (opcode, fmt, slot)
+ xtensa_opcode opcode;
+ xtensa_format fmt;
+ int slot;
+{
+ return bit_is_set (slot, op_placement_table[opcode].slots[fmt]);
+}
+
+
+/* If the opcode is available in a single slot format, return its size. */
+
+int
+xg_get_single_size (opcode)
+ xtensa_opcode opcode;
+{
+ assert (op_placement_table[opcode].single != XTENSA_UNDEFINED);
+ return op_placement_table[opcode].single_size;
+}
+
+
+xtensa_format
+xg_get_single_format (opcode)
+ xtensa_opcode opcode;
+{
+ return op_placement_table[opcode].single;
+}
+
+
/* Instruction Stack Functions (from "xtensa-istack.h"). */
void
@@ -8218,7 +12007,7 @@ istack_full (stack)
/* Return a pointer to the top IStack entry.
- It is an error to call this if istack_empty () is true. */
+ It is an error to call this if istack_empty () is TRUE. */
TInsn *
istack_top (stack)
@@ -8231,7 +12020,7 @@ istack_top (stack)
/* Add a new TInsn to an IStack.
- It is an error to call this if istack_full () is true. */
+ It is an error to call this if istack_full () is TRUE. */
void
istack_push (stack, insn)
@@ -8240,13 +12029,13 @@ istack_push (stack, insn)
{
int rec = stack->ninsn;
assert (!istack_full (stack));
- tinsn_copy (&stack->insn[rec], insn);
+ stack->insn[rec] = *insn;
stack->ninsn++;
}
/* Clear space for the next TInsn on the IStack and return a pointer
- to it. It is an error to call this if istack_full () is true. */
+ to it. It is an error to call this if istack_full () is TRUE. */
TInsn *
istack_push_space (stack)
@@ -8263,7 +12052,7 @@ istack_push_space (stack)
/* Remove the last pushed instruction. It is an error to call this if
- istack_empty () returns true. */
+ istack_empty () returns TRUE. */
void
istack_pop (stack)
@@ -8286,16 +12075,6 @@ tinsn_init (dst)
}
-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. */
@@ -8309,7 +12088,7 @@ tinsn_get_tok (insn, num)
}
-/* Return true if ANY of the operands in the insn are symbolic. */
+/* Return TRUE if ANY of the operands in the insn are symbolic. */
static bfd_boolean
tinsn_has_symbolic_operands (insn)
@@ -8339,6 +12118,7 @@ bfd_boolean
tinsn_has_invalid_symbolic_operands (insn)
const TInsn *insn;
{
+ xtensa_isa isa = xtensa_default_isa;
int i;
int n = insn->ntok;
@@ -8351,12 +12131,30 @@ tinsn_has_invalid_symbolic_operands (insn)
case O_register:
case O_constant:
break;
+ case O_big:
+ case O_illegal:
+ case O_absent:
+ /* Errors for these types are caught later. */
+ break;
+ case O_hi16:
+ case O_lo16:
default:
- if (i == get_relaxable_immed (insn->opcode))
- break;
- as_bad (_("invalid symbolic operand %d on '%s'"),
- i, xtensa_opcode_name (xtensa_default_isa, insn->opcode));
- return TRUE;
+ /* Symbolic immediates are only allowed on the last immediate
+ operand. At this time, CONST16 is the only opcode where we
+ support non-PC-relative relocations. (It isn't necessary
+ to complain about non-PC-relative relocations here, but
+ otherwise, no error is reported until the relocations are
+ generated, and the assembler won't get that far if there
+ are any other errors. It's nice to see all the problems
+ at once.) */
+ if (i != get_relaxable_immed (insn->opcode)
+ || (xtensa_operand_is_PCrelative (isa, insn->opcode, i) != 1
+ && insn->opcode != xtensa_const16_opcode))
+ {
+ as_bad (_("invalid symbolic operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, insn->opcode));
+ return TRUE;
+ }
}
}
return FALSE;
@@ -8383,6 +12181,8 @@ tinsn_has_complex_operands (insn)
case O_register:
case O_constant:
case O_symbol:
+ case O_lo16:
+ case O_hi16:
break;
default:
return TRUE;
@@ -8392,69 +12192,164 @@ tinsn_has_complex_operands (insn)
}
-/* Convert the constant operands in the t_insn to insnbuf.
- Return true if there is a symbol in the immediate field.
+/* Convert the constant operands in the tinsn to insnbuf.
+ Return TRUE if there is a symbol in the immediate field.
- Before this is called,
+ Before this is called,
1) the number of operands are correct
- 2) the t_insn is a ITYPE_INSN
+ 2) the tinsn is a ITYPE_INSN
3) ONLY the relaxable_ is built
4) All operands are O_constant, O_symbol. All constants fit
The return value tells whether there are any remaining O_symbols. */
static bfd_boolean
-tinsn_to_insnbuf (t_insn, insnbuf)
- TInsn *t_insn;
+tinsn_to_insnbuf (tinsn, insnbuf)
+ TInsn *tinsn;
xtensa_insnbuf insnbuf;
{
+ static xtensa_insnbuf slotbuf = 0;
xtensa_isa isa = xtensa_default_isa;
- xtensa_opcode opcode = t_insn->opcode;
+ xtensa_opcode opcode = tinsn->opcode;
+ xtensa_format fmt = xg_get_single_format (opcode);
bfd_boolean has_fixup = FALSE;
- int noperands = xtensa_num_operands (isa, opcode);
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
int i;
uint32 opnd_value;
char *file_name;
int line;
- assert (t_insn->insn_type == ITYPE_INSN);
- if (noperands != t_insn->ntok)
+ if (!slotbuf)
+ slotbuf = xtensa_insnbuf_alloc (isa);
+
+ assert (tinsn->insn_type == ITYPE_INSN);
+ if (noperands != tinsn->ntok)
as_fatal (_("operand number mismatch"));
- xtensa_encode_insn (isa, opcode, insnbuf);
+ if (xtensa_opcode_encode (isa, fmt, 0, slotbuf, opcode))
+ as_fatal (_("cannot encode opcode"));
for (i = 0; i < noperands; ++i)
{
- expressionS *expr = &t_insn->tok[i];
- xtensa_operand operand = xtensa_get_operand (isa, opcode, i);
+ expressionS *expr = &tinsn->tok[i];
switch (expr->X_op)
{
case O_register:
- /* The register number has already been checked in
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ /* The register number has already been checked in
expression_maybe_register, so we don't need to check here. */
opnd_value = expr->X_add_number;
- (void) xtensa_operand_encode (operand, &opnd_value);
- xtensa_operand_set_field (operand, insnbuf, opnd_value);
+ (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
+ xtensa_operand_set_field (isa, opcode, i, fmt, 0,
+ slotbuf, opnd_value);
break;
case O_constant:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
as_where (&file_name, &line);
/* It is a constant and we called this function,
then we have to try to fit it. */
- xtensa_insnbuf_set_operand (insnbuf, opcode, operand,
+ xtensa_insnbuf_set_operand (slotbuf, fmt, 0, opcode, i,
+ expr->X_add_number, file_name, line);
+ break;
+
+ default:
+ has_fixup = TRUE;
+ break;
+ }
+ }
+
+ xtensa_format_encode (isa, fmt, insnbuf);
+ xtensa_format_set_slot (isa, fmt, 0, insnbuf, slotbuf);
+
+ return has_fixup;
+}
+
+
+/* Convert the constant operands in the tinsn to slotbuf.
+ Return TRUE if there is a symbol in the immediate field.
+ (Eventually this should replace tinsn_to_insnbuf.) */
+
+/* Before this is called,
+ 1) the number of operands are correct
+ 2) the tinsn is a ITYPE_INSN
+ 3) ONLY the relaxable_ is built
+ 4) All operands are
+ O_constant, O_symbol
+ All constants fit
+
+ The return value tells whether there are any remaining O_symbols. */
+
+static bfd_boolean
+tinsn_to_slotbuf (fmt, slot, tinsn, slotbuf)
+ xtensa_format fmt;
+ int slot;
+ TInsn *tinsn;
+ xtensa_insnbuf slotbuf;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_opcode opcode = tinsn->opcode;
+ bfd_boolean has_fixup = FALSE;
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
+ int i;
+
+ *((int *) &slotbuf[0]) = 0;
+ *((int *) &slotbuf[1]) = 0;
+ assert (tinsn->insn_type == ITYPE_INSN);
+ if (noperands != tinsn->ntok)
+ as_fatal (_("operand number mismatch"));
+
+ if (xtensa_opcode_encode (isa, fmt, slot, slotbuf, opcode))
+ {
+ as_bad (_("cannot encode opcode \"%s\" in the given format \"%s\""),
+ xtensa_opcode_name (isa, opcode), xtensa_format_name (isa, fmt));
+ return FALSE;
+ }
+
+ for (i = 0; i < noperands; i++)
+ {
+ expressionS *expr = &tinsn->tok[i];
+ int rc, line;
+ char *file_name;
+ uint32 opnd_value;
+
+ switch (expr->X_op)
+ {
+ case O_register:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ /* The register number has already been checked in
+ expression_maybe_register, so we don't need to check here. */
+ opnd_value = expr->X_add_number;
+ (void) xtensa_operand_encode (isa, opcode, i, &opnd_value);
+ rc = xtensa_operand_set_field (isa, opcode, i, fmt, slot, slotbuf,
+ opnd_value);
+ if (rc != 0)
+ as_warn (_("xtensa-isa failure: %s"), xtensa_isa_error_msg (isa));
+ break;
+
+ case O_constant:
+ if (xtensa_operand_is_visible (isa, opcode, i) == 0)
+ break;
+ as_where (&file_name, &line);
+ /* It is a constant and we called this function
+ then we have to try to fit it. */
+ xtensa_insnbuf_set_operand (slotbuf, fmt, slot, opcode, i,
expr->X_add_number, file_name, line);
break;
- case O_symbol:
default:
has_fixup = TRUE;
break;
}
}
+
return has_fixup;
}
-/* Check the instruction arguments. Return true on failure. */
+/* Check the instruction arguments. Return TRUE on failure. */
bfd_boolean
tinsn_check_arguments (insn)
@@ -8469,13 +12364,13 @@ tinsn_check_arguments (insn)
return TRUE;
}
- if (xtensa_num_operands (isa, opcode) > insn->ntok)
+ if (xtensa_opcode_num_operands (isa, opcode) > insn->ntok)
{
as_bad (_("too few operands"));
return TRUE;
}
- if (xtensa_num_operands (isa, opcode) < insn->ntok)
+ if (xtensa_opcode_num_operands (isa, opcode) < insn->ntok)
{
as_bad (_("too many operands"));
return TRUE;
@@ -8487,31 +12382,42 @@ tinsn_check_arguments (insn)
/* Load an instruction from its encoded form. */
static void
-tinsn_from_chars (t_insn, f)
- TInsn *t_insn;
+tinsn_from_chars (tinsn, f, slot)
+ TInsn *tinsn;
char *f;
+ int slot;
{
- static xtensa_insnbuf insnbuf = NULL;
- int i;
- xtensa_opcode opcode;
- xtensa_isa isa = xtensa_default_isa;
+ vliw_insn vinsn;
+
+ xg_init_vinsn (&vinsn);
+ vinsn_from_chars (&vinsn, f);
+
+ *tinsn = vinsn.slots[slot];
+ xg_free_vinsn (&vinsn);
+}
- if (!insnbuf)
- insnbuf = xtensa_insnbuf_alloc (isa);
- xtensa_insnbuf_from_chars (isa, insnbuf, f);
- opcode = xtensa_decode_insn (isa, insnbuf);
+static void
+tinsn_from_insnbuf (tinsn, slotbuf, fmt, slot)
+ TInsn *tinsn;
+ xtensa_insnbuf slotbuf;
+ xtensa_format fmt;
+ int slot;
+{
+ int i;
+ xtensa_isa isa = xtensa_default_isa;
/* Find the immed. */
- tinsn_init (t_insn);
- t_insn->insn_type = ITYPE_INSN;
- t_insn->is_specific_opcode = FALSE; /* Must not be specific. */
- t_insn->opcode = opcode;
- t_insn->ntok = xtensa_num_operands (isa, opcode);
- for (i = 0; i < t_insn->ntok; i++)
+ tinsn_init (tinsn);
+ tinsn->insn_type = ITYPE_INSN;
+ tinsn->is_specific_opcode = FALSE; /* must not be specific */
+ tinsn->opcode = xtensa_opcode_decode (isa, fmt, slot, slotbuf);
+ tinsn->ntok = xtensa_opcode_num_operands (isa, tinsn->opcode);
+ for (i = 0; i < tinsn->ntok; i++)
{
- set_expr_const (&t_insn->tok[i],
- xtensa_insnbuf_get_operand (insnbuf, opcode, i));
+ set_expr_const (&tinsn->tok[i],
+ xtensa_insnbuf_get_operand (slotbuf, fmt, slot,
+ tinsn->opcode, i));
}
}
@@ -8519,18 +12425,33 @@ tinsn_from_chars (t_insn, f)
/* 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;
+tinsn_immed_from_frag (tinsn, fragP, slot)
+ TInsn *tinsn;
fragS *fragP;
+ int slot;
{
- xtensa_opcode opcode = t_insn->opcode;
+ xtensa_opcode opcode = tinsn->opcode;
int opnum;
- if (fragP->fr_symbol)
+ if (fragP->tc_frag_data.slot_symbols[slot])
{
opnum = get_relaxable_immed (opcode);
- set_expr_symbol_offset (&t_insn->tok[opnum],
- fragP->fr_symbol, fragP->fr_offset);
+ assert (opnum >= 0);
+ if (fragP->tc_frag_data.slot_sub_symbols[slot])
+ {
+ set_expr_symbol_offset_diff
+ (&tinsn->tok[opnum],
+ fragP->tc_frag_data.slot_symbols[slot],
+ fragP->tc_frag_data.slot_sub_symbols[slot],
+ fragP->tc_frag_data.slot_offsets[slot]);
+ }
+ else
+ {
+ set_expr_symbol_offset
+ (&tinsn->tok[opnum],
+ fragP->tc_frag_data.slot_symbols[slot],
+ fragP->tc_frag_data.slot_offsets[slot]);
+ }
}
}
@@ -8544,9 +12465,9 @@ get_num_stack_text_bytes (istack)
for (i = 0; i < istack->ninsn; i++)
{
- TInsn *t_insn = &istack->insn[i];
- if (t_insn->insn_type == ITYPE_INSN)
- text_bytes += xg_get_insn_size (t_insn);
+ TInsn *tinsn = &istack->insn[i];
+ if (tinsn->insn_type == ITYPE_INSN)
+ text_bytes += xg_get_single_size (tinsn->opcode);
}
return text_bytes;
}
@@ -8561,18 +12482,234 @@ get_num_stack_literal_bytes (istack)
for (i = 0; i < istack->ninsn; i++)
{
- TInsn *t_insn = &istack->insn[i];
-
- if (t_insn->insn_type == ITYPE_LITERAL && t_insn->ntok == 1)
+ TInsn *tinsn = &istack->insn[i];
+ if (tinsn->insn_type == ITYPE_LITERAL && tinsn->ntok == 1)
lit_bytes += 4;
}
return lit_bytes;
}
+/* vliw_insn functions. */
+
+void
+xg_init_vinsn (v)
+ vliw_insn *v;
+{
+ int i;
+ xtensa_isa isa = xtensa_default_isa;
+
+ xg_clear_vinsn (v);
+
+ v->insnbuf = xtensa_insnbuf_alloc (isa);
+ if (v->insnbuf == NULL)
+ as_fatal (_("out of memory"));
+
+ for (i = 0; i < MAX_SLOTS; i++)
+ {
+ tinsn_init (&v->slots[i]);
+ v->slots[i].opcode = XTENSA_UNDEFINED;
+ v->slotbuf[i] = xtensa_insnbuf_alloc (isa);
+ if (v->slotbuf[i] == NULL)
+ as_fatal (_("out of memory"));
+ }
+}
+
+
+void
+xg_clear_vinsn (v)
+ vliw_insn *v;
+{
+ int i;
+ v->format = XTENSA_UNDEFINED;
+ v->num_slots = 0;
+ v->inside_bundle = FALSE;
+
+ if (xt_saved_debug_type != DEBUG_NONE)
+ debug_type = xt_saved_debug_type;
+
+ for (i = 0; i < MAX_SLOTS; i++)
+ {
+ memset (&v->slots[i], 0, sizeof (TInsn));
+ v->slots[i].opcode = XTENSA_UNDEFINED;
+ }
+}
+
+
+bfd_boolean
+vinsn_has_specific_opcodes (v)
+ vliw_insn *v;
+{
+ int i;
+
+ for (i = 0; i < v->num_slots; i++)
+ {
+ if (v->slots[i].is_specific_opcode)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+void
+xg_free_vinsn (v)
+ vliw_insn *v;
+{
+ int i;
+ xtensa_insnbuf_free (xtensa_default_isa, v->insnbuf);
+ for (i = 0; i < MAX_SLOTS; i++)
+ xtensa_insnbuf_free (xtensa_default_isa, v->slotbuf[i]);
+}
+
+
+/* Before this is called, we should have
+ filled out the following fields:
+
+ 1) the number of operands for each opcode are correct
+ 2) the tinsn in the slots are ITYPE_INSN
+ 3) ONLY the relaxable_ is built
+ 4) All operands are
+ O_constant, O_symbol
+ All constants fit
+
+ The return value tells whether there are any remaining O_symbols. */
+
+static bfd_boolean
+vinsn_to_insnbuf (vinsn, frag_offset, fragP, record_fixup)
+ vliw_insn *vinsn;
+ char *frag_offset;
+ fragS *fragP;
+ bfd_boolean record_fixup;
+{
+ xtensa_isa isa = xtensa_default_isa;
+ xtensa_format fmt = vinsn->format;
+ xtensa_insnbuf insnbuf = vinsn->insnbuf;
+ int slot;
+ bfd_boolean has_fixup = FALSE;
+
+ xtensa_format_encode (isa, fmt, insnbuf);
+
+ for (slot = 0; slot < vinsn->num_slots; slot++)
+ {
+ TInsn *tinsn = &vinsn->slots[slot];
+ bfd_boolean tinsn_has_fixup =
+ tinsn_to_slotbuf (vinsn->format, slot, tinsn,
+ vinsn->slotbuf[slot]);
+
+ xtensa_format_set_slot (isa, fmt, slot,
+ insnbuf, vinsn->slotbuf[slot]);
+ /* tinsn_has_fixup tracks if there is a fixup at all.
+ record_fixup controls globally. I.E., we use this
+ function from several places, some of which are after
+ fixups have already been recorded. Finally,
+ tinsn->record_fixup controls based on the individual ops,
+ which may or may not need it based on the relaxation
+ requirements. */
+ if (tinsn_has_fixup && record_fixup)
+ {
+ int i;
+ xtensa_opcode opcode = tinsn->opcode;
+ int noperands = xtensa_opcode_num_operands (isa, opcode);
+ has_fixup = TRUE;
+
+ for (i = 0; i < noperands; i++)
+ {
+ expressionS* expr = &tinsn->tok[i];
+ switch (expr->X_op)
+ {
+ case O_symbol:
+ case O_lo16:
+ case O_hi16:
+ if (get_relaxable_immed (opcode) == i)
+ {
+ if (tinsn->record_fix || expr->X_op != O_symbol)
+ {
+ if (!xg_add_opcode_fix
+ (tinsn, i, fmt, slot, expr, fragP,
+ frag_offset - fragP->fr_literal))
+ as_bad (_("instruction with constant operands does not fit"));
+ }
+ else
+ {
+ tinsn->symbol = expr->X_add_symbol;
+ tinsn->offset = expr->X_add_number;
+ }
+ }
+ else
+ as_bad (_("invalid operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, opcode));
+ break;
+
+ case O_constant:
+ case O_register:
+ break;
+
+ case O_subtract:
+ if (get_relaxable_immed (opcode) == i)
+ {
+ if (tinsn->record_fix)
+ as_bad (_("invalid subtract operand"));
+ else
+ {
+ tinsn->symbol = expr->X_add_symbol;
+ tinsn->sub_symbol = expr->X_op_symbol;
+ tinsn->offset = expr->X_add_number;
+ }
+ }
+ else
+ as_bad (_("invalid operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, opcode));
+ break;
+
+ default:
+ as_bad (_("invalid expression for operand %d on '%s'"),
+ i, xtensa_opcode_name (isa, opcode));
+ break;
+ }
+ }
+ }
+ }
+
+ return has_fixup;
+}
+
+
+static void
+vinsn_from_chars (vinsn, f)
+ vliw_insn *vinsn;
+ char *f;
+{
+ static xtensa_insnbuf insnbuf = NULL;
+ static xtensa_insnbuf slotbuf = NULL;
+ int i;
+ xtensa_format fmt;
+ xtensa_isa isa = xtensa_default_isa;
+
+ if (!insnbuf)
+ {
+ insnbuf = xtensa_insnbuf_alloc (isa);
+ slotbuf = xtensa_insnbuf_alloc (isa);
+ }
+
+ xtensa_insnbuf_from_chars (isa, insnbuf, f, 0);
+ fmt = xtensa_format_decode (isa, insnbuf);
+ if (fmt == XTENSA_UNDEFINED)
+ as_fatal (_("cannot decode instruction format"));
+ vinsn->format = fmt;
+ vinsn->num_slots = xtensa_format_num_slots (isa, fmt);
+
+ for (i = 0; i < vinsn->num_slots; i++)
+ {
+ TInsn *tinsn = &vinsn->slots[i];
+ xtensa_format_get_slot (isa, fmt, i, insnbuf, slotbuf);
+ tinsn_from_insnbuf (tinsn, slotbuf, fmt, i);
+ }
+}
+
+
/* Expression utilities. */
-/* Return true if the expression is an integer constant. */
+/* Return TRUE if the expression is an integer constant. */
bfd_boolean
expr_is_const (s)
@@ -8583,7 +12720,7 @@ expr_is_const (s)
/* Get the expression constant.
- Calling this is illegal if expr_is_const () returns true. */
+ Calling this is illegal if expr_is_const () returns TRUE. */
offsetT
get_expr_const (s)
@@ -8608,6 +12745,26 @@ set_expr_const (s, val)
}
+bfd_boolean
+expr_is_register (s)
+ const expressionS *s;
+{
+ return (s->X_op == O_register);
+}
+
+
+/* Get the expression constant.
+ Calling this is illegal if expr_is_const () returns TRUE. */
+
+offsetT
+get_expr_register (s)
+ const expressionS *s;
+{
+ assert (expr_is_register (s));
+ return s->X_add_number;
+}
+
+
/* Set the expression to a symbol + constant offset. */
void
@@ -8623,6 +12780,24 @@ set_expr_symbol_offset (s, sym, offset)
}
+/* Set the expression to symbol - minus_sym + offset. */
+
+void
+set_expr_symbol_offset_diff (s, sym, minus_sym, offset)
+ expressionS *s;
+ symbolS *sym;
+ symbolS *minus_sym;
+ offsetT offset;
+{
+ s->X_op = O_subtract;
+ s->X_add_symbol = sym;
+ s->X_op_symbol = minus_sym; /* unused */
+ s->X_add_number = offset;
+}
+
+
+/* Return TRUE if the two expressions are equal. */
+
bfd_boolean
expr_is_equal (s1, s2)
expressionS *s1;
@@ -8663,7 +12838,7 @@ struct rename_section_struct
static struct rename_section_struct *section_rename;
-/* Parse the string oldname=new_name:oldname2=new_name2
+/* Parse the string oldname=new_name:oldname2=new_name2
and call add_section_rename. */
void
@@ -8741,8 +12916,10 @@ xtensa_section_rename (name)
struct rename_section_struct *r = section_rename;
for (r = section_rename; r != NULL; r = r->next)
- if (strcmp (r->old_name, name) == 0)
- return r->new_name;
+ {
+ if (strcmp (r->old_name, name) == 0)
+ return r->new_name;
+ }
return name;
}
diff --git a/gas/config/tc-xtensa.h b/gas/config/tc-xtensa.h
index cf4a3507b1..3be75c74f3 100644
--- a/gas/config/tc-xtensa.h
+++ b/gas/config/tc-xtensa.h
@@ -1,5 +1,5 @@
/* tc-xtensa.h -- Header file for tc-xtensa.c.
- Copyright (C) 2003 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
@@ -33,61 +33,250 @@ struct fix;
#error Xtensa support requires ELF object format
#endif
+#include "xtensa-isa.h"
#include "xtensa-config.h"
#define TARGET_BYTES_BIG_ENDIAN XCHAL_HAVE_BE
+/* Maximum number of opcode slots in a VLIW instruction. */
+#define MAX_SLOTS 31
+
+
+/* For all xtensa relax states except RELAX_DESIRE_ALIGN and
+ RELAX_DESIRE_ALIGN_IF_TARGET, the amount a frag might grow is stored
+ in the fr_var field. For the two exceptions, fr_var is a float value
+ that records the frequency with which the following instruction is
+ executed as a branch target. The aligner uses this information to
+ tell which targets are most important to be aligned. */
+
+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_CHECK_ALIGN_NEXT_OPCODE,
+ /* The next non-empty frag contains a loop instruction. Check to see
+ if it is correctly aligned, but do not align it. */
+
+ RELAX_DESIRE_ALIGN_IF_TARGET,
+ /* These are placed in front of labels and converted to either
+ 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. Before
+ relaxation begins, they are turned into either NOPs for branches
+ immediately followed by RETW or RETW.N or rs_fills of 0. This is
+ used to avoid a hardware bug in some early versions of the
+ processor. */
+
+ RELAX_ADD_NOP_IF_PRE_LOOP_END,
+ /* These are placed after JX instructions. Before relaxation begins,
+ they are turned into either NOPs, if the JX is one instruction
+ before a loop end label, or rs_fills of 0. This is used to avoid a
+ hardware interlock issue prior to Xtensa version T1040. */
+
+ RELAX_ADD_NOP_IF_SHORT_LOOP,
+ /* These are placed after LOOP instructions and turned into NOPs 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 they are turned into rs_fills 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 and turned into NOPs if
+ there are less than 12 bytes to the end of some other loop's end.
+ Otherwise they are turned into rs_fills of 0 before relaxation
+ begins. This is used to avoid hardware bug PR3830. */
+
+ RELAX_DESIRE_ALIGN,
+ /* The next fragment would like its first instruction to NOT cross an
+ instruction fetch boundary. */
+
+ RELAX_MAYBE_DESIRE_ALIGN,
+ /* The next fragment might like its first instruction to NOT cross an
+ instruction fetch boundary. These are placed after a branch that
+ might be relaxed. If the branch is relaxed, then this frag will be
+ a branch target and this frag will be changed to RELAX_DESIRE_ALIGN
+ frag. */
+
+ 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. */
+
+ RELAX_SLOTS,
+ /* There are instructions within the last VLIW instruction that need
+ relaxation. Find the relaxation based on the slot info in
+ xtensa_frag_type. Relaxations that deal with particular opcodes
+ are slot-based (e.g., converting a MOVI to an L32R). Relaxations
+ that deal with entire instructions, such as alignment, are not
+ slot-based. */
+
+ RELAX_FILL_NOP,
+ /* This marks the location of a pipeline stall. We can fill these guys
+ in for alignment of any size. */
+
+ RELAX_UNREACHABLE,
+ /* This marks the location as unreachable. The assembler may widen or
+ narrow this area to meet alignment requirements of nearby
+ instructions. */
+
+ RELAX_MAYBE_UNREACHABLE,
+ /* This marks the location as possibly unreachable. These are placed
+ after a branch that may be relaxed into a branch and jump. If the
+ branch is relaxed, then this frag will be converted to a
+ RELAX_UNREACHABLE frag. */
+
+ RELAX_NONE
+};
+
+/* 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)
+
struct xtensa_frag_type
{
- unsigned is_literal:1;
- unsigned is_text:1;
- unsigned is_loop_target:1;
- unsigned is_branch_target:1;
- unsigned is_insn:1;
+ /* Info about the current state of assembly, e.g., transform,
+ absolute_literals, etc. These need to be passed to the backend and
+ then to the object file.
+
+ When is_assembly_state_set is false, the frag inherits some of the
+ state settings from the previous frag in this segment. Because it
+ is not possible to intercept all fragment closures (frag_more and
+ frag_append_1_char can close a frag), we use a pass after initial
+ assembly to fill in the assembly states. */
+
+ unsigned int is_assembly_state_set : 1;
+ unsigned int is_no_density : 1;
+ unsigned int is_no_transform : 1;
+ unsigned int use_absolute_literals : 1;
+
+ /* Inhibits relaxation of machine-dependent alignment frags the
+ first time through a relaxation.... */
+ unsigned int relax_seen : 1;
+
+ /* Infomation that is needed in the object file and set when known. */
+ unsigned int is_literal : 1;
+ unsigned int is_loop_target : 1;
+ unsigned int is_branch_target : 1;
+ unsigned int is_insn : 1;
+ unsigned int is_unreachable : 1;
- /* Info about the current state of assembly, i.e., density, relax,
- generics, freeregs, longcalls. These need to be passed to the
- backend and then to the linking file. */
+ unsigned int is_specific_opcode : 1; /* also implies no_transform */
- unsigned is_no_density:1;
- unsigned is_relax:1;
- unsigned is_generics:1;
- unsigned is_longcalls:1;
+ unsigned int is_align : 1;
+ unsigned int is_text_align : 1;
+ unsigned int alignment : 5;
+
+ /* A frag with this bit set is the first in a loop that actually
+ contains an instruction. */
+ unsigned int is_first_loop_insn : 1;
/* For text fragments that can generate literals at relax time, this
variable points to the frag where the literal will be stored. For
literal frags, this variable points to the nearest literal pool
location frag. This literal frag will be moved to after this
location. */
-
fragS *literal_frag;
/* The destination segment for literal frags. (Note that this is only
valid after xtensa_move_literals. */
-
segT lit_seg;
/* For the relaxation scheme, some literal fragments can have their
expansions modified by an instruction that relaxes. */
-
- unsigned text_expansion;
- unsigned literal_expansion;
- unsigned unreported_expansion;
+ int text_expansion[MAX_SLOTS];
+ int literal_expansion[MAX_SLOTS];
+ int unreported_expansion;
+
+ /* For text fragments that can generate literals at relax time: */
+ fragS *literal_frags[MAX_SLOTS];
+ enum xtensa_relax_statesE slot_subtypes[MAX_SLOTS];
+ symbolS *slot_symbols[MAX_SLOTS];
+ symbolS *slot_sub_symbols[MAX_SLOTS];
+ offsetT slot_offsets[MAX_SLOTS];
+
+ /* The global aligner needs to walk backward through the list of
+ frags. This field is only valid after xtensa_end. */
+ fragS *fr_prev;
};
-typedef struct xtensa_block_info_struct
+
+/* For VLIW support, we need to know what slot a fixup applies to. */
+typedef struct xtensa_fix_data_struct
+{
+ int slot;
+ symbolS *X_add_symbol;
+ offsetT X_add_number;
+} xtensa_fix_data;
+
+
+/* Structure to record xtensa-specific symbol information. */
+typedef struct xtensa_symfield_type
{
- segT sec;
- bfd_vma offset;
- size_t size;
- struct xtensa_block_info_struct *next;
-} xtensa_block_info;
+ unsigned int is_loop_target : 1;
+ unsigned int is_branch_target : 1;
+} xtensa_symfield_type;
+
+
+/* Structure for saving information about a block of property data
+ for frags that have the same flags. The forward reference is
+ in this header file. The actual definition is in tc-xtensa.c. */
+struct xtensa_block_info_struct;
+typedef struct xtensa_block_info_struct xtensa_block_info;
+
+/* Property section types. */
typedef enum
{
- xt_insn_sec,
xt_literal_sec,
+ xt_prop_sec,
max_xt_sec
} xt_section_type;
@@ -97,17 +286,9 @@ typedef struct xtensa_segment_info_struct
xtensa_block_info *blocks[max_xt_sec];
} xtensa_segment_info;
-typedef struct xtensa_symfield_type_struct
-{
- unsigned int plt : 1;
- unsigned int is_loop_target : 1;
- unsigned int is_branch_target : 1;
-} xtensa_symfield_type;
-
/* Section renaming is only supported in Tensilica's version of GAS. */
-#define XTENSA_SECTION_RENAME 1
-#ifdef XTENSA_SECTION_RENAME
+#ifdef XTENSA_SECTION_RENAME
extern const char *xtensa_section_rename
PARAMS ((const char *));
#else
@@ -118,10 +299,12 @@ extern const char *xtensa_section_rename
extern const char *xtensa_target_format
PARAMS ((void));
+extern void xtensa_init_fix_data
+ PARAMS ((struct fix *));
extern void xtensa_frag_init
PARAMS ((fragS *));
-extern void xtensa_cons_fix_new
- PARAMS ((fragS *, int, int, expressionS *));
+extern int xtensa_force_relocation
+ PARAMS ((struct fix *));
extern void xtensa_frob_label
PARAMS ((struct symbol *));
extern void xtensa_end
@@ -138,19 +321,32 @@ extern void xtensa_symbol_new_hook
PARAMS ((symbolS *));
extern long xtensa_relax_frag
PARAMS ((fragS *, long, int *));
+extern void xtensa_elf_section_change_hook
+ PARAMS ((void));
+extern int xtensa_unrecognized_line
+ PARAMS ((int));
+extern bfd_boolean xtensa_check_inside_bundle
+ PARAMS ((void));
+extern void xtensa_handle_align
+ PARAMS ((fragS *));
#define TARGET_FORMAT xtensa_target_format ()
#define TARGET_ARCH bfd_arch_xtensa
#define TC_SEGMENT_INFO_TYPE xtensa_segment_info
-#define TC_SYMFIELD_TYPE xtensa_symfield_type
+#define TC_SYMFIELD_TYPE struct xtensa_symfield_type
+#define TC_FIX_TYPE xtensa_fix_data
+#define TC_INIT_FIX_DATA(x) xtensa_init_fix_data (x)
#define TC_FRAG_TYPE struct xtensa_frag_type
#define TC_FRAG_INIT(frag) xtensa_frag_init (frag)
-#define TC_CONS_FIX_NEW xtensa_cons_fix_new
+#define TC_FORCE_RELOCATION(fix) xtensa_force_relocation (fix)
+#define NO_PSEUDO_DOT xtensa_check_inside_bundle ()
#define tc_canonicalize_symbol_name(s) xtensa_section_rename (s)
#define tc_init_after_args() xtensa_file_arch_init (stdoutput)
#define tc_fix_adjustable(fix) xtensa_fix_adjustable (fix)
#define tc_frob_label(sym) xtensa_frob_label (sym)
-#define tc_symbol_new_hook(s) xtensa_symbol_new_hook (s)
+#define tc_unrecognized_line(ch) xtensa_unrecognized_line (ch)
+#define md_do_align(a,b,c,d,e) xtensa_flush_pending_output ()
+#define md_elf_section_change_hook xtensa_elf_section_change_hook
#define md_elf_section_rename(name) xtensa_section_rename (name)
#define md_end xtensa_end
#define md_flush_pending_output() xtensa_flush_pending_output ()
@@ -158,6 +354,7 @@ extern long xtensa_relax_frag
#define TEXT_SECTION_NAME xtensa_section_rename (".text")
#define DATA_SECTION_NAME xtensa_section_rename (".data")
#define BSS_SECTION_NAME xtensa_section_rename (".bss")
+#define HANDLE_ALIGN(fragP) xtensa_handle_align (fragP)
/* The renumber_section function must be mapped over all the sections
@@ -188,7 +385,66 @@ extern long xtensa_relax_frag
#define DOUBLESLASH_LINE_COMMENTS
#define TC_HANDLES_FX_DONE
#define TC_FINALIZE_SYMS_BEFORE_SIZE_SEG 0
-
+#define TC_LINKRELAX_FIXUP(SEG) 0
#define MD_APPLY_SYM_VALUE(FIX) 0
+#define SUB_SEGMENT_ALIGN(SEG, FRCHAIN) 0
+
+
+/* Resource reservation info functions. */
+
+/* Returns the number of copies of a particular unit. */
+typedef int (*unit_num_copies_func) (void *, xtensa_funcUnit);
+
+/* Returns the number of units the opcode uses. */
+typedef int (*opcode_num_units_func) (void *, xtensa_opcode);
+
+/* Given an opcode and an index into the opcode's funcUnit list,
+ returns the unit used for the index. */
+typedef int (*opcode_funcUnit_use_unit_func) (void *, xtensa_opcode, int);
+
+/* Given an opcode and an index into the opcode's funcUnit list,
+ returns the cycle during which the unit is used. */
+typedef int (*opcode_funcUnit_use_stage_func) (void *, xtensa_opcode, int);
+
+/* The above typedefs parameterize the resource_table so that the
+ optional scheduler doesn't need its own resource reservation system.
+
+ For simple resource checking, which is all that happens normally,
+ the functions will be as follows (with some wrapping to make the
+ interface more convenient):
+
+ unit_num_copies_func = xtensa_funcUnit_num_copies
+ opcode_num_units_func = xtensa_opcode_num_funcUnit_uses
+ opcode_funcUnit_use_unit_func = xtensa_opcode_funcUnit_use->unit
+ opcode_funcUnit_use_stage_func = xtensa_opcode_funcUnit_use->stage
+
+ Of course the optional scheduler has its own reservation table
+ and functions. */
+
+int opcode_funcUnit_use_unit PARAMS ((void *, xtensa_opcode, int));
+int opcode_funcUnit_use_stage PARAMS ((void *, xtensa_opcode, int));
+
+typedef struct
+{
+ void *data;
+ int cycles;
+ int allocated_cycles;
+ int num_units;
+ unit_num_copies_func unit_num_copies;
+ opcode_num_units_func opcode_num_units;
+ opcode_funcUnit_use_unit_func opcode_unit_use;
+ opcode_funcUnit_use_stage_func opcode_unit_stage;
+ char **units;
+} resource_table;
+
+resource_table *new_resource_table
+ PARAMS ((void *, int, int, unit_num_copies_func, opcode_num_units_func,
+ opcode_funcUnit_use_unit_func, opcode_funcUnit_use_stage_func));
+void resize_resource_table PARAMS ((resource_table *, int));
+void clear_resource_table PARAMS ((resource_table *));
+bfd_boolean resources_available
+ PARAMS ((resource_table *, xtensa_opcode, int));
+void reserve_resources PARAMS ((resource_table *, xtensa_opcode, int));
+void release_resources PARAMS ((resource_table *, xtensa_opcode, int));
#endif /* TC_XTENSA */
diff --git a/gas/config/xtensa-istack.h b/gas/config/xtensa-istack.h
index a1cca2ea70..bb9f989e37 100644
--- a/gas/config/xtensa-istack.h
+++ b/gas/config/xtensa-istack.h
@@ -1,5 +1,5 @@
/* Declarations for stacks of tokenized Xtensa instructions.
- Copyright (C) 2003 Free Software Foundation, Inc.
+ Copyright (C) 2003, 2004 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
@@ -21,10 +21,11 @@
#ifndef XTENSA_ISTACK_H
#define XTENSA_ISTACK_H
+#include "dwarf2dbg.h"
#include "xtensa-isa.h"
#define MAX_ISTACK 12
-#define MAX_INSN_ARGS 6
+#define MAX_INSN_ARGS 10
enum itype_enum
{
@@ -40,11 +41,25 @@ enum itype_enum
typedef struct tinsn_struct
{
enum itype_enum insn_type;
-
- bfd_boolean is_specific_opcode;
+
xtensa_opcode opcode; /* Literals have an invalid opcode. */
+ bfd_boolean is_specific_opcode;
+ bfd_boolean keep_wide;
int ntok;
expressionS tok[MAX_INSN_ARGS];
+ struct dwarf2_line_info loc;
+
+ struct fixP *fixup;
+
+ /* Filled out by relaxation_requirements: */
+ bfd_boolean record_fix;
+ enum xtensa_relax_statesE subtype;
+ int literal_space;
+ /* Filled out by vinsn_to_insnbuf: */
+ symbolS *symbol;
+ symbolS *sub_symbol;
+ offsetT offset;
+ fragS *literal_frag;
} TInsn;
@@ -57,17 +72,29 @@ typedef struct tinsn_stack
} IStack;
-void istack_init PARAMS ((IStack *));
-bfd_boolean istack_empty PARAMS ((IStack *));
-bfd_boolean istack_full PARAMS ((IStack *));
-TInsn * istack_top PARAMS ((IStack *));
-void istack_push PARAMS ((IStack *, TInsn *));
-TInsn * istack_push_space PARAMS ((IStack *));
-void istack_pop PARAMS ((IStack *));
+void istack_init PARAMS ((IStack *));
+bfd_boolean istack_empty PARAMS ((IStack *));
+bfd_boolean istack_full PARAMS ((IStack *));
+TInsn *istack_top PARAMS ((IStack *));
+void istack_push PARAMS ((IStack *, TInsn *));
+TInsn *istack_push_space PARAMS ((IStack *));
+void istack_pop PARAMS ((IStack *));
/* TInsn utilities. */
-void tinsn_init PARAMS ((TInsn *));
-void tinsn_copy PARAMS ((TInsn *, const TInsn *));
-expressionS *tinsn_get_tok PARAMS ((TInsn *, int));
+void tinsn_init PARAMS ((TInsn *));
+expressionS *tinsn_get_tok PARAMS ((TInsn *, int));
+
+
+/* vliw_insn: bundles of TInsns. */
+
+typedef struct vliw_insn
+{
+ xtensa_format format;
+ xtensa_insnbuf insnbuf;
+ int num_slots;
+ unsigned int inside_bundle;
+ TInsn slots[MAX_SLOTS];
+ xtensa_insnbuf slotbuf[MAX_SLOTS];
+} vliw_insn;
#endif /* !XTENSA_ISTACK_H */
diff --git a/gas/config/xtensa-relax.c b/gas/config/xtensa-relax.c
index 49a93b20e0..4d2d01ac28 100644
--- a/gas/config/xtensa-relax.c
+++ b/gas/config/xtensa-relax.c
@@ -1,5 +1,5 @@
/* Table of relaxations for Xtensa assembly.
- Copyright 2003 Free Software Foundation, Inc.
+ Copyright 2003, 2004 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
@@ -23,20 +23,28 @@
Each action contains an instruction pattern to match and
preconditions for the match as well as an expansion if the pattern
matches. The preconditions can specify that two operands are the
- same or an operand is a specific constant. The expansion uses the
- bound variables from the pattern to specify that specific operands
- from the pattern should be used in the result.
+ same or an operand is a specific constant or register. The expansion
+ uses the bound variables from the pattern to specify that specific
+ operands from the pattern should be used in the result.
+
+ The code determines whether the condition applies to a constant or
+ a register depending on the type of the operand. You may get
+ unexpected results if you don't match the rule against the operand
+ type correctly.
The patterns match a language like:
- INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )*
+ INSN_PATTERN ::= INSN_TEMPL ( '|' PRECOND )* ( '?' OPTIONPRED )*
INSN_TEMPL ::= OPCODE ' ' [ OPERAND (',' OPERAND)* ]
OPCODE ::= id
OPERAND ::= CONSTANT | VARIABLE | SPECIALFN '(' VARIABLE ')'
SPECIALFN ::= 'HI24S' | 'F32MINUS' | 'LOW8'
+ | 'HI16' | 'LOW16'
VARIABLE ::= '%' id
PRECOND ::= OPERAND CMPOP OPERAND
CMPOP ::= '==' | '!='
+ OPTIONPRED ::= OPTIONNAME ('+' OPTIONNAME)
+ OPTIONNAME ::= '"' id '"'
The replacement language
INSN_REPL ::= INSN_LABEL_LIT ( ';' INSN_LABEL_LIT )*
@@ -47,6 +55,13 @@
The operands in a PRECOND must be constants or variables bound by
the INSN_PATTERN.
+ The configuration options define a predicate on the availability of
+ options which must be TRUE for this rule to be valid. Examples are
+ requiring "density" for replacements with density instructions,
+ requiring "const16" for replacements that require const16
+ instructions, etc. The names are interpreted by the assembler to a
+ truth value for a particular frag.
+
The operands in the INSN_REPL must be constants, variables bound in
the associated INSN_PATTERN, special variables that are bound in
the INSN_REPL by LABEL or LITERAL definitions, or special value
@@ -84,11 +99,11 @@
#include "xtensa-isa.h"
#include "xtensa-relax.h"
#include <stddef.h>
+#include "xtensa-config.h"
/* Imported from bfd. */
extern xtensa_isa xtensa_default_isa;
-
/* The opname_list is a small list of names that we use for opcode and
operand variable names to simplify ownership of these commonly used
strings. Strings entered in the table can be compared by pointer
@@ -115,7 +130,7 @@ typedef struct opname_map_struct opname_map;
struct opname_map_e_struct
{
const char *operand_name; /* If null, then use constant_value. */
- size_t operand_num;
+ int operand_num;
unsigned constant_value;
opname_map_e *next;
};
@@ -173,6 +188,7 @@ struct insn_pattern_struct
{
insn_templ t;
precond_list preconds;
+ ReqOptionList *options;
};
@@ -202,7 +218,7 @@ typedef struct split_rec_struct split_rec;
struct split_rec_struct
{
char **vec;
- size_t count;
+ int count;
};
/* The "string_pattern_pair" is a set of pairs containing instruction
@@ -231,93 +247,113 @@ struct string_pattern_pair_struct
static string_pattern_pair widen_spec_list[] =
{
- {"add.n %ar,%as,%at", "add %ar,%as,%at"},
- {"addi.n %ar,%as,%imm", "addi %ar,%as,%imm"},
- {"beqz.n %as,%label", "beqz %as,%label"},
- {"bnez.n %as,%label", "bnez %as,%label"},
- {"l32i.n %at,%as,%imm", "l32i %at,%as,%imm"},
- {"mov.n %at,%as", "or %at,%as,%as"},
- {"movi.n %as,%imm", "movi %as,%imm"},
- {"nop.n", "or 1,1,1"},
- {"ret.n", "ret"},
- {"retw.n", "retw"},
- {"s32i.n %at,%as,%imm", "s32i %at,%as,%imm"},
+ {"add.n %ar,%as,%at ? IsaUseDensityInstruction", "add %ar,%as,%at"},
+ {"addi.n %ar,%as,%imm ? IsaUseDensityInstruction", "addi %ar,%as,%imm"},
+ {"beqz.n %as,%label ? IsaUseDensityInstruction", "beqz %as,%label"},
+ {"bnez.n %as,%label ? IsaUseDensityInstruction", "bnez %as,%label"},
+ {"l32i.n %at,%as,%imm ? IsaUseDensityInstruction", "l32i %at,%as,%imm"},
+ {"mov.n %at,%as ? IsaUseDensityInstruction", "or %at,%as,%as"},
+ {"movi.n %as,%imm ? IsaUseDensityInstruction", "movi %as,%imm"},
+ {"nop.n ? IsaUseDensityInstruction ? realnop", "nop"},
+ {"nop.n ? IsaUseDensityInstruction ? no-realnop", "or 1,1,1"},
+ {"ret.n %as ? IsaUseDensityInstruction", "ret %as"},
+ {"retw.n %as ? IsaUseDensityInstruction", "retw %as"},
+ {"s32i.n %at,%as,%imm ? IsaUseDensityInstruction", "s32i %at,%as,%imm"},
{"srli %at,%as,%imm", "extui %at,%as,%imm,F32MINUS(%imm)"},
{"slli %ar,%as,0", "or %ar,%as,%as"},
- /* Widening with literals */
- {"movi %at,%imm", "LITERAL0 %imm; l32r %at,%LITERAL0"},
+
+ /* Widening with literals or const16. */
+ {"movi %at,%imm ? IsaUseL32R ",
+ "LITERAL0 %imm; l32r %at,%LITERAL0"},
+ {"movi %at,%imm ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm)"},
+
{"addi %ar,%as,%imm", "addmi %ar,%as,%imm"},
/* LOW8 is the low 8 bits of the Immed
MID8S is the middle 8 bits of the Immed */
{"addmi %ar,%as,%imm", "addmi %ar,%as,HI24S(%imm); addi %ar,%ar,LOW8(%imm)"},
- {"addmi %ar,%as,%imm | %ar!=%as",
+
+ /* In the end convert to either an l32r or const16. */
+ {"addmi %ar,%as,%imm | %ar!=%as ? IsaUseL32R",
"LITERAL0 %imm; l32r %ar,%LITERAL0; add %ar,%as,%ar"},
+ {"addmi %ar,%as,%imm | %ar!=%as ? IsaUseConst16",
+ "const16 %ar,HI16U(%imm); const16 %ar,LOW16U(%imm); add %ar,%as,%ar"},
/* Widening the load instructions with too-large immediates */
- {"l8ui %at,%as,%imm | %at!=%as",
+ {"l8ui %at,%as,%imm | %at!=%as ? IsaUseL32R",
"LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l8ui %at,%at,0"},
- {"l16si %at,%as,%imm | %at!=%as",
+ {"l16si %at,%as,%imm | %at!=%as ? IsaUseL32R",
"LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16si %at,%at,0"},
- {"l16ui %at,%as,%imm | %at!=%as",
+ {"l16ui %at,%as,%imm | %at!=%as ? IsaUseL32R",
"LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l16ui %at,%at,0"},
+ {"l32i %at,%as,%imm | %at!=%as ? IsaUseL32R",
+ "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"},
+
+ /* Widening load instructions with const16s. */
+ {"l8ui %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l8ui %at,%at,0"},
+ {"l16si %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l16si %at,%at,0"},
+ {"l16ui %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l16ui %at,%at,0"},
+ {"l32i %at,%as,%imm | %at!=%as ? IsaUseConst16",
+ "const16 %at,HI16U(%imm); const16 %at,LOW16U(%imm); add %at,%at,%as; l32i %at,%at,0"},
+
#if 0 /* Xtensa Synchronization Option not yet available */
- {"l32ai %at,%as,%imm",
+ {"l32ai %at,%as,%imm ? IsaUseL32R",
"LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32ai %at,%at,0"},
#endif
#if 0 /* Xtensa Speculation Option not yet available */
- {"l32is %at,%as,%imm",
+ {"l32is %at,%as,%imm ? IsaUseL32R",
"LITERAL0 %imm; l32r %at,%LITERAL0; add.n %at,%at,%as; l32is %at,%at,0"},
#endif
- {"l32i %at,%as,%imm | %at!=%as",
- "LITERAL0 %imm; l32r %at,%LITERAL0; add %at,%at,%as; l32i %at,%at,0"},
- /* This is only PART of the loop instruction. In addition, hard
- coded into it's use is a modification of the final operand in the
- instruction in bytes 9 and 12. */
- {"loop %as,%label",
+ /* This is only PART of the loop instruction. In addition,
+ hardcoded into its use is a modification of the final operand in
+ the instruction in bytes 9 and 12. */
+ {"loop %as,%label | %as!=1 ? IsaUseLoops",
"loop %as,%LABEL0;"
- "rsr %as, 1;" /* LEND */
- "wsr %as, 0;" /* LBEG */
+ "rsr.lend %as;" /* LEND */
+ "wsr.lbeg %as;" /* LBEG */
"addi %as, %as, 0;" /* lo8(%label-%LABEL1) */
"addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */
- "wsr %as, 1;"
+ "wsr.lend %as;"
"isync;"
- "rsr %as, 2;" /* LCOUNT */
+ "rsr.lcount %as;" /* LCOUNT */
"addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */
"LABEL0"},
- {"loopgtz %as,%label",
- "beqz %as,%label;"
- "bltz %as,%label;"
+ {"loopgtz %as,%label | %as!=1 ? IsaUseLoops",
+ "beqz %as,%label;"
+ "bltz %as,%label;"
"loopgtz %as,%LABEL0;"
- "rsr %as, 1;" /* LEND */
- "wsr %as, 0;" /* LBEG */
+ "rsr.lend %as;" /* LEND */
+ "wsr.lbeg %as;" /* LBEG */
"addi %as, %as, 0;" /* lo8(%label-%LABEL1) */
"addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */
- "wsr %as, 1;"
+ "wsr.lend %as;"
"isync;"
- "rsr %as, 2;" /* LCOUNT */
+ "rsr.lcount %as;" /* LCOUNT */
"addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */
"LABEL0"},
- {"loopnez %as,%label",
+ {"loopnez %as,%label | %as!=1 ? IsaUseLoops",
"beqz %as,%label;"
"loopnez %as,%LABEL0;"
- "rsr %as, 1;" /* LEND */
- "wsr %as, 0;" /* LBEG */
+ "rsr.lend %as;" /* LEND */
+ "wsr.lbeg %as;" /* LBEG */
"addi %as, %as, 0;" /* lo8(%label-%LABEL1) */
"addmi %as, %as, 0;" /* mid8(%label-%LABEL1) */
- "wsr %as, 1;"
+ "wsr.lend %as;"
"isync;"
- "rsr %as, 2;" /* LCOUNT */
+ "rsr.lcount %as;" /* LCOUNT */
"addi %as, %as, 1;" /* density -> addi.n %as, %as, 1 */
"LABEL0"},
-#if 0 /* no mechanism here to determine if Density Option is available */
- {"beqz %as,%label", "bnez.n %as,%LABEL0;j %label;LABEL0"},
- {"bnez %as,%label", "beqz.n %as,%LABEL0;j %label;LABEL0"},
-#else
+ {"beqz %as,%label ? IsaUseDensityInstruction", "bnez.n %as,%LABEL0;j %label;LABEL0"},
+ {"bnez %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%LABEL0;j %label;LABEL0"},
{"beqz %as,%label", "bnez %as,%LABEL0;j %label;LABEL0"},
{"bnez %as,%label", "beqz %as,%LABEL0;j %label;LABEL0"},
-#endif
+ {"beqzt %as,%label ? IsaUsePredictedBranches", "bnez %as,%LABEL0;j %label;LABEL0"},
+ {"bnezt %as,%label ? IsaUsePredictedBranches", "beqz %as,%LABEL0;j %label;LABEL0"},
{"bgez %as,%label", "bltz %as,%LABEL0;j %label;LABEL0"},
{"bltz %as,%label", "bgez %as,%LABEL0;j %label;LABEL0"},
@@ -331,24 +367,42 @@ static string_pattern_pair widen_spec_list[] =
{"bbsi %as,%imm,%label", "bbci %as,%imm,%LABEL0;j %label;LABEL0"},
{"beq %as,%at,%label", "bne %as,%at,%LABEL0;j %label;LABEL0"},
{"bne %as,%at,%label", "beq %as,%at,%LABEL0;j %label;LABEL0"},
+ {"beqt %as,%at,%label ? IsaUsePredictedBranches", "bne %as,%at,%LABEL0;j %label;LABEL0"},
+ {"bnet %as,%at,%label ? IsaUsePredictedBranches", "beq %as,%at,%LABEL0;j %label;LABEL0"},
{"bge %as,%at,%label", "blt %as,%at,%LABEL0;j %label;LABEL0"},
{"blt %as,%at,%label", "bge %as,%at,%LABEL0;j %label;LABEL0"},
{"bgeu %as,%at,%label", "bltu %as,%at,%LABEL0;j %label;LABEL0"},
{"bltu %as,%at,%label", "bgeu %as,%at,%LABEL0;j %label;LABEL0"},
{"bany %as,%at,%label", "bnone %as,%at,%LABEL0;j %label;LABEL0"},
-#if 1 /* provide relaxations for Boolean Option */
- {"bt %bs,%label", "bf %bs,%LABEL0;j %label;LABEL0"},
- {"bf %bs,%label", "bt %bs,%LABEL0;j %label;LABEL0"},
-#endif
+
+ {"bt %bs,%label ? IsaUseBooleans", "bf %bs,%LABEL0;j %label;LABEL0"},
+ {"bf %bs,%label ? IsaUseBooleans", "bt %bs,%LABEL0;j %label;LABEL0"},
+
{"bnone %as,%at,%label", "bany %as,%at,%LABEL0;j %label;LABEL0"},
{"ball %as,%at,%label", "bnall %as,%at,%LABEL0;j %label;LABEL0"},
{"bnall %as,%at,%label", "ball %as,%at,%LABEL0;j %label;LABEL0"},
{"bbc %as,%at,%label", "bbs %as,%at,%LABEL0;j %label;LABEL0"},
{"bbs %as,%at,%label", "bbc %as,%at,%LABEL0;j %label;LABEL0"},
- {"call0 %label", "LITERAL0 %label; l32r a0,%LITERAL0; callx0 a0"},
- {"call4 %label", "LITERAL0 %label; l32r a4,%LITERAL0; callx4 a4"},
- {"call8 %label", "LITERAL0 %label; l32r a8,%LITERAL0; callx8 a8"},
- {"call12 %label", "LITERAL0 %label; l32r a12,%LITERAL0; callx12 a12"}
+
+ /* Expanding calls with literals. */
+ {"call0 %label,%ar0 ? IsaUseL32R",
+ "LITERAL0 %label; l32r a0,%LITERAL0; callx0 a0,%ar0"},
+ {"call4 %label,%ar4 ? IsaUseL32R",
+ "LITERAL0 %label; l32r a4,%LITERAL0; callx4 a4,%ar4"},
+ {"call8 %label,%ar8 ? IsaUseL32R",
+ "LITERAL0 %label; l32r a8,%LITERAL0; callx8 a8,%ar8"},
+ {"call12 %label,%ar12 ? IsaUseL32R",
+ "LITERAL0 %label; l32r a12,%LITERAL0; callx12 a12,%ar12"},
+
+ /* Expanding calls with const16. */
+ {"call0 %label,%ar0 ? IsaUseConst16",
+ "const16 a0,HI16U(%label); const16 a0,LOW16U(%label); callx0 a0,%ar0"},
+ {"call4 %label,%ar4 ? IsaUseConst16",
+ "const16 a4,HI16U(%label); const16 a4,LOW16U(%label); callx4 a4,%ar4"},
+ {"call8 %label,%ar8 ? IsaUseConst16",
+ "const16 a8,HI16U(%label); const16 a8,LOW16U(%label); callx8 a8,%ar8"},
+ {"call12 %label,%ar12 ? IsaUseConst16",
+ "const16 a12,HI16U(%label); const16 a12,LOW16U(%label); callx12 a12,%ar12"}
};
#define WIDEN_COUNT (sizeof (widen_spec_list) / sizeof (string_pattern_pair))
@@ -366,20 +420,22 @@ static string_pattern_pair widen_spec_list[] =
string_pattern_pair simplify_spec_list[] =
{
- {"add %ar,%as,%at", "add.n %ar,%as,%at"},
- {"addi.n %ar,%as,0", "mov.n %ar,%as"},
- {"addi %ar,%as,0", "mov.n %ar,%as"},
- {"addi %ar,%as,%imm", "addi.n %ar,%as,%imm"},
- {"addmi %ar,%as,%imm", "addi.n %ar,%as,%imm"},
- {"beqz %as,%label", "beqz.n %as,%label"},
- {"bnez %as,%label", "bnez.n %as,%label"},
- {"l32i %at,%as,%imm", "l32i.n %at,%as,%imm"},
- {"movi %as,%imm", "movi.n %as,%imm"},
- {"or %ar,%as,%at | %as==%at", "mov.n %ar,%as"},
- {"ret", "ret.n"},
- {"retw", "retw.n"},
- {"s32i %at,%as,%imm", "s32i.n %at,%as,%imm"},
- {"slli %ar,%as,0", "mov.n %ar,%as"}
+ {"add %ar,%as,%at ? IsaUseDensityInstruction", "add.n %ar,%as,%at"},
+ {"addi.n %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+ {"addi %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+ {"addi %ar,%as,%imm ? IsaUseDensityInstruction", "addi.n %ar,%as,%imm"},
+ {"addmi %ar,%as,%imm ? IsaUseDensityInstruction", "addi.n %ar,%as,%imm"},
+ {"beqz %as,%label ? IsaUseDensityInstruction", "beqz.n %as,%label"},
+ {"bnez %as,%label ? IsaUseDensityInstruction", "bnez.n %as,%label"},
+ {"l32i %at,%as,%imm ? IsaUseDensityInstruction", "l32i.n %at,%as,%imm"},
+ {"movi %as,%imm ? IsaUseDensityInstruction", "movi.n %as,%imm"},
+ {"nop ? realnop ? IsaUseDensityInstruction", "nop.n"},
+ {"or %ar,%as,%at | %ar==%as | %as==%at ? IsaUseDensityInstruction", "nop.n"},
+ {"or %ar,%as,%at | %ar!=%as | %as==%at ? IsaUseDensityInstruction", "mov.n %ar,%as"},
+ {"ret %as ? IsaUseDensityInstruction", "ret.n %as"},
+ {"retw %as ? IsaUseDensityInstruction", "retw.n %as"},
+ {"s32i %at,%as,%imm ? IsaUseDensityInstruction", "s32i.n %at,%as,%imm"},
+ {"slli %ar,%as,0 ? IsaUseDensityInstruction", "mov.n %ar,%as"}
};
#define SIMPLIFY_COUNT \
@@ -389,7 +445,8 @@ string_pattern_pair simplify_spec_list[] =
/* Transition generation helpers. */
static void append_transition
- PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *));
+ PARAMS ((TransitionTable *, xtensa_opcode, TransitionRule *,
+ transition_cmp_fn));
static void append_condition
PARAMS ((TransitionRule *, Precondition *));
static void append_value_condition
@@ -416,6 +473,10 @@ static long operand_function_F32MINUS
PARAMS ((long));
static long operand_function_LOW8
PARAMS ((long));
+static long operand_function_LOW16U
+ PARAMS ((long));
+static long operand_function_HI16U
+ PARAMS ((long));
/* Externally visible functions. */
@@ -427,7 +488,7 @@ extern long xg_apply_userdef_op_fn
/* Parsing helpers. */
static const char *enter_opname_n
- PARAMS ((const char *, size_t));
+ PARAMS ((const char *, int));
static const char *enter_opname
PARAMS ((const char *));
@@ -457,6 +518,14 @@ static void init_split_rec
PARAMS ((split_rec *));
static void clear_split_rec
PARAMS ((split_rec *));
+static void clear_req_or_option_list
+ PARAMS ((ReqOrOption **));
+static void clear_req_option_list
+ PARAMS ((ReqOption **));
+static ReqOrOption *clone_req_or_option_list
+ PARAMS ((ReqOrOption *));
+static ReqOption *clone_req_option_list
+ PARAMS ((ReqOption *));
/* Operand and insn_templ helpers. */
@@ -468,10 +537,10 @@ static bfd_boolean op_is_constant
PARAMS ((const opname_map_e *));
static unsigned op_get_constant
PARAMS ((const opname_map_e *));
-static size_t insn_templ_operand_count
+static int insn_templ_operand_count
PARAMS ((const insn_templ *));
-/* parsing helpers. */
+/* Parsing helpers. */
static const char *skip_white
PARAMS ((const char *));
@@ -496,24 +565,29 @@ static bfd_boolean parse_constant
PARAMS ((const char *, unsigned *));
static bfd_boolean parse_id_constant
PARAMS ((const char *, const char *, unsigned *));
+static bfd_boolean parse_option_cond
+ PARAMS ((const char *, ReqOption *));
/* Transition table building code. */
+static bfd_boolean transition_applies
+ PARAMS ((insn_pattern *, const char *, const char *));
static TransitionRule *build_transition
PARAMS ((insn_pattern *, insn_repl *, const char *, const char *));
static TransitionTable *build_transition_table
- PARAMS ((const string_pattern_pair *, size_t));
+ PARAMS ((const string_pattern_pair *, int, transition_cmp_fn));
void
-append_transition (tt, opcode, t)
+append_transition (tt, opcode, t, cmp)
TransitionTable *tt;
xtensa_opcode opcode;
TransitionRule *t;
+ transition_cmp_fn cmp;
{
TransitionList *tl = (TransitionList *) xmalloc (sizeof (TransitionList));
TransitionList *prev;
- TransitionList *nxt;
+ TransitionList **t_p;
assert (tt != NULL);
assert (opcode < tt->num_opcodes);
@@ -525,13 +599,18 @@ append_transition (tt, opcode, t)
tt->table[opcode] = tl;
return;
}
- nxt = prev->next;
- while (nxt != NULL)
+
+ for (t_p = &tt->table[opcode]; (*t_p) != NULL; t_p = &(*t_p)->next)
{
- prev = nxt;
- nxt = nxt->next;
+ if (cmp && cmp (t, (*t_p)->rule) < 0)
+ {
+ /* Insert it here. */
+ tl->next = *t_p;
+ *t_p = tl;
+ return;
+ }
}
- prev->next = tl;
+ (*t_p) = tl;
}
@@ -759,6 +838,23 @@ operand_function_LOW8 (a)
}
+long
+operand_function_LOW16U (a)
+ long a;
+{
+ return (a & 0xffff);
+}
+
+
+long
+operand_function_HI16U (a)
+ long a;
+{
+ unsigned long b = a & 0xffff0000;
+ return (long) (b >> 16);
+}
+
+
bfd_boolean
xg_has_userdef_op_fn (op)
OpType op;
@@ -768,6 +864,8 @@ xg_has_userdef_op_fn (op)
case OP_OPERAND_F32MINUS:
case OP_OPERAND_LOW8:
case OP_OPERAND_HI24S:
+ case OP_OPERAND_LOW16U:
+ case OP_OPERAND_HI16U:
return TRUE;
default:
break;
@@ -789,6 +887,10 @@ xg_apply_userdef_op_fn (op, a)
return operand_function_LOW8 (a);
case OP_OPERAND_HI24S:
return operand_function_HI24S (a);
+ case OP_OPERAND_LOW16U:
+ return operand_function_LOW16U (a);
+ case OP_OPERAND_HI16U:
+ return operand_function_HI16U (a);
default:
break;
}
@@ -801,13 +903,14 @@ xg_apply_userdef_op_fn (op, a)
const char *
enter_opname_n (name, len)
const char *name;
- size_t len;
+ int len;
{
opname_e *op;
for (op = local_opnames; op != NULL; op = op->next)
{
- if (strlen (op->opname) == len && strncmp (op->opname, name, len) == 0)
+ if (strlen (op->opname) == (unsigned) len
+ && strncmp (op->opname, name, len) == 0)
return op->opname;
}
op = (opname_e *) xmalloc (sizeof (opname_e));
@@ -830,7 +933,7 @@ enter_opname (name)
return op->opname;
}
op = (opname_e *) xmalloc (sizeof (opname_e));
- op->opname = strdup (name);
+ op->opname = xstrdup (name);
return op->opname;
}
@@ -952,6 +1055,7 @@ init_insn_pattern (p)
{
init_insn_templ (&p->t);
init_precond_list (&p->preconds);
+ p->options = NULL;
}
@@ -989,14 +1093,14 @@ clear_insn_repl (r)
}
-static size_t
+static int
insn_templ_operand_count (t)
const insn_templ *t;
{
- size_t i = 0;
+ int i = 0;
const opname_map_e *op;
- for (op = t->operand_map.head; op != NULL; op = op->next, ++i)
+ for (op = t->operand_map.head; op != NULL; op = op->next, i++)
;
return i;
}
@@ -1131,8 +1235,8 @@ split_string (rec, in, c, elide_whitespace)
char c;
bfd_boolean elide_whitespace;
{
- size_t cnt = 0;
- size_t i;
+ int cnt = 0;
+ int i;
const char *p = in;
while (p != NULL && *p != '\0')
@@ -1156,7 +1260,7 @@ split_string (rec, in, c, elide_whitespace)
for (i = 0; i < cnt; i++)
{
const char *q;
- size_t len;
+ int len;
q = p;
if (elide_whitespace)
@@ -1164,7 +1268,7 @@ split_string (rec, in, c, elide_whitespace)
p = strchr (q, c);
if (p == NULL)
- rec->vec[i] = strdup (q);
+ rec->vec[i] = xstrdup (q);
else
{
len = p - q;
@@ -1184,9 +1288,9 @@ void
clear_split_rec (rec)
split_rec *rec;
{
- size_t i;
+ int i;
- for (i = 0; i < rec->count; ++i)
+ for (i = 0; i < rec->count; i++)
free (rec->vec[i]);
if (rec->count > 0)
@@ -1194,6 +1298,9 @@ clear_split_rec (rec)
}
+/* Initialize a split record. The split record must be initialized
+ before split_string is called. */
+
void
init_split_rec (rec)
split_rec *rec;
@@ -1211,10 +1318,11 @@ parse_insn_templ (s, t)
insn_templ *t;
{
const char *p = s;
- /* First find the first whitespace. */
- size_t insn_name_len;
+ int insn_name_len;
split_rec oprec;
- size_t i;
+ int i;
+
+ /* First find the first whitespace. */
init_split_rec (&oprec);
@@ -1277,7 +1385,7 @@ parse_precond (s, precond)
to identify when density is available. */
const char *p = s;
- size_t len;
+ int len;
precond->opname1 = NULL;
precond->opval1 = 0;
precond->cmpop = OP_EQUAL;
@@ -1322,34 +1430,170 @@ parse_precond (s, precond)
}
+void
+clear_req_or_option_list (r_p)
+ ReqOrOption **r_p;
+{
+ if (*r_p == NULL)
+ return;
+
+ free ((*r_p)->option_name);
+ clear_req_or_option_list (&(*r_p)->next);
+ *r_p = NULL;
+}
+
+
+void
+clear_req_option_list (r_p)
+ ReqOption **r_p;
+{
+ if (*r_p == NULL)
+ return;
+
+ clear_req_or_option_list (&(*r_p)->or_option_terms);
+ clear_req_option_list (&(*r_p)->next);
+ *r_p = NULL;
+}
+
+
+ReqOrOption *
+clone_req_or_option_list (req_or_option)
+ ReqOrOption *req_or_option;
+{
+ ReqOrOption *new_req_or_option;
+
+ if (req_or_option == NULL)
+ return NULL;
+
+ new_req_or_option = (ReqOrOption *) xmalloc (sizeof (ReqOrOption));
+ new_req_or_option->option_name = xstrdup (req_or_option->option_name);
+ new_req_or_option->is_true = req_or_option->is_true;
+ new_req_or_option->next = NULL;
+ new_req_or_option->next = clone_req_or_option_list (req_or_option->next);
+ return new_req_or_option;
+}
+
+
+ReqOption *
+clone_req_option_list (req_option)
+ ReqOption *req_option;
+{
+ ReqOption *new_req_option;
+
+ if (req_option == NULL)
+ return NULL;
+
+ new_req_option = (ReqOption *) xmalloc (sizeof (ReqOption));
+ new_req_option->or_option_terms = NULL;
+ new_req_option->next = NULL;
+ new_req_option->or_option_terms =
+ clone_req_or_option_list (req_option->or_option_terms);
+ new_req_option->next = clone_req_option_list (req_option->next);
+ return new_req_option;
+}
+
+
+bfd_boolean
+parse_option_cond (s, option)
+ const char *s;
+ ReqOption *option;
+{
+ int i;
+ split_rec option_term_rec;
+
+ /* All option or conditions are of the form:
+ optionA + no-optionB + ...
+ "Ands" are divided by "?". */
+
+ init_split_rec (&option_term_rec);
+ split_string (&option_term_rec, s, '+', TRUE);
+
+ if (option_term_rec.count == 0)
+ {
+ clear_split_rec (&option_term_rec);
+ return FALSE;
+ }
+
+ for (i = 0; i < option_term_rec.count; i++)
+ {
+ char *option_name = option_term_rec.vec[i];
+ bfd_boolean is_true = TRUE;
+ ReqOrOption *req;
+ ReqOrOption **r_p;
+
+ if (strncmp (option_name, "no-", 3) == 0)
+ {
+ option_name = xstrdup (&option_name[3]);
+ is_true = FALSE;
+ }
+ else
+ option_name = xstrdup (option_name);
+
+ req = (ReqOrOption *) xmalloc (sizeof (ReqOrOption));
+ req->option_name = option_name;
+ req->is_true = is_true;
+ req->next = NULL;
+
+ /* Append to list. */
+ for (r_p = &option->or_option_terms; (*r_p) != NULL;
+ r_p = &(*r_p)->next)
+ ;
+ (*r_p) = req;
+ }
+ return TRUE;
+}
+
+
/* Parse a string like:
"insn op1, op2, op3, op4 | op1 != op2 | op2 == op3 | op4 == 1".
I.E., instruction "insn" with 4 operands where operand 1 and 2 are not
- the same and operand 2 and 3 are the same and operand 4 is 1. */
+ the same and operand 2 and 3 are the same and operand 4 is 1.
+
+ or:
+
+ "insn op1 | op1 == 1 / density + boolean / no-useroption".
+ i.e. instruction "insn" with 1 operands where operand 1 is 1
+ when "density" or "boolean" options are available and
+ "useroption" is not available.
+
+ Because the current implementation of this parsing scheme uses
+ split_string, it requires that '|' and '?' are only used as
+ delimiters for predicates and required options. */
bfd_boolean
parse_insn_pattern (in, insn)
const char *in;
insn_pattern *insn;
{
-
split_rec rec;
- size_t i;
+ split_rec optionrec;
+ int i;
- init_split_rec (&rec);
init_insn_pattern (insn);
- split_string (&rec, in, '|', TRUE);
+ init_split_rec (&optionrec);
+ split_string (&optionrec, in, '?', TRUE);
+ if (optionrec.count == 0)
+ {
+ clear_split_rec (&optionrec);
+ return FALSE;
+ }
+
+ init_split_rec (&rec);
+
+ split_string (&rec, optionrec.vec[0], '|', TRUE);
if (rec.count == 0)
{
clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
return FALSE;
}
if (!parse_insn_templ (rec.vec[0], &insn->t))
{
clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
return FALSE;
}
@@ -1360,6 +1604,7 @@ parse_insn_pattern (in, insn)
if (!parse_precond (rec.vec[i], cond))
{
clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
clear_insn_pattern (insn);
return FALSE;
}
@@ -1369,7 +1614,32 @@ parse_insn_pattern (in, insn)
insn->preconds.tail = &cond->next;
}
+ for (i = 1; i < optionrec.count; i++)
+ {
+ /* Handle the option conditions. */
+ ReqOption **r_p;
+ ReqOption *req_option = (ReqOption *) xmalloc (sizeof (ReqOption));
+ req_option->or_option_terms = NULL;
+ req_option->next = NULL;
+
+ if (!parse_option_cond (optionrec.vec[i], req_option))
+ {
+ clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
+ clear_insn_pattern (insn);
+ clear_req_option_list (&req_option);
+ return FALSE;
+ }
+
+ /* Append the condition. */
+ for (r_p = &insn->options; (*r_p) != NULL; r_p = &(*r_p)->next)
+ ;
+
+ (*r_p) = req_option;
+ }
+
clear_split_rec (&rec);
+ clear_split_rec (&optionrec);
return TRUE;
}
@@ -1381,7 +1651,7 @@ parse_insn_repl (in, r_p)
{
/* This is a list of instruction templates separated by ';'. */
split_rec rec;
- size_t i;
+ int i;
split_string (&rec, in, ';', TRUE);
@@ -1404,6 +1674,59 @@ parse_insn_repl (in, r_p)
}
+bfd_boolean
+transition_applies (initial_insn, from_string, to_string)
+ insn_pattern *initial_insn;
+ const char *from_string ATTRIBUTE_UNUSED;
+ const char *to_string ATTRIBUTE_UNUSED;
+{
+ ReqOption *req_option;
+
+ for (req_option = initial_insn->options;
+ req_option != NULL;
+ req_option = req_option->next)
+ {
+ ReqOrOption *req_or_option = req_option->or_option_terms;
+
+ if (req_or_option == NULL
+ || req_or_option->next != NULL)
+ continue;
+
+ if (strncmp (req_or_option->option_name, "IsaUse", 6) == 0)
+ {
+ bfd_boolean option_available = FALSE;
+ char *option_name = req_or_option->option_name + 6;
+ if (!strcmp (option_name, "DensityInstruction"))
+ option_available = (XCHAL_HAVE_DENSITY == 1);
+ else if (!strcmp (option_name, "L32R"))
+ option_available = (XCHAL_HAVE_L32R == 1);
+ else if (!strcmp (option_name, "Const16"))
+ option_available = (XCHAL_HAVE_CONST16 == 1);
+ else if (!strcmp (option_name, "Loops"))
+ option_available = (XCHAL_HAVE_LOOPS == 1);
+ else if (!strcmp (option_name, "PredictedBranches"))
+ option_available = (XCHAL_HAVE_PREDICTED_BRANCHES == 1);
+ else if (!strcmp (option_name, "Booleans"))
+ option_available = (XCHAL_HAVE_BOOLEANS == 1);
+ else
+ as_warn (_("invalid configuration option '%s' in transition rule '%s'"),
+ req_or_option->option_name, from_string);
+ if ((option_available ^ req_or_option->is_true) != 0)
+ return FALSE;
+ }
+ else if (strcmp (req_or_option->option_name, "realnop") == 0)
+ {
+ bfd_boolean nop_available =
+ (xtensa_opcode_lookup (xtensa_default_isa, "nop")
+ != XTENSA_UNDEFINED);
+ if ((nop_available ^ req_or_option->is_true) != 0)
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+
TransitionRule *
build_transition (initial_insn, replace_insns, from_string, to_string)
insn_pattern *initial_insn;
@@ -1430,15 +1753,15 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
{
/* It is OK to not be able to translate some of these opcodes. */
#if 0
- as_warn (_("Invalid opcode '%s' in transition rule '%s'\n"),
- initial_insn->t.opcode_name, to_string);
+ as_warn (_("invalid opcode '%s' in transition rule '%s'"),
+ initial_insn->t.opcode_name, from_string);
#endif
return NULL;
}
- if (xtensa_num_operands (isa, opcode)
- != (int) insn_templ_operand_count (&initial_insn->t))
+ if (xtensa_opcode_num_operands (isa, opcode)
+ != insn_templ_operand_count (&initial_insn->t))
{
/* This is also OK because there are opcodes that
have different numbers of operands on different
@@ -1531,12 +1854,14 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
op1->operand_num, op2->operand_num);
else if (op2 == NULL)
append_constant_value_condition (tr, precond->cmpop,
- op1->operand_num, precond->opval1);
+ op1->operand_num, precond->opval2);
else
append_constant_value_condition (tr, precond->cmpop,
- op2->operand_num, precond->opval2);
+ op2->operand_num, precond->opval1);
}
+ tr->options = clone_req_option_list (initial_insn->options);
+
/* Generate the replacement instructions. Some of these
"instructions" are actually labels and literals. The literals
must be defined in order 0..n and a literal must be defined
@@ -1549,7 +1874,7 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
{
BuildInstr *bi;
const char *opcode_name;
- size_t operand_count;
+ int operand_count;
opname_map_e *op;
unsigned idnum = 0;
const char *fn_name;
@@ -1592,12 +1917,17 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
bi->typ = INSTR_INSTR;
bi->opcode = xtensa_opcode_lookup (isa, r->t.opcode_name);
if (bi->opcode == XTENSA_UNDEFINED)
- return NULL;
+ {
+ as_warn (_("invalid opcode '%s' in transition rule '%s'"),
+ r->t.opcode_name, to_string);
+ return NULL;
+ }
/* Check for the right number of ops. */
- if (xtensa_num_operands (isa, bi->opcode)
+ if (xtensa_opcode_num_operands (isa, bi->opcode)
!= (int) operand_count)
as_fatal (_("opcode '%s': replacement does not have %d ops"),
- opcode_name, xtensa_num_operands (isa, bi->opcode));
+ opcode_name,
+ xtensa_opcode_num_operands (isa, bi->opcode));
}
for (op = r->t.operand_map.head; op != NULL; op = op->next)
@@ -1650,8 +1980,12 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
typ = OP_OPERAND_HI24S;
else if (strcmp (fn_name, "F32MINUS") == 0)
typ = OP_OPERAND_F32MINUS;
+ else if (strcmp (fn_name, "LOW16U") == 0)
+ typ = OP_OPERAND_LOW16U;
+ else if (strcmp (fn_name, "HI16U") == 0)
+ typ = OP_OPERAND_HI16U;
else
- as_fatal (_("unknown user defined function %s"), fn_name);
+ as_fatal (_("unknown user-defined function %s"), fn_name);
orig_op = get_opmatch (&initial_insn->t.operand_map,
operand_arg_name);
@@ -1686,14 +2020,14 @@ build_transition (initial_insn, replace_insns, from_string, to_string)
TransitionTable *
-build_transition_table (transitions, transition_count)
+build_transition_table (transitions, transition_count, cmp)
const string_pattern_pair *transitions;
- size_t transition_count;
+ int transition_count;
+ transition_cmp_fn cmp;
{
TransitionTable *table = NULL;
- int num_opcodes = xtensa_num_opcodes (xtensa_default_isa);
- int i;
- size_t tnum;
+ int num_opcodes = xtensa_isa_num_opcodes (xtensa_default_isa);
+ int i, tnum;
if (table != NULL)
return table;
@@ -1733,10 +2067,20 @@ build_transition_table (transitions, transition_count)
continue;
}
- tr = build_transition (&initial_insn, &replace_insns,
- from_string, to_string);
- if (tr)
- append_transition (table, tr->opcode, tr);
+ if (transition_applies (&initial_insn, from_string, to_string))
+ {
+ tr = build_transition (&initial_insn, &replace_insns,
+ from_string, to_string);
+ if (tr)
+ append_transition (table, tr->opcode, tr, cmp);
+ else
+ {
+#if TENSILICA_DEBUG
+ as_warn (_("could not build transition for %s => %s"),
+ from_string, to_string);
+#endif
+ }
+ }
clear_insn_repl (&replace_insns);
clear_insn_pattern (&initial_insn);
@@ -1746,20 +2090,22 @@ build_transition_table (transitions, transition_count)
extern TransitionTable *
-xg_build_widen_table ()
+xg_build_widen_table (cmp)
+ transition_cmp_fn cmp;
{
static TransitionTable *table = NULL;
if (table == NULL)
- table = build_transition_table (widen_spec_list, WIDEN_COUNT);
+ table = build_transition_table (widen_spec_list, WIDEN_COUNT, cmp);
return table;
}
extern TransitionTable *
-xg_build_simplify_table ()
+xg_build_simplify_table (cmp)
+ transition_cmp_fn cmp;
{
static TransitionTable *table = NULL;
if (table == NULL)
- table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT);
+ table = build_transition_table (simplify_spec_list, SIMPLIFY_COUNT, cmp);
return table;
}
diff --git a/gas/config/xtensa-relax.h b/gas/config/xtensa-relax.h
index 99bf77bfd8..2a228bce32 100644
--- a/gas/config/xtensa-relax.h
+++ b/gas/config/xtensa-relax.h
@@ -1,5 +1,5 @@
/* Table of relaxations for Xtensa assembly.
- Copyright 2003 Free Software Foundation, Inc.
+ Copyright 2003, 2004 Free Software Foundation, Inc.
This file is part of GAS, the GNU Assembler.
@@ -33,6 +33,11 @@ typedef struct transition_rule TransitionRule;
typedef struct precondition_list PreconditionList;
typedef struct precondition Precondition;
+typedef struct req_or_option_list ReqOrOptionList;
+typedef struct req_or_option_list ReqOrOption;
+typedef struct req_option_list ReqOptionList;
+typedef struct req_option_list ReqOption;
+
struct transition_table
{
int num_opcodes;
@@ -52,6 +57,38 @@ struct precondition_list
};
+/* The required options for a rule are represented with a two-level
+ structure, with leaf expressions combined by logical ORs at the
+ lower level, and the results then combined by logical ANDs at the
+ top level. The AND terms are linked in a list, and each one can
+ contain a reference to a list of OR terms. The leaf expressions,
+ i.e., the OR options, can be negated by setting the is_true field
+ to FALSE. There are two classes of leaf expressions: (1) those
+ that are properties of the Xtensa configuration and can be
+ evaluated once when building the tables, and (2) those that depend
+ of the state of directives or other settings that may vary during
+ the assembly. The following expressions may be used in group (1):
+
+ IsaUse*: Xtensa configuration settings.
+ realnop: TRUE if the instruction set includes a NOP instruction.
+
+ There are currently no expressions in group (2), but they are still
+ supported since there is a good chance they'll be needed again for
+ something. */
+
+struct req_option_list
+{
+ ReqOrOptionList *or_option_terms;
+ ReqOptionList *next;
+};
+
+struct req_or_option_list
+{
+ char *option_name;
+ bfd_boolean is_true;
+ ReqOrOptionList *next;
+};
+
/* Operand types and constraints on operands: */
typedef enum op_type OpType;
@@ -62,9 +99,11 @@ enum op_type
OP_CONSTANT,
OP_OPERAND,
OP_OPERAND_LOW8, /* Sign-extended low 8 bits of immed. */
- OP_OPERAND_HI24S, /* high 24 bits of immed,
+ OP_OPERAND_HI24S, /* High 24 bits of immed,
plus 0x100 if low 8 bits are signed. */
OP_OPERAND_F32MINUS, /* 32 - immed. */
+ OP_OPERAND_LOW16U, /* Low 16 bits of immed. */
+ OP_OPERAND_HI16U, /* High 16 bits of immed. */
OP_LITERAL,
OP_LABEL
};
@@ -85,6 +124,7 @@ struct precondition
int op_data;
};
+
typedef struct build_op BuildOp;
struct build_op
@@ -117,7 +157,7 @@ struct build_instr
InstrType typ;
unsigned id; /* LITERAL_DEF or LABEL_DEF: an ordinal to
identify which one. */
- xtensa_opcode opcode; /* unused for LITERAL_DEF or LABEL_DEF. */
+ xtensa_opcode opcode; /* Unused for LITERAL_DEF or LABEL_DEF. */
BuildOp *ops;
BuildInstr *next;
};
@@ -126,17 +166,17 @@ struct transition_rule
{
xtensa_opcode opcode;
PreconditionList *conditions;
+ ReqOptionList *options;
BuildInstr *to_instr;
};
-extern TransitionTable *xg_build_simplify_table
- PARAMS ((void));
-extern TransitionTable *xg_build_widen_table
- PARAMS ((void));
+typedef int (*transition_cmp_fn) (const TransitionRule *,
+ const TransitionRule *);
+
+extern TransitionTable *xg_build_simplify_table PARAMS ((transition_cmp_fn));
+extern TransitionTable *xg_build_widen_table PARAMS ((transition_cmp_fn));
-extern bfd_boolean xg_has_userdef_op_fn
- PARAMS ((OpType));
-extern long xg_apply_userdef_op_fn
- PARAMS ((OpType, long));
+extern bfd_boolean xg_has_userdef_op_fn PARAMS ((OpType));
+extern long xg_apply_userdef_op_fn PARAMS ((OpType, long));
#endif /* !XTENSA_RELAX_H */
OpenPOWER on IntegriCloud