| /* tc-arm.c -- Assemble for the ARM |
| Copyright (C) 1994-2021 Free Software Foundation, Inc. |
| Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org) |
| Modified by David Taylor (dtaylor@armltd.co.uk) |
| Cirrus coprocessor mods by Aldy Hernandez (aldyh@redhat.com) |
| Cirrus coprocessor fixes by Petko Manolov (petkan@nucleusys.com) |
| Cirrus coprocessor fixes by Vladimir Ivanov (vladitx@nucleusys.com) |
| |
| This file is part of GAS, the GNU Assembler. |
| |
| GAS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GAS is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GAS; see the file COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| #include "as.h" |
| #include <limits.h> |
| #include <stdarg.h> |
| #define NO_RELOC 0 |
| #include "safe-ctype.h" |
| #include "subsegs.h" |
| #include "obstack.h" |
| #include "libiberty.h" |
| #include "opcode/arm.h" |
| #include "cpu-arm.h" |
| |
| #ifdef OBJ_ELF |
| #include "elf/arm.h" |
| #include "dw2gencfi.h" |
| #endif |
| |
| #include "dwarf2dbg.h" |
| |
| #ifdef OBJ_ELF |
| /* Must be at least the size of the largest unwind opcode (currently two). */ |
| #define ARM_OPCODE_CHUNK_SIZE 8 |
| |
| /* This structure holds the unwinding state. */ |
| |
| static struct |
| { |
| symbolS * proc_start; |
| symbolS * table_entry; |
| symbolS * personality_routine; |
| int personality_index; |
| /* The segment containing the function. */ |
| segT saved_seg; |
| subsegT saved_subseg; |
| /* Opcodes generated from this function. */ |
| unsigned char * opcodes; |
| int opcode_count; |
| int opcode_alloc; |
| /* The number of bytes pushed to the stack. */ |
| offsetT frame_size; |
| /* We don't add stack adjustment opcodes immediately so that we can merge |
| multiple adjustments. We can also omit the final adjustment |
| when using a frame pointer. */ |
| offsetT pending_offset; |
| /* These two fields are set by both unwind_movsp and unwind_setfp. They |
| hold the reg+offset to use when restoring sp from a frame pointer. */ |
| offsetT fp_offset; |
| int fp_reg; |
| /* Nonzero if an unwind_setfp directive has been seen. */ |
| unsigned fp_used:1; |
| /* Nonzero if the last opcode restores sp from fp_reg. */ |
| unsigned sp_restored:1; |
| } unwind; |
| |
| /* Whether --fdpic was given. */ |
| static int arm_fdpic; |
| |
| #endif /* OBJ_ELF */ |
| |
| /* Results from operand parsing worker functions. */ |
| |
| typedef enum |
| { |
| PARSE_OPERAND_SUCCESS, |
| PARSE_OPERAND_FAIL, |
| PARSE_OPERAND_FAIL_NO_BACKTRACK |
| } parse_operand_result; |
| |
| enum arm_float_abi |
| { |
| ARM_FLOAT_ABI_HARD, |
| ARM_FLOAT_ABI_SOFTFP, |
| ARM_FLOAT_ABI_SOFT |
| }; |
| |
| /* Types of processor to assemble for. */ |
| #ifndef CPU_DEFAULT |
| /* The code that was here used to select a default CPU depending on compiler |
| pre-defines which were only present when doing native builds, thus |
| changing gas' default behaviour depending upon the build host. |
| |
| If you have a target that requires a default CPU option then the you |
| should define CPU_DEFAULT here. */ |
| #endif |
| |
| /* Perform range checks on positive and negative overflows by checking if the |
| VALUE given fits within the range of an BITS sized immediate. */ |
| static bool out_of_range_p (offsetT value, offsetT bits) |
| { |
| gas_assert (bits < (offsetT)(sizeof (value) * 8)); |
| return (value & ~((1 << bits)-1)) |
| && ((value & ~((1 << bits)-1)) != ~((1 << bits)-1)); |
| } |
| |
| #ifndef FPU_DEFAULT |
| # ifdef TE_LINUX |
| # define FPU_DEFAULT FPU_ARCH_FPA |
| # elif defined (TE_NetBSD) |
| # ifdef OBJ_ELF |
| # define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, but VFP order. */ |
| # else |
| /* Legacy a.out format. */ |
| # define FPU_DEFAULT FPU_ARCH_FPA /* Soft-float, but FPA order. */ |
| # endif |
| # elif defined (TE_VXWORKS) |
| # define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, VFP order. */ |
| # else |
| /* For backwards compatibility, default to FPA. */ |
| # define FPU_DEFAULT FPU_ARCH_FPA |
| # endif |
| #endif /* ifndef FPU_DEFAULT */ |
| |
| #define streq(a, b) (strcmp (a, b) == 0) |
| |
| /* Current set of feature bits available (CPU+FPU). Different from |
| selected_cpu + selected_fpu in case of autodetection since the CPU |
| feature bits are then all set. */ |
| static arm_feature_set cpu_variant; |
| /* Feature bits used in each execution state. Used to set build attribute |
| (in particular Tag_*_ISA_use) in CPU autodetection mode. */ |
| static arm_feature_set arm_arch_used; |
| static arm_feature_set thumb_arch_used; |
| |
| /* Flags stored in private area of BFD structure. */ |
| static int uses_apcs_26 = false; |
| static int atpcs = false; |
| static int support_interwork = false; |
| static int uses_apcs_float = false; |
| static int pic_code = false; |
| static int fix_v4bx = false; |
| /* Warn on using deprecated features. */ |
| static int warn_on_deprecated = true; |
| static int warn_on_restrict_it = false; |
| |
| /* Understand CodeComposer Studio assembly syntax. */ |
| bool codecomposer_syntax = false; |
| |
| /* Variables that we set while parsing command-line options. Once all |
| options have been read we re-process these values to set the real |
| assembly flags. */ |
| |
| /* CPU and FPU feature bits set for legacy CPU and FPU options (eg. -marm1 |
| instead of -mcpu=arm1). */ |
| static const arm_feature_set *legacy_cpu = NULL; |
| static const arm_feature_set *legacy_fpu = NULL; |
| |
| /* CPU, extension and FPU feature bits selected by -mcpu. */ |
| static const arm_feature_set *mcpu_cpu_opt = NULL; |
| static arm_feature_set *mcpu_ext_opt = NULL; |
| static const arm_feature_set *mcpu_fpu_opt = NULL; |
| |
| /* CPU, extension and FPU feature bits selected by -march. */ |
| static const arm_feature_set *march_cpu_opt = NULL; |
| static arm_feature_set *march_ext_opt = NULL; |
| static const arm_feature_set *march_fpu_opt = NULL; |
| |
| /* Feature bits selected by -mfpu. */ |
| static const arm_feature_set *mfpu_opt = NULL; |
| |
| /* Constants for known architecture features. */ |
| static const arm_feature_set fpu_default = FPU_DEFAULT; |
| static const arm_feature_set fpu_arch_vfp_v1 ATTRIBUTE_UNUSED = FPU_ARCH_VFP_V1; |
| static const arm_feature_set fpu_arch_vfp_v2 = FPU_ARCH_VFP_V2; |
| static const arm_feature_set fpu_arch_vfp_v3 ATTRIBUTE_UNUSED = FPU_ARCH_VFP_V3; |
| static const arm_feature_set fpu_arch_neon_v1 ATTRIBUTE_UNUSED = FPU_ARCH_NEON_V1; |
| static const arm_feature_set fpu_arch_fpa = FPU_ARCH_FPA; |
| static const arm_feature_set fpu_any_hard = FPU_ANY_HARD; |
| #ifdef OBJ_ELF |
| static const arm_feature_set fpu_arch_maverick = FPU_ARCH_MAVERICK; |
| #endif |
| static const arm_feature_set fpu_endian_pure = FPU_ARCH_ENDIAN_PURE; |
| |
| #ifdef CPU_DEFAULT |
| static const arm_feature_set cpu_default = CPU_DEFAULT; |
| #endif |
| |
| static const arm_feature_set arm_ext_v1 = ARM_FEATURE_CORE_LOW (ARM_EXT_V1); |
| static const arm_feature_set arm_ext_v2 = ARM_FEATURE_CORE_LOW (ARM_EXT_V2); |
| static const arm_feature_set arm_ext_v2s = ARM_FEATURE_CORE_LOW (ARM_EXT_V2S); |
| static const arm_feature_set arm_ext_v3 = ARM_FEATURE_CORE_LOW (ARM_EXT_V3); |
| static const arm_feature_set arm_ext_v3m = ARM_FEATURE_CORE_LOW (ARM_EXT_V3M); |
| static const arm_feature_set arm_ext_v4 = ARM_FEATURE_CORE_LOW (ARM_EXT_V4); |
| static const arm_feature_set arm_ext_v4t = ARM_FEATURE_CORE_LOW (ARM_EXT_V4T); |
| static const arm_feature_set arm_ext_v5 = ARM_FEATURE_CORE_LOW (ARM_EXT_V5); |
| static const arm_feature_set arm_ext_v4t_5 = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_V4T | ARM_EXT_V5); |
| static const arm_feature_set arm_ext_v5t = ARM_FEATURE_CORE_LOW (ARM_EXT_V5T); |
| static const arm_feature_set arm_ext_v5e = ARM_FEATURE_CORE_LOW (ARM_EXT_V5E); |
| static const arm_feature_set arm_ext_v5exp = ARM_FEATURE_CORE_LOW (ARM_EXT_V5ExP); |
| static const arm_feature_set arm_ext_v5j = ARM_FEATURE_CORE_LOW (ARM_EXT_V5J); |
| static const arm_feature_set arm_ext_v6 = ARM_FEATURE_CORE_LOW (ARM_EXT_V6); |
| static const arm_feature_set arm_ext_v6k = ARM_FEATURE_CORE_LOW (ARM_EXT_V6K); |
| static const arm_feature_set arm_ext_v6t2 = ARM_FEATURE_CORE_LOW (ARM_EXT_V6T2); |
| /* Only for compatability of hint instructions. */ |
| static const arm_feature_set arm_ext_v6k_v6t2 = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_V6K | ARM_EXT_V6T2); |
| static const arm_feature_set arm_ext_v6_notm = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_V6_NOTM); |
| static const arm_feature_set arm_ext_v6_dsp = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_V6_DSP); |
| static const arm_feature_set arm_ext_barrier = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_BARRIER); |
| static const arm_feature_set arm_ext_msr = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_THUMB_MSR); |
| static const arm_feature_set arm_ext_div = ARM_FEATURE_CORE_LOW (ARM_EXT_DIV); |
| static const arm_feature_set arm_ext_v7 = ARM_FEATURE_CORE_LOW (ARM_EXT_V7); |
| static const arm_feature_set arm_ext_v7a = ARM_FEATURE_CORE_LOW (ARM_EXT_V7A); |
| static const arm_feature_set arm_ext_v7r = ARM_FEATURE_CORE_LOW (ARM_EXT_V7R); |
| static const arm_feature_set arm_ext_v8r = ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8R); |
| #ifdef OBJ_ELF |
| static const arm_feature_set ATTRIBUTE_UNUSED arm_ext_v7m = ARM_FEATURE_CORE_LOW (ARM_EXT_V7M); |
| #endif |
| static const arm_feature_set arm_ext_v8 = ARM_FEATURE_CORE_LOW (ARM_EXT_V8); |
| static const arm_feature_set arm_ext_m = |
| ARM_FEATURE_CORE (ARM_EXT_V6M | ARM_EXT_V7M, |
| ARM_EXT2_V8M | ARM_EXT2_V8M_MAIN); |
| static const arm_feature_set arm_ext_mp = ARM_FEATURE_CORE_LOW (ARM_EXT_MP); |
| static const arm_feature_set arm_ext_sec = ARM_FEATURE_CORE_LOW (ARM_EXT_SEC); |
| static const arm_feature_set arm_ext_os = ARM_FEATURE_CORE_LOW (ARM_EXT_OS); |
| static const arm_feature_set arm_ext_adiv = ARM_FEATURE_CORE_LOW (ARM_EXT_ADIV); |
| static const arm_feature_set arm_ext_virt = ARM_FEATURE_CORE_LOW (ARM_EXT_VIRT); |
| static const arm_feature_set arm_ext_pan = ARM_FEATURE_CORE_HIGH (ARM_EXT2_PAN); |
| static const arm_feature_set arm_ext_v8m = ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M); |
| static const arm_feature_set arm_ext_v8m_main = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M_MAIN); |
| static const arm_feature_set arm_ext_v8_1m_main = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN); |
| /* Instructions in ARMv8-M only found in M profile architectures. */ |
| static const arm_feature_set arm_ext_v8m_m_only = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M | ARM_EXT2_V8M_MAIN); |
| static const arm_feature_set arm_ext_v6t2_v8m = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_V6T2_V8M); |
| /* Instructions shared between ARMv8-A and ARMv8-M. */ |
| static const arm_feature_set arm_ext_atomics = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_ATOMICS); |
| #ifdef OBJ_ELF |
| /* DSP instructions Tag_DSP_extension refers to. */ |
| static const arm_feature_set arm_ext_dsp = |
| ARM_FEATURE_CORE_LOW (ARM_EXT_V5E | ARM_EXT_V5ExP | ARM_EXT_V6_DSP); |
| #endif |
| static const arm_feature_set arm_ext_ras = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_RAS); |
| /* FP16 instructions. */ |
| static const arm_feature_set arm_ext_fp16 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_INST); |
| static const arm_feature_set arm_ext_fp16_fml = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_FML); |
| static const arm_feature_set arm_ext_v8_2 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_2A); |
| static const arm_feature_set arm_ext_v8_3 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_3A); |
| static const arm_feature_set arm_ext_sb = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_SB); |
| static const arm_feature_set arm_ext_predres = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_PREDRES); |
| static const arm_feature_set arm_ext_bf16 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_BF16); |
| static const arm_feature_set arm_ext_i8mm = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_I8MM); |
| static const arm_feature_set arm_ext_crc = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CRC); |
| static const arm_feature_set arm_ext_cde = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE); |
| static const arm_feature_set arm_ext_cde0 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE0); |
| static const arm_feature_set arm_ext_cde1 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE1); |
| static const arm_feature_set arm_ext_cde2 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE2); |
| static const arm_feature_set arm_ext_cde3 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE3); |
| static const arm_feature_set arm_ext_cde4 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE4); |
| static const arm_feature_set arm_ext_cde5 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE5); |
| static const arm_feature_set arm_ext_cde6 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE6); |
| static const arm_feature_set arm_ext_cde7 = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE7); |
| |
| static const arm_feature_set arm_arch_any = ARM_ANY; |
| static const arm_feature_set fpu_any = FPU_ANY; |
| static const arm_feature_set arm_arch_full ATTRIBUTE_UNUSED = ARM_FEATURE (-1, -1, -1); |
| static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2; |
| static const arm_feature_set arm_arch_none = ARM_ARCH_NONE; |
| |
| static const arm_feature_set arm_cext_iwmmxt2 = |
| ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT2); |
| static const arm_feature_set arm_cext_iwmmxt = |
| ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT); |
| static const arm_feature_set arm_cext_xscale = |
| ARM_FEATURE_COPROC (ARM_CEXT_XSCALE); |
| static const arm_feature_set arm_cext_maverick = |
| ARM_FEATURE_COPROC (ARM_CEXT_MAVERICK); |
| static const arm_feature_set fpu_fpa_ext_v1 = |
| ARM_FEATURE_COPROC (FPU_FPA_EXT_V1); |
| static const arm_feature_set fpu_fpa_ext_v2 = |
| ARM_FEATURE_COPROC (FPU_FPA_EXT_V2); |
| static const arm_feature_set fpu_vfp_ext_v1xd = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_V1xD); |
| static const arm_feature_set fpu_vfp_ext_v1 = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_V1); |
| static const arm_feature_set fpu_vfp_ext_v2 = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_V2); |
| static const arm_feature_set fpu_vfp_ext_v3xd = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_V3xD); |
| static const arm_feature_set fpu_vfp_ext_v3 = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_V3); |
| static const arm_feature_set fpu_vfp_ext_d32 = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_D32); |
| static const arm_feature_set fpu_neon_ext_v1 = |
| ARM_FEATURE_COPROC (FPU_NEON_EXT_V1); |
| static const arm_feature_set fpu_vfp_v3_or_neon_ext = |
| ARM_FEATURE_COPROC (FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3); |
| static const arm_feature_set mve_ext = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_MVE); |
| static const arm_feature_set mve_fp_ext = |
| ARM_FEATURE_CORE_HIGH (ARM_EXT2_MVE_FP); |
| /* Note: This has more than one bit set, which means using it with |
| mark_feature_used (which returns if *any* of the bits are set in the current |
| cpu variant) can give surprising results. */ |
| static const arm_feature_set armv8m_fp = |
| ARM_FEATURE_COPROC (FPU_VFP_V5_SP_D16); |
| #ifdef OBJ_ELF |
| static const arm_feature_set fpu_vfp_fp16 = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_FP16); |
| static const arm_feature_set fpu_neon_ext_fma = |
| ARM_FEATURE_COPROC (FPU_NEON_EXT_FMA); |
| #endif |
| static const arm_feature_set fpu_vfp_ext_fma = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_FMA); |
| static const arm_feature_set fpu_vfp_ext_armv8 = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_ARMV8); |
| static const arm_feature_set fpu_vfp_ext_armv8xd = |
| ARM_FEATURE_COPROC (FPU_VFP_EXT_ARMV8xD); |
| static const arm_feature_set fpu_neon_ext_armv8 = |
| ARM_FEATURE_COPROC (FPU_NEON_EXT_ARMV8); |
| static const arm_feature_set fpu_crypto_ext_armv8 = |
| ARM_FEATURE_COPROC (FPU_CRYPTO_EXT_ARMV8); |
| static const arm_feature_set fpu_neon_ext_v8_1 = |
| ARM_FEATURE_COPROC (FPU_NEON_EXT_RDMA); |
| static const arm_feature_set fpu_neon_ext_dotprod = |
| ARM_FEATURE_COPROC (FPU_NEON_EXT_DOTPROD); |
| static const arm_feature_set pacbti_ext = |
| ARM_FEATURE_CORE_HIGH_HIGH (ARM_EXT3_PACBTI); |
| |
| static int mfloat_abi_opt = -1; |
| /* Architecture feature bits selected by the last -mcpu/-march or .cpu/.arch |
| directive. */ |
| static arm_feature_set selected_arch = ARM_ARCH_NONE; |
| /* Extension feature bits selected by the last -mcpu/-march or .arch_extension |
| directive. */ |
| static arm_feature_set selected_ext = ARM_ARCH_NONE; |
| /* Feature bits selected by the last -mcpu/-march or by the combination of the |
| last .cpu/.arch directive .arch_extension directives since that |
| directive. */ |
| static arm_feature_set selected_cpu = ARM_ARCH_NONE; |
| /* FPU feature bits selected by the last -mfpu or .fpu directive. */ |
| static arm_feature_set selected_fpu = FPU_NONE; |
| /* Feature bits selected by the last .object_arch directive. */ |
| static arm_feature_set selected_object_arch = ARM_ARCH_NONE; |
| /* Must be long enough to hold any of the names in arm_cpus. */ |
| static const struct arm_ext_table * selected_ctx_ext_table = NULL; |
| static char selected_cpu_name[20]; |
| |
| extern FLONUM_TYPE generic_floating_point_number; |
| |
| /* Return if no cpu was selected on command-line. */ |
| static bool |
| no_cpu_selected (void) |
| { |
| return ARM_FEATURE_EQUAL (selected_cpu, arm_arch_none); |
| } |
| |
| #ifdef OBJ_ELF |
| # ifdef EABI_DEFAULT |
| static int meabi_flags = EABI_DEFAULT; |
| # else |
| static int meabi_flags = EF_ARM_EABI_UNKNOWN; |
| # endif |
| |
| static int attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES]; |
| |
| bool |
| arm_is_eabi (void) |
| { |
| return (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4); |
| } |
| #endif |
| |
| #ifdef OBJ_ELF |
| /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ |
| symbolS * GOT_symbol; |
| #endif |
| |
| /* 0: assemble for ARM, |
| 1: assemble for Thumb, |
| 2: assemble for Thumb even though target CPU does not support thumb |
| instructions. */ |
| static int thumb_mode = 0; |
| /* A value distinct from the possible values for thumb_mode that we |
| can use to record whether thumb_mode has been copied into the |
| tc_frag_data field of a frag. */ |
| #define MODE_RECORDED (1 << 4) |
| |
| /* Specifies the intrinsic IT insn behavior mode. */ |
| enum implicit_it_mode |
| { |
| IMPLICIT_IT_MODE_NEVER = 0x00, |
| IMPLICIT_IT_MODE_ARM = 0x01, |
| IMPLICIT_IT_MODE_THUMB = 0x02, |
| IMPLICIT_IT_MODE_ALWAYS = (IMPLICIT_IT_MODE_ARM | IMPLICIT_IT_MODE_THUMB) |
| }; |
| static int implicit_it_mode = IMPLICIT_IT_MODE_ARM; |
| |
| /* If unified_syntax is true, we are processing the new unified |
| ARM/Thumb syntax. Important differences from the old ARM mode: |
| |
| - Immediate operands do not require a # prefix. |
| - Conditional affixes always appear at the end of the |
| instruction. (For backward compatibility, those instructions |
| that formerly had them in the middle, continue to accept them |
| there.) |
| - The IT instruction may appear, and if it does is validated |
| against subsequent conditional affixes. It does not generate |
| machine code. |
| |
| Important differences from the old Thumb mode: |
| |
| - Immediate operands do not require a # prefix. |
| - Most of the V6T2 instructions are only available in unified mode. |
| - The .N and .W suffixes are recognized and honored (it is an error |
| if they cannot be honored). |
| - All instructions set the flags if and only if they have an 's' affix. |
| - Conditional affixes may be used. They are validated against |
| preceding IT instructions. Unlike ARM mode, you cannot use a |
| conditional affix except in the scope of an IT instruction. */ |
| |
| static bool unified_syntax = false; |
| |
| /* An immediate operand can start with #, and ld*, st*, pld operands |
| can contain [ and ]. We need to tell APP not to elide whitespace |
| before a [, which can appear as the first operand for pld. |
| Likewise, a { can appear as the first operand for push, pop, vld*, etc. */ |
| const char arm_symbol_chars[] = "#[]{}"; |
| |
| enum neon_el_type |
| { |
| NT_invtype, |
| NT_untyped, |
| NT_integer, |
| NT_float, |
| NT_poly, |
| NT_signed, |
| NT_bfloat, |
| NT_unsigned |
| }; |
| |
| struct neon_type_el |
| { |
| enum neon_el_type type; |
| unsigned size; |
| }; |
| |
| #define NEON_MAX_TYPE_ELS 5 |
| |
| struct neon_type |
| { |
| struct neon_type_el el[NEON_MAX_TYPE_ELS]; |
| unsigned elems; |
| }; |
| |
| enum pred_instruction_type |
| { |
| OUTSIDE_PRED_INSN, |
| INSIDE_VPT_INSN, |
| INSIDE_IT_INSN, |
| INSIDE_IT_LAST_INSN, |
| IF_INSIDE_IT_LAST_INSN, /* Either outside or inside; |
| if inside, should be the last one. */ |
| NEUTRAL_IT_INSN, /* This could be either inside or outside, |
| i.e. BKPT and NOP. */ |
| IT_INSN, /* The IT insn has been parsed. */ |
| VPT_INSN, /* The VPT/VPST insn has been parsed. */ |
| MVE_OUTSIDE_PRED_INSN , /* Instruction to indicate a MVE instruction without |
| a predication code. */ |
| MVE_UNPREDICABLE_INSN, /* MVE instruction that is non-predicable. */ |
| }; |
| |
| /* The maximum number of operands we need. */ |
| #define ARM_IT_MAX_OPERANDS 6 |
| #define ARM_IT_MAX_RELOCS 3 |
| |
| struct arm_it |
| { |
| const char * error; |
| unsigned long instruction; |
| unsigned int size; |
| unsigned int size_req; |
| unsigned int cond; |
| /* "uncond_value" is set to the value in place of the conditional field in |
| unconditional versions of the instruction, or -1u if nothing is |
| appropriate. */ |
| unsigned int uncond_value; |
| struct neon_type vectype; |
| /* This does not indicate an actual NEON instruction, only that |
| the mnemonic accepts neon-style type suffixes. */ |
| int is_neon; |
| /* Set to the opcode if the instruction needs relaxation. |
| Zero if the instruction is not relaxed. */ |
| unsigned long relax; |
| struct |
| { |
| bfd_reloc_code_real_type type; |
| expressionS exp; |
| int pc_rel; |
| } relocs[ARM_IT_MAX_RELOCS]; |
| |
| enum pred_instruction_type pred_insn_type; |
| |
| struct |
| { |
| unsigned reg; |
| signed int imm; |
| struct neon_type_el vectype; |
| unsigned present : 1; /* Operand present. */ |
| unsigned isreg : 1; /* Operand was a register. */ |
| unsigned immisreg : 2; /* .imm field is a second register. |
| 0: imm, 1: gpr, 2: MVE Q-register. */ |
| unsigned isscalar : 2; /* Operand is a (SIMD) scalar: |
| 0) not scalar, |
| 1) Neon scalar, |
| 2) MVE scalar. */ |
| unsigned immisalign : 1; /* Immediate is an alignment specifier. */ |
| unsigned immisfloat : 1; /* Immediate was parsed as a float. */ |
| /* Note: we abuse "regisimm" to mean "is Neon register" in VMOV |
| instructions. This allows us to disambiguate ARM <-> vector insns. */ |
| unsigned regisimm : 1; /* 64-bit immediate, reg forms high 32 bits. */ |
| unsigned isvec : 1; /* Is a single, double or quad VFP/Neon reg. */ |
| unsigned isquad : 1; /* Operand is SIMD quad register. */ |
| unsigned issingle : 1; /* Operand is VFP single-precision register. */ |
| unsigned iszr : 1; /* Operand is ZR register. */ |
| unsigned hasreloc : 1; /* Operand has relocation suffix. */ |
| unsigned writeback : 1; /* Operand has trailing ! */ |
| unsigned preind : 1; /* Preindexed address. */ |
| unsigned postind : 1; /* Postindexed address. */ |
| unsigned negative : 1; /* Index register was negated. */ |
| unsigned shifted : 1; /* Shift applied to operation. */ |
| unsigned shift_kind : 3; /* Shift operation (enum shift_kind). */ |
| } operands[ARM_IT_MAX_OPERANDS]; |
| }; |
| |
| static struct arm_it inst; |
| |
| #define NUM_FLOAT_VALS 8 |
| |
| const char * fp_const[] = |
| { |
| "0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0 |
| }; |
| |
| LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS]; |
| |
| #define FAIL (-1) |
| #define SUCCESS (0) |
| |
| #define SUFF_S 1 |
| #define SUFF_D 2 |
| #define SUFF_E 3 |
| #define SUFF_P 4 |
| |
| #define CP_T_X 0x00008000 |
| #define CP_T_Y 0x00400000 |
| |
| #define CONDS_BIT 0x00100000 |
| #define LOAD_BIT 0x00100000 |
| |
| #define DOUBLE_LOAD_FLAG 0x00000001 |
| |
| struct asm_cond |
| { |
| const char * template_name; |
| unsigned long value; |
| }; |
| |
| #define COND_ALWAYS 0xE |
| |
| struct asm_psr |
| { |
| const char * template_name; |
| unsigned long field; |
| }; |
| |
| struct asm_barrier_opt |
| { |
| const char * template_name; |
| unsigned long value; |
| const arm_feature_set arch; |
| }; |
| |
| /* The bit that distinguishes CPSR and SPSR. */ |
| #define SPSR_BIT (1 << 22) |
| |
| /* The individual PSR flag bits. */ |
| #define PSR_c (1 << 16) |
| #define PSR_x (1 << 17) |
| #define PSR_s (1 << 18) |
| #define PSR_f (1 << 19) |
| |
| struct reloc_entry |
| { |
| const char * name; |
| bfd_reloc_code_real_type reloc; |
| }; |
| |
| enum vfp_reg_pos |
| { |
| VFP_REG_Sd, VFP_REG_Sm, VFP_REG_Sn, |
| VFP_REG_Dd, VFP_REG_Dm, VFP_REG_Dn |
| }; |
| |
| enum vfp_ldstm_type |
| { |
| VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX |
| }; |
| |
| /* Bits for DEFINED field in neon_typed_alias. */ |
| #define NTA_HASTYPE 1 |
| #define NTA_HASINDEX 2 |
| |
| struct neon_typed_alias |
| { |
| unsigned char defined; |
| unsigned char index; |
| struct neon_type_el eltype; |
| }; |
| |
| /* ARM register categories. This includes coprocessor numbers and various |
| architecture extensions' registers. Each entry should have an error message |
| in reg_expected_msgs below. */ |
| enum arm_reg_type |
| { |
| REG_TYPE_RN, |
| REG_TYPE_CP, |
| REG_TYPE_CN, |
| REG_TYPE_FN, |
| REG_TYPE_VFS, |
| REG_TYPE_VFD, |
| REG_TYPE_NQ, |
| REG_TYPE_VFSD, |
| REG_TYPE_NDQ, |
| REG_TYPE_NSD, |
| REG_TYPE_NSDQ, |
| REG_TYPE_VFC, |
| REG_TYPE_MVF, |
| REG_TYPE_MVD, |
| REG_TYPE_MVFX, |
| REG_TYPE_MVDX, |
| REG_TYPE_MVAX, |
| REG_TYPE_MQ, |
| REG_TYPE_DSPSC, |
| REG_TYPE_MMXWR, |
| REG_TYPE_MMXWC, |
| REG_TYPE_MMXWCG, |
| REG_TYPE_XSCALE, |
| REG_TYPE_RNB, |
| REG_TYPE_ZR |
| }; |
| |
| /* Structure for a hash table entry for a register. |
| If TYPE is REG_TYPE_VFD or REG_TYPE_NQ, the NEON field can point to extra |
| information which states whether a vector type or index is specified (for a |
| register alias created with .dn or .qn). Otherwise NEON should be NULL. */ |
| struct reg_entry |
| { |
| const char * name; |
| unsigned int number; |
| unsigned char type; |
| unsigned char builtin; |
| struct neon_typed_alias * neon; |
| }; |
| |
| /* Diagnostics used when we don't get a register of the expected type. */ |
| const char * const reg_expected_msgs[] = |
| { |
| [REG_TYPE_RN] = N_("ARM register expected"), |
| [REG_TYPE_CP] = N_("bad or missing co-processor number"), |
| [REG_TYPE_CN] = N_("co-processor register expected"), |
| [REG_TYPE_FN] = N_("FPA register expected"), |
| [REG_TYPE_VFS] = N_("VFP single precision register expected"), |
| [REG_TYPE_VFD] = N_("VFP/Neon double precision register expected"), |
| [REG_TYPE_NQ] = N_("Neon quad precision register expected"), |
| [REG_TYPE_VFSD] = N_("VFP single or double precision register expected"), |
| [REG_TYPE_NDQ] = N_("Neon double or quad precision register expected"), |
| [REG_TYPE_NSD] = N_("Neon single or double precision register expected"), |
| [REG_TYPE_NSDQ] = N_("VFP single, double or Neon quad precision register" |
| " expected"), |
| [REG_TYPE_VFC] = N_("VFP system register expected"), |
| [REG_TYPE_MVF] = N_("Maverick MVF register expected"), |
| [REG_TYPE_MVD] = N_("Maverick MVD register expected"), |
| [REG_TYPE_MVFX] = N_("Maverick MVFX register expected"), |
| [REG_TYPE_MVDX] = N_("Maverick MVDX register expected"), |
| [REG_TYPE_MVAX] = N_("Maverick MVAX register expected"), |
| [REG_TYPE_DSPSC] = N_("Maverick DSPSC register expected"), |
| [REG_TYPE_MMXWR] = N_("iWMMXt data register expected"), |
| [REG_TYPE_MMXWC] = N_("iWMMXt control register expected"), |
| [REG_TYPE_MMXWCG] = N_("iWMMXt scalar register expected"), |
| [REG_TYPE_XSCALE] = N_("XScale accumulator register expected"), |
| [REG_TYPE_MQ] = N_("MVE vector register expected"), |
| [REG_TYPE_RNB] = "", |
| [REG_TYPE_ZR] = N_("ZR register expected"), |
| }; |
| |
| /* Some well known registers that we refer to directly elsewhere. */ |
| #define REG_R12 12 |
| #define REG_SP 13 |
| #define REG_LR 14 |
| #define REG_PC 15 |
| |
| /* ARM instructions take 4bytes in the object file, Thumb instructions |
| take 2: */ |
| #define INSN_SIZE 4 |
| |
| struct asm_opcode |
| { |
| /* Basic string to match. */ |
| const char * template_name; |
| |
| /* Parameters to instruction. */ |
| unsigned int operands[8]; |
| |
| /* Conditional tag - see opcode_lookup. */ |
| unsigned int tag : 4; |
| |
| /* Basic instruction code. */ |
| unsigned int avalue; |
| |
| /* Thumb-format instruction code. */ |
| unsigned int tvalue; |
| |
| /* Which architecture variant provides this instruction. */ |
| const arm_feature_set * avariant; |
| const arm_feature_set * tvariant; |
| |
| /* Function to call to encode instruction in ARM format. */ |
| void (* aencode) (void); |
| |
| /* Function to call to encode instruction in Thumb format. */ |
| void (* tencode) (void); |
| |
| /* Indicates whether this instruction may be vector predicated. */ |
| unsigned int mayBeVecPred : 1; |
| }; |
| |
| /* Defines for various bits that we will want to toggle. */ |
| #define INST_IMMEDIATE 0x02000000 |
| #define OFFSET_REG 0x02000000 |
| #define HWOFFSET_IMM 0x00400000 |
| #define SHIFT_BY_REG 0x00000010 |
| #define PRE_INDEX 0x01000000 |
| #define INDEX_UP 0x00800000 |
| #define WRITE_BACK 0x00200000 |
| #define LDM_TYPE_2_OR_3 0x00400000 |
| #define CPSI_MMOD 0x00020000 |
| |
| #define LITERAL_MASK 0xf000f000 |
| #define OPCODE_MASK 0xfe1fffff |
| #define V4_STR_BIT 0x00000020 |
| #define VLDR_VMOV_SAME 0x0040f000 |
| |
| #define T2_SUBS_PC_LR 0xf3de8f00 |
| |
| #define DATA_OP_SHIFT 21 |
| #define SBIT_SHIFT 20 |
| |
| #define T2_OPCODE_MASK 0xfe1fffff |
| #define T2_DATA_OP_SHIFT 21 |
| #define T2_SBIT_SHIFT 20 |
| |
| #define A_COND_MASK 0xf0000000 |
| #define A_PUSH_POP_OP_MASK 0x0fff0000 |
| |
| /* Opcodes for pushing/poping registers to/from the stack. */ |
| #define A1_OPCODE_PUSH 0x092d0000 |
| #define A2_OPCODE_PUSH 0x052d0004 |
| #define A2_OPCODE_POP 0x049d0004 |
| |
| /* Codes to distinguish the arithmetic instructions. */ |
| #define OPCODE_AND 0 |
| #define OPCODE_EOR 1 |
| #define OPCODE_SUB 2 |
| #define OPCODE_RSB 3 |
| #define OPCODE_ADD 4 |
| #define OPCODE_ADC 5 |
| #define OPCODE_SBC 6 |
| #define OPCODE_RSC 7 |
| #define OPCODE_TST 8 |
| #define OPCODE_TEQ 9 |
| #define OPCODE_CMP 10 |
| #define OPCODE_CMN 11 |
| #define OPCODE_ORR 12 |
| #define OPCODE_MOV 13 |
| #define OPCODE_BIC 14 |
| #define OPCODE_MVN 15 |
| |
| #define T2_OPCODE_AND 0 |
| #define T2_OPCODE_BIC 1 |
| #define T2_OPCODE_ORR 2 |
| #define T2_OPCODE_ORN 3 |
| #define T2_OPCODE_EOR 4 |
| #define T2_OPCODE_ADD 8 |
| #define T2_OPCODE_ADC 10 |
| #define T2_OPCODE_SBC 11 |
| #define T2_OPCODE_SUB 13 |
| #define T2_OPCODE_RSB 14 |
| |
| #define T_OPCODE_MUL 0x4340 |
| #define T_OPCODE_TST 0x4200 |
| #define T_OPCODE_CMN 0x42c0 |
| #define T_OPCODE_NEG 0x4240 |
| #define T_OPCODE_MVN 0x43c0 |
| |
| #define T_OPCODE_ADD_R3 0x1800 |
| #define T_OPCODE_SUB_R3 0x1a00 |
| #define T_OPCODE_ADD_HI 0x4400 |
| #define T_OPCODE_ADD_ST 0xb000 |
| #define T_OPCODE_SUB_ST 0xb080 |
| #define T_OPCODE_ADD_SP 0xa800 |
| #define T_OPCODE_ADD_PC 0xa000 |
| #define T_OPCODE_ADD_I8 0x3000 |
| #define T_OPCODE_SUB_I8 0x3800 |
| #define T_OPCODE_ADD_I3 0x1c00 |
| #define T_OPCODE_SUB_I3 0x1e00 |
| |
| #define T_OPCODE_ASR_R 0x4100 |
| #define T_OPCODE_LSL_R 0x4080 |
| #define T_OPCODE_LSR_R 0x40c0 |
| #define T_OPCODE_ROR_R 0x41c0 |
| #define T_OPCODE_ASR_I 0x1000 |
| #define T_OPCODE_LSL_I 0x0000 |
| #define T_OPCODE_LSR_I 0x0800 |
| |
| #define T_OPCODE_MOV_I8 0x2000 |
| #define T_OPCODE_CMP_I8 0x2800 |
| #define T_OPCODE_CMP_LR 0x4280 |
| #define T_OPCODE_MOV_HR 0x4600 |
| #define T_OPCODE_CMP_HR 0x4500 |
| |
| #define T_OPCODE_LDR_PC 0x4800 |
| #define T_OPCODE_LDR_SP 0x9800 |
| #define T_OPCODE_STR_SP 0x9000 |
| #define T_OPCODE_LDR_IW 0x6800 |
| #define T_OPCODE_STR_IW 0x6000 |
| #define T_OPCODE_LDR_IH 0x8800 |
| #define T_OPCODE_STR_IH 0x8000 |
| #define T_OPCODE_LDR_IB 0x7800 |
| #define T_OPCODE_STR_IB 0x7000 |
| #define T_OPCODE_LDR_RW 0x5800 |
| #define T_OPCODE_STR_RW 0x5000 |
| #define T_OPCODE_LDR_RH 0x5a00 |
| #define T_OPCODE_STR_RH 0x5200 |
| #define T_OPCODE_LDR_RB 0x5c00 |
| #define T_OPCODE_STR_RB 0x5400 |
| |
| #define T_OPCODE_PUSH 0xb400 |
| #define T_OPCODE_POP 0xbc00 |
| |
| #define T_OPCODE_BRANCH 0xe000 |
| |
| #define THUMB_SIZE 2 /* Size of thumb instruction. */ |
| #define THUMB_PP_PC_LR 0x0100 |
| #define THUMB_LOAD_BIT 0x0800 |
| #define THUMB2_LOAD_BIT 0x00100000 |
| |
| #define BAD_SYNTAX _("syntax error") |
| #define BAD_ARGS _("bad arguments to instruction") |
| #define BAD_SP _("r13 not allowed here") |
| #define BAD_PC _("r15 not allowed here") |
| #define BAD_ODD _("Odd register not allowed here") |
| #define BAD_EVEN _("Even register not allowed here") |
| #define BAD_COND _("instruction cannot be conditional") |
| #define BAD_OVERLAP _("registers may not be the same") |
| #define BAD_HIREG _("lo register required") |
| #define BAD_THUMB32 _("instruction not supported in Thumb16 mode") |
| #define BAD_ADDR_MODE _("instruction does not accept this addressing mode") |
| #define BAD_BRANCH _("branch must be last instruction in IT block") |
| #define BAD_BRANCH_OFF _("branch out of range or not a multiple of 2") |
| #define BAD_NO_VPT _("instruction not allowed in VPT block") |
| #define BAD_NOT_IT _("instruction not allowed in IT block") |
| #define BAD_NOT_VPT _("instruction missing MVE vector predication code") |
| #define BAD_FPU _("selected FPU does not support instruction") |
| #define BAD_OUT_IT _("thumb conditional instruction should be in IT block") |
| #define BAD_OUT_VPT \ |
| _("vector predicated instruction should be in VPT/VPST block") |
| #define BAD_IT_COND _("incorrect condition in IT block") |
| #define BAD_VPT_COND _("incorrect condition in VPT/VPST block") |
| #define BAD_IT_IT _("IT falling in the range of a previous IT block") |
| #define MISSING_FNSTART _("missing .fnstart before unwinding directive") |
| #define BAD_PC_ADDRESSING \ |
| _("cannot use register index with PC-relative addressing") |
| #define BAD_PC_WRITEBACK \ |
| _("cannot use writeback with PC-relative addressing") |
| #define BAD_RANGE _("branch out of range") |
| #define BAD_FP16 _("selected processor does not support fp16 instruction") |
| #define BAD_BF16 _("selected processor does not support bf16 instruction") |
| #define BAD_CDE _("selected processor does not support cde instruction") |
| #define BAD_CDE_COPROC _("coprocessor for insn is not enabled for cde") |
| #define UNPRED_REG(R) _("using " R " results in unpredictable behaviour") |
| #define THUMB1_RELOC_ONLY _("relocation valid in thumb1 code only") |
| #define MVE_NOT_IT _("Warning: instruction is UNPREDICTABLE in an IT " \ |
| "block") |
| #define MVE_NOT_VPT _("Warning: instruction is UNPREDICTABLE in a VPT " \ |
| "block") |
| #define MVE_BAD_PC _("Warning: instruction is UNPREDICTABLE with PC" \ |
| " operand") |
| #define MVE_BAD_SP _("Warning: instruction is UNPREDICTABLE with SP" \ |
| " operand") |
| #define BAD_SIMD_TYPE _("bad type in SIMD instruction") |
| #define BAD_MVE_AUTO \ |
| _("GAS auto-detection mode and -march=all is deprecated for MVE, please" \ |
| " use a valid -march or -mcpu option.") |
| #define BAD_MVE_SRCDEST _("Warning: 32-bit element size and same destination "\ |
| "and source operands makes instruction UNPREDICTABLE") |
| #define BAD_EL_TYPE _("bad element type for instruction") |
| #define MVE_BAD_QREG _("MVE vector register Q[0..7] expected") |
| #define BAD_PACBTI _("selected processor does not support PACBTI extention") |
| |
| static htab_t arm_ops_hsh; |
| static htab_t arm_cond_hsh; |
| static htab_t arm_vcond_hsh; |
| static htab_t arm_shift_hsh; |
| static htab_t arm_psr_hsh; |
| static htab_t arm_v7m_psr_hsh; |
| static htab_t arm_reg_hsh; |
| static htab_t arm_reloc_hsh; |
| static htab_t arm_barrier_opt_hsh; |
| |
| /* Stuff needed to resolve the label ambiguity |
| As: |
| ... |
| label: <insn> |
| may differ from: |
| ... |
| label: |
| <insn> */ |
| |
| symbolS * last_label_seen; |
| static int label_is_thumb_function_name = false; |
| |
| /* Literal pool structure. Held on a per-section |
| and per-sub-section basis. */ |
| |
| #define MAX_LITERAL_POOL_SIZE 1024 |
| typedef struct literal_pool |
| { |
| expressionS literals [MAX_LITERAL_POOL_SIZE]; |
| unsigned int next_free_entry; |
| unsigned int id; |
| symbolS * symbol; |
| segT section; |
| subsegT sub_section; |
| #ifdef OBJ_ELF |
| struct dwarf2_line_info locs [MAX_LITERAL_POOL_SIZE]; |
| #endif |
| struct literal_pool * next; |
| unsigned int alignment; |
| } literal_pool; |
| |
| /* Pointer to a linked list of literal pools. */ |
| literal_pool * list_of_pools = NULL; |
| |
| typedef enum asmfunc_states |
| { |
| OUTSIDE_ASMFUNC, |
| WAITING_ASMFUNC_NAME, |
| WAITING_ENDASMFUNC |
| } asmfunc_states; |
| |
| static asmfunc_states asmfunc_state = OUTSIDE_ASMFUNC; |
| |
| #ifdef OBJ_ELF |
| # define now_pred seg_info (now_seg)->tc_segment_info_data.current_pred |
| #else |
| static struct current_pred now_pred; |
| #endif |
| |
| static inline int |
| now_pred_compatible (int cond) |
| { |
| return (cond & ~1) == (now_pred.cc & ~1); |
| } |
| |
| static inline int |
| conditional_insn (void) |
| { |
| return inst.cond != COND_ALWAYS; |
| } |
| |
| static int in_pred_block (void); |
| |
| static int handle_pred_state (void); |
| |
| static void force_automatic_it_block_close (void); |
| |
| static void it_fsm_post_encode (void); |
| |
| #define set_pred_insn_type(type) \ |
| do \ |
| { \ |
| inst.pred_insn_type = type; \ |
| if (handle_pred_state () == FAIL) \ |
| return; \ |
| } \ |
| while (0) |
| |
| #define set_pred_insn_type_nonvoid(type, failret) \ |
| do \ |
| { \ |
| inst.pred_insn_type = type; \ |
| if (handle_pred_state () == FAIL) \ |
| return failret; \ |
| } \ |
| while(0) |
| |
| #define set_pred_insn_type_last() \ |
| do \ |
| { \ |
| if (inst.cond == COND_ALWAYS) \ |
| set_pred_insn_type (IF_INSIDE_IT_LAST_INSN); \ |
| else \ |
| set_pred_insn_type (INSIDE_IT_LAST_INSN); \ |
| } \ |
| while (0) |
| |
| /* Toggle value[pos]. */ |
| #define TOGGLE_BIT(value, pos) (value ^ (1 << pos)) |
| |
| /* Pure syntax. */ |
| |
| /* This array holds the chars that always start a comment. If the |
| pre-processor is disabled, these aren't very useful. */ |
| char arm_comment_chars[] = "@"; |
| |
| /* This array holds the chars that only start a comment at the beginning of |
| a line. If the line seems to have the form '# 123 filename' |
| .line and .file directives will appear in the pre-processed output. */ |
| /* Note that input_file.c hand checks for '#' at the beginning of the |
| first line of the input file. This is because the compiler outputs |
| #NO_APP at the beginning of its output. */ |
| /* Also note that comments like this one will always work. */ |
| const char line_comment_chars[] = "#"; |
| |
| char arm_line_separator_chars[] = ";"; |
| |
| /* Chars that can be used to separate mant |
| from exp in floating point numbers. */ |
| const char EXP_CHARS[] = "eE"; |
| |
| /* Chars that mean this number is a floating point constant. */ |
| /* As in 0f12.456 */ |
| /* or 0d1.2345e12 */ |
| |
| const char FLT_CHARS[] = "rRsSfFdDxXeEpPHh"; |
| |
| /* Prefix characters that indicate the start of an immediate |
| value. */ |
| #define is_immediate_prefix(C) ((C) == '#' || (C) == '$') |
| |
| /* Separator character handling. */ |
| |
| #define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0) |
| |
| enum fp_16bit_format |
| { |
| ARM_FP16_FORMAT_IEEE = 0x1, |
| ARM_FP16_FORMAT_ALTERNATIVE = 0x2, |
| ARM_FP16_FORMAT_DEFAULT = 0x3 |
| }; |
| |
| static enum fp_16bit_format fp16_format = ARM_FP16_FORMAT_DEFAULT; |
| |
| |
| static inline int |
| skip_past_char (char ** str, char c) |
| { |
| /* PR gas/14987: Allow for whitespace before the expected character. */ |
| skip_whitespace (*str); |
| |
| if (**str == c) |
| { |
| (*str)++; |
| return SUCCESS; |
| } |
| else |
| return FAIL; |
| } |
| |
| #define skip_past_comma(str) skip_past_char (str, ',') |
| |
| /* Arithmetic expressions (possibly involving symbols). */ |
| |
| /* Return TRUE if anything in the expression is a bignum. */ |
| |
| static bool |
| walk_no_bignums (symbolS * sp) |
| { |
| if (symbol_get_value_expression (sp)->X_op == O_big) |
| return true; |
| |
| if (symbol_get_value_expression (sp)->X_add_symbol) |
| { |
| return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol) |
| || (symbol_get_value_expression (sp)->X_op_symbol |
| && walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol))); |
| } |
| |
| return false; |
| } |
| |
| static bool in_my_get_expression = false; |
| |
| /* Third argument to my_get_expression. */ |
| #define GE_NO_PREFIX 0 |
| #define GE_IMM_PREFIX 1 |
| #define GE_OPT_PREFIX 2 |
| /* This is a bit of a hack. Use an optional prefix, and also allow big (64-bit) |
| immediates, as can be used in Neon VMVN and VMOV immediate instructions. */ |
| #define GE_OPT_PREFIX_BIG 3 |
| |
| static int |
| my_get_expression (expressionS * ep, char ** str, int prefix_mode) |
| { |
| char * save_in; |
| |
| /* In unified syntax, all prefixes are optional. */ |
| if (unified_syntax) |
| prefix_mode = (prefix_mode == GE_OPT_PREFIX_BIG) ? prefix_mode |
| : GE_OPT_PREFIX; |
| |
| switch (prefix_mode) |
| { |
| case GE_NO_PREFIX: break; |
| case GE_IMM_PREFIX: |
| if (!is_immediate_prefix (**str)) |
| { |
| inst.error = _("immediate expression requires a # prefix"); |
| return FAIL; |
| } |
| (*str)++; |
| break; |
| case GE_OPT_PREFIX: |
| case GE_OPT_PREFIX_BIG: |
| if (is_immediate_prefix (**str)) |
| (*str)++; |
| break; |
| default: |
| abort (); |
| } |
| |
| memset (ep, 0, sizeof (expressionS)); |
| |
| save_in = input_line_pointer; |
| input_line_pointer = *str; |
| in_my_get_expression = true; |
| expression (ep); |
| in_my_get_expression = false; |
| |
| if (ep->X_op == O_illegal || ep->X_op == O_absent) |
| { |
| /* We found a bad or missing expression in md_operand(). */ |
| *str = input_line_pointer; |
| input_line_pointer = save_in; |
| if (inst.error == NULL) |
| inst.error = (ep->X_op == O_absent |
| ? _("missing expression") :_("bad expression")); |
| return 1; |
| } |
| |
| /* Get rid of any bignums now, so that we don't generate an error for which |
| we can't establish a line number later on. Big numbers are never valid |
| in instructions, which is where this routine is always called. */ |
| if (prefix_mode != GE_OPT_PREFIX_BIG |
| && (ep->X_op == O_big |
| || (ep->X_add_symbol |
| && (walk_no_bignums (ep->X_add_symbol) |
| || (ep->X_op_symbol |
| && walk_no_bignums (ep->X_op_symbol)))))) |
| { |
| inst.error = _("invalid constant"); |
| *str = input_line_pointer; |
| input_line_pointer = save_in; |
| return 1; |
| } |
| |
| *str = input_line_pointer; |
| input_line_pointer = save_in; |
| return SUCCESS; |
| } |
| |
| /* Turn a string in input_line_pointer into a floating point constant |
| of type TYPE, and store the appropriate bytes in *LITP. The number |
| of LITTLENUMS emitted is stored in *SIZEP. An error message is |
| returned, or NULL on OK. |
| |
| Note that fp constants aren't represent in the normal way on the ARM. |
| In big endian mode, things are as expected. However, in little endian |
| mode fp constants are big-endian word-wise, and little-endian byte-wise |
| within the words. For example, (double) 1.1 in big endian mode is |
| the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is |
| the byte sequence 99 99 f1 3f 9a 99 99 99. |
| |
| ??? The format of 12 byte floats is uncertain according to gcc's arm.h. */ |
| |
| const char * |
| md_atof (int type, char * litP, int * sizeP) |
| { |
| int prec; |
| LITTLENUM_TYPE words[MAX_LITTLENUMS]; |
| char *t; |
| int i; |
| |
| switch (type) |
| { |
| case 'H': |
| case 'h': |
| /* bfloat16, despite not being part of the IEEE specification, can also |
| be handled by atof_ieee(). */ |
| case 'b': |
| prec = 1; |
| break; |
| |
| case 'f': |
| case 'F': |
| case 's': |
| case 'S': |
| prec = 2; |
| break; |
| |
| case 'd': |
| case 'D': |
| case 'r': |
| case 'R': |
| prec = 4; |
| break; |
| |
| case 'x': |
| case 'X': |
| prec = 5; |
| break; |
| |
| case 'p': |
| case 'P': |
| prec = 5; |
| break; |
| |
| default: |
| *sizeP = 0; |
| return _("Unrecognized or unsupported floating point constant"); |
| } |
| |
| t = atof_ieee (input_line_pointer, type, words); |
| if (t) |
| input_line_pointer = t; |
| *sizeP = prec * sizeof (LITTLENUM_TYPE); |
| |
| if (target_big_endian || prec == 1) |
| for (i = 0; i < prec; i++) |
| { |
| md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); |
| litP += sizeof (LITTLENUM_TYPE); |
| } |
| else if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_endian_pure)) |
| for (i = prec - 1; i >= 0; i--) |
| { |
| md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE)); |
| litP += sizeof (LITTLENUM_TYPE); |
| } |
| else |
| /* For a 4 byte float the order of elements in `words' is 1 0. |
| For an 8 byte float the order is 1 0 3 2. */ |
| for (i = 0; i < prec; i += 2) |
| { |
| md_number_to_chars (litP, (valueT) words[i + 1], |
| sizeof (LITTLENUM_TYPE)); |
| md_number_to_chars (litP + sizeof (LITTLENUM_TYPE), |
| (valueT) words[i], sizeof (LITTLENUM_TYPE)); |
| litP += 2 * sizeof (LITTLENUM_TYPE); |
| } |
| |
| return NULL; |
| } |
| |
| /* We handle all bad expressions here, so that we can report the faulty |
| instruction in the error message. */ |
| |
| void |
| md_operand (expressionS * exp) |
| { |
| if (in_my_get_expression) |
| exp->X_op = O_illegal; |
| } |
| |
| /* Immediate values. */ |
| |
| #ifdef OBJ_ELF |
| /* Generic immediate-value read function for use in directives. |
| Accepts anything that 'expression' can fold to a constant. |
| *val receives the number. */ |
| |
| static int |
| immediate_for_directive (int *val) |
| { |
| expressionS exp; |
| exp.X_op = O_illegal; |
| |
| if (is_immediate_prefix (*input_line_pointer)) |
| { |
| input_line_pointer++; |
| expression (&exp); |
| } |
| |
| if (exp.X_op != O_constant) |
| { |
| as_bad (_("expected #constant")); |
| ignore_rest_of_line (); |
| return FAIL; |
| } |
| *val = exp.X_add_number; |
| return SUCCESS; |
| } |
| #endif |
| |
| /* Register parsing. */ |
| |
| /* Generic register parser. CCP points to what should be the |
| beginning of a register name. If it is indeed a valid register |
| name, advance CCP over it and return the reg_entry structure; |
| otherwise return NULL. Does not issue diagnostics. */ |
| |
| static struct reg_entry * |
| arm_reg_parse_multi (char **ccp) |
| { |
| char *start = *ccp; |
| char *p; |
| struct reg_entry *reg; |
| |
| skip_whitespace (start); |
| |
| #ifdef REGISTER_PREFIX |
| if (*start != REGISTER_PREFIX) |
| return NULL; |
| start++; |
| #endif |
| #ifdef OPTIONAL_REGISTER_PREFIX |
| if (*start == OPTIONAL_REGISTER_PREFIX) |
| start++; |
| #endif |
| |
| p = start; |
| if (!ISALPHA (*p) || !is_name_beginner (*p)) |
| return NULL; |
| |
| do |
| p++; |
| while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_'); |
| |
| reg = (struct reg_entry *) str_hash_find_n (arm_reg_hsh, start, p - start); |
| |
| if (!reg) |
| return NULL; |
| |
| *ccp = p; |
| return reg; |
| } |
| |
| static int |
| arm_reg_alt_syntax (char **ccp, char *start, struct reg_entry *reg, |
| enum arm_reg_type type) |
| { |
| /* Alternative syntaxes are accepted for a few register classes. */ |
| switch (type) |
| { |
| case REG_TYPE_MVF: |
| case REG_TYPE_MVD: |
| case REG_TYPE_MVFX: |
| case REG_TYPE_MVDX: |
| /* Generic coprocessor register names are allowed for these. */ |
| if (reg && reg->type == REG_TYPE_CN) |
| return reg->number; |
| break; |
| |
| case REG_TYPE_CP: |
| /* For backward compatibility, a bare number is valid here. */ |
| { |
| unsigned long processor = strtoul (start, ccp, 10); |
| if (*ccp != start && processor <= 15) |
| return processor; |
| } |
| /* Fall through. */ |
| |
| case REG_TYPE_MMXWC: |
| /* WC includes WCG. ??? I'm not sure this is true for all |
| instructions that take WC registers. */ |
| if (reg && reg->type == REG_TYPE_MMXWCG) |
| return reg->number; |
| break; |
| |
| default: |
| break; |
| } |
| |
| return FAIL; |
| } |
| |
| /* As arm_reg_parse_multi, but the register must be of type TYPE, and the |
| return value is the register number or FAIL. */ |
| |
| static int |
| arm_reg_parse (char **ccp, enum arm_reg_type type) |
| { |
| char *start = *ccp; |
| struct reg_entry *reg = arm_reg_parse_multi (ccp); |
| int ret; |
| |
| /* Do not allow a scalar (reg+index) to parse as a register. */ |
| if (reg && reg->neon && (reg->neon->defined & NTA_HASINDEX)) |
| return FAIL; |
| |
| if (reg && reg->type == type) |
| return reg->number; |
| |
| if ((ret = arm_reg_alt_syntax (ccp, start, reg, type)) != FAIL) |
| return ret; |
| |
| *ccp = start; |
| return FAIL; |
| } |
| |
| /* Parse a Neon type specifier. *STR should point at the leading '.' |
| character. Does no verification at this stage that the type fits the opcode |
| properly. E.g., |
| |
| .i32.i32.s16 |
| .s32.f32 |
| .u16 |
| |
| Can all be legally parsed by this function. |
| |
| Fills in neon_type struct pointer with parsed information, and updates STR |
| to point after the parsed type specifier. Returns SUCCESS if this was a legal |
| type, FAIL if not. */ |
| |
| static int |
| parse_neon_type (struct neon_type *type, char **str) |
| { |
| char *ptr = *str; |
| |
| if (type) |
| type->elems = 0; |
| |
| while (type->elems < NEON_MAX_TYPE_ELS) |
| { |
| enum neon_el_type thistype = NT_untyped; |
| unsigned thissize = -1u; |
| |
| if (*ptr != '.') |
| break; |
| |
| ptr++; |
| |
| /* Just a size without an explicit type. */ |
| if (ISDIGIT (*ptr)) |
| goto parsesize; |
| |
| switch (TOLOWER (*ptr)) |
| { |
| case 'i': thistype = NT_integer; break; |
| case 'f': thistype = NT_float; break; |
| case 'p': thistype = NT_poly; break; |
| case 's': thistype = NT_signed; break; |
| case 'u': thistype = NT_unsigned; break; |
| case 'd': |
| thistype = NT_float; |
| thissize = 64; |
| ptr++; |
| goto done; |
| case 'b': |
| thistype = NT_bfloat; |
| switch (TOLOWER (*(++ptr))) |
| { |
| case 'f': |
| ptr += 1; |
| thissize = strtoul (ptr, &ptr, 10); |
| if (thissize != 16) |
| { |
| as_bad (_("bad size %d in type specifier"), thissize); |
| return FAIL; |
| } |
| goto done; |
| case '0': case '1': case '2': case '3': case '4': |
| case '5': case '6': case '7': case '8': case '9': |
| case ' ': case '.': |
| as_bad (_("unexpected type character `b' -- did you mean `bf'?")); |
| return FAIL; |
| default: |
| break; |
| } |
| break; |
| default: |
| as_bad (_("unexpected character `%c' in type specifier"), *ptr); |
| return FAIL; |
| } |
| |
| ptr++; |
| |
| /* .f is an abbreviation for .f32. */ |
| if (thistype == NT_float && !ISDIGIT (*ptr)) |
| thissize = 32; |
| else |
| { |
| parsesize: |
| thissize = strtoul (ptr, &ptr, 10); |
| |
| if (thissize != 8 && thissize != 16 && thissize != 32 |
| && thissize != 64) |
| { |
| as_bad (_("bad size %d in type specifier"), thissize); |
| return FAIL; |
| } |
| } |
| |
| done: |
| if (type) |
| { |
| type->el[type->elems].type = thistype; |
| type->el[type->elems].size = thissize; |
| type->elems++; |
| } |
| } |
| |
| /* Empty/missing type is not a successful parse. */ |
| if (type->elems == 0) |
| return FAIL; |
| |
| *str = ptr; |
| |
| return SUCCESS; |
| } |
| |
| /* Errors may be set multiple times during parsing or bit encoding |
| (particularly in the Neon bits), but usually the earliest error which is set |
| will be the most meaningful. Avoid overwriting it with later (cascading) |
| errors by calling this function. */ |
| |
| static void |
| first_error (const char *err) |
| { |
| if (!inst.error) |
| inst.error = err; |
| } |
| |
| /* Parse a single type, e.g. ".s32", leading period included. */ |
| static int |
| parse_neon_operand_type (struct neon_type_el *vectype, char **ccp) |
| { |
| char *str = *ccp; |
| struct neon_type optype; |
| |
| if (*str == '.') |
| { |
| if (parse_neon_type (&optype, &str) == SUCCESS) |
| { |
| if (optype.elems == 1) |
| *vectype = optype.el[0]; |
| else |
| { |
| first_error (_("only one type should be specified for operand")); |
| return FAIL; |
| } |
| } |
| else |
| { |
| first_error (_("vector type expected")); |
| return FAIL; |
| } |
| } |
| else |
| return FAIL; |
| |
| *ccp = str; |
| |
| return SUCCESS; |
| } |
| |
| /* Special meanings for indices (which have a range of 0-7), which will fit into |
| a 4-bit integer. */ |
| |
| #define NEON_ALL_LANES 15 |
| #define NEON_INTERLEAVE_LANES 14 |
| |
| /* Record a use of the given feature. */ |
| static void |
| record_feature_use (const arm_feature_set *feature) |
| { |
| if (thumb_mode) |
| ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, *feature); |
| else |
| ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *feature); |
| } |
| |
| /* If the given feature available in the selected CPU, mark it as used. |
| Returns TRUE iff feature is available. */ |
| static bool |
| mark_feature_used (const arm_feature_set *feature) |
| { |
| |
| /* Do not support the use of MVE only instructions when in auto-detection or |
| -march=all. */ |
| if (((feature == &mve_ext) || (feature == &mve_fp_ext)) |
| && ARM_CPU_IS_ANY (cpu_variant)) |
| { |
| first_error (BAD_MVE_AUTO); |
| return false; |
| } |
| /* Ensure the option is valid on the current architecture. */ |
| if (!ARM_CPU_HAS_FEATURE (cpu_variant, *feature)) |
| return false; |
| |
| /* Add the appropriate architecture feature for the barrier option used. |
| */ |
| record_feature_use (feature); |
| |
| return true; |
| } |
| |
| /* Parse either a register or a scalar, with an optional type. Return the |
| register number, and optionally fill in the actual type of the register |
| when multiple alternatives were given (NEON_TYPE_NDQ) in *RTYPE, and |
| type/index information in *TYPEINFO. */ |
| |
| static int |
| parse_typed_reg_or_scalar (char **ccp, enum arm_reg_type type, |
| enum arm_reg_type *rtype, |
| struct neon_typed_alias *typeinfo) |
| { |
| char *str = *ccp; |
| struct reg_entry *reg = arm_reg_parse_multi (&str); |
| struct neon_typed_alias atype; |
| struct neon_type_el parsetype; |
| |
| atype.defined = 0; |
| atype.index = -1; |
| atype.eltype.type = NT_invtype; |
| atype.eltype.size = -1; |
| |
| /* Try alternate syntax for some types of register. Note these are mutually |
| exclusive with the Neon syntax extensions. */ |
| if (reg == NULL) |
| { |
| int altreg = arm_reg_alt_syntax (&str, *ccp, reg, type); |
| if (altreg != FAIL) |
| *ccp = str; |
| if (typeinfo) |
| *typeinfo = atype; |
| return altreg; |
| } |
| |
| /* Undo polymorphism when a set of register types may be accepted. */ |
| if ((type == REG_TYPE_NDQ |
| && (reg->type == REG_TYPE_NQ || reg->type == REG_TYPE_VFD)) |
| || (type == REG_TYPE_VFSD |
| && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD)) |
| || (type == REG_TYPE_NSDQ |
| && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD |
| || reg->type == REG_TYPE_NQ)) |
| || (type == REG_TYPE_NSD |
| && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD)) |
| || (type == REG_TYPE_MMXWC |
| && (reg->type == REG_TYPE_MMXWCG))) |
| type = (enum arm_reg_type) reg->type; |
| |
| if (type == REG_TYPE_MQ) |
| { |
| if (!ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext)) |
| return FAIL; |
| |
| if (!reg || reg->type != REG_TYPE_NQ) |
| return FAIL; |
| |
| if (reg->number > 14 && !mark_feature_used (&fpu_vfp_ext_d32)) |
| { |
| first_error (_("expected MVE register [q0..q7]")); |
| return FAIL; |
| } |
| type = REG_TYPE_NQ; |
| } |
| else if (ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext) |
| && (type == REG_TYPE_NQ)) |
| return FAIL; |
| |
| |
| if (type != reg->type) |
| return FAIL; |
| |
| if (reg->neon) |
| atype = *reg->neon; |
| |
| if (parse_neon_operand_type (&parsetype, &str) == SUCCESS) |
| { |
| if ((atype.defined & NTA_HASTYPE) != 0) |
| { |
| first_error (_("can't redefine type for operand")); |
| return FAIL; |
| } |
| atype.defined |= NTA_HASTYPE; |
| atype.eltype = parsetype; |
| } |
| |
| if (skip_past_char (&str, '[') == SUCCESS) |
| { |
| if (type != REG_TYPE_VFD |
| && !(type == REG_TYPE_VFS |
| && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8_2)) |
| && !(type == REG_TYPE_NQ |
| && ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext))) |
| { |
| if (ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext)) |
| first_error (_("only D and Q registers may be indexed")); |
| else |
| first_error (_("only D registers may be indexed")); |
| return FAIL; |
| } |
| |
| if ((atype.defined & NTA_HASINDEX) != 0) |
| { |
| first_error (_("can't change index for operand")); |
| return FAIL; |
| } |
| |
| atype.defined |= NTA_HASINDEX; |
| |
| if (skip_past_char (&str, ']') == SUCCESS) |
| atype.index = NEON_ALL_LANES; |
| else |
| { |
| expressionS exp; |
| |
| my_get_expression (&exp, &str, GE_NO_PREFIX); |
| |
| if (exp.X_op != O_constant) |
| { |
| first_error (_("constant expression required")); |
| return FAIL; |
| } |
| |
| if (skip_past_char (&str, ']') == FAIL) |
| return FAIL; |
| |
| atype.index = exp.X_add_number; |
| } |
| } |
| |
| if (typeinfo) |
| *typeinfo = atype; |
| |
| if (rtype) |
| *rtype = type; |
| |
| *ccp = str; |
| |
| return reg->number; |
| } |
| |
| /* Like arm_reg_parse, but also allow the following extra features: |
| - If RTYPE is non-zero, return the (possibly restricted) type of the |
| register (e.g. Neon double or quad reg when either has been requested). |
| - If this is a Neon vector type with additional type information, fill |
| in the struct pointed to by VECTYPE (if non-NULL). |
| This function will fault on encountering a scalar. */ |
| |
| static int |
| arm_typed_reg_parse (char **ccp, enum arm_reg_type type, |
| enum arm_reg_type *rtype, struct neon_type_el *vectype) |
| { |
| struct neon_typed_alias atype; |
| char *str = *ccp; |
| int reg = parse_typed_reg_or_scalar (&str, type, rtype, &atype); |
| |
| if (reg == FAIL) |
| return FAIL; |
| |
| /* Do not allow regname(... to parse as a register. */ |
| if (*str == '(') |
| return FAIL; |
| |
| /* Do not allow a scalar (reg+index) to parse as a register. */ |
| if ((atype.defined & NTA_HASINDEX) != 0) |
| { |
| first_error (_("register operand expected, but got scalar")); |
| return FAIL; |
| } |
| |
| if (vectype) |
| *vectype = atype.eltype; |
| |
| *ccp = str; |
| |
| return reg; |
| } |
| |
| #define NEON_SCALAR_REG(X) ((X) >> 4) |
| #define NEON_SCALAR_INDEX(X) ((X) & 15) |
| |
| /* Parse a Neon scalar. Most of the time when we're parsing a scalar, we don't |
| have enough information to be able to do a good job bounds-checking. So, we |
| just do easy checks here, and do further checks later. */ |
| |
| static int |
| parse_scalar (char **ccp, int elsize, struct neon_type_el *type, enum |
| arm_reg_type reg_type) |
| { |
| int reg; |
| char *str = *ccp; |
| struct neon_typed_alias atype; |
| unsigned reg_size; |
| |
| reg = parse_typed_reg_or_scalar (&str, reg_type, NULL, &atype); |
| |
| switch (reg_type) |
| { |
| case REG_TYPE_VFS: |
| reg_size = 32; |
| break; |
| case REG_TYPE_VFD: |
| reg_size = 64; |
| break; |
| case REG_TYPE_MQ: |
| reg_size = 128; |
| break; |
| default: |
| gas_assert (0); |
| return FAIL; |
| } |
| |
| if (reg == FAIL || (atype.defined & NTA_HASINDEX) == 0) |
| return FAIL; |
| |
| if (reg_type != REG_TYPE_MQ && atype.index == NEON_ALL_LANES) |
| { |
| first_error (_("scalar must have an index")); |
| return FAIL; |
| } |
| else if (atype.index >= reg_size / elsize) |
| { |
| first_error (_("scalar index out of range")); |
| return FAIL; |
| } |
| |
| if (type) |
| *type = atype.eltype; |
| |
| *ccp = str; |
| |
| return reg * 16 + atype.index; |
| } |
| |
| /* Types of registers in a list. */ |
| |
| enum reg_list_els |
| { |
| REGLIST_RN, |
| REGLIST_CLRM, |
| REGLIST_VFP_S, |
| REGLIST_VFP_S_VPR, |
| REGLIST_VFP_D, |
| REGLIST_VFP_D_VPR, |
| REGLIST_NEON_D |
| }; |
| |
| /* Parse an ARM register list. Returns the bitmask, or FAIL. */ |
| |
| static long |
| parse_reg_list (char ** strp, enum reg_list_els etype) |
| { |
| char *str = *strp; |
| long range = 0; |
| int another_range; |
| |
| gas_assert (etype == REGLIST_RN || etype == REGLIST_CLRM); |
| |
| /* We come back here if we get ranges concatenated by '+' or '|'. */ |
| do |
| { |
| skip_whitespace (str); |
| |
| another_range = 0; |
| |
| if (*str == '{') |
| { |
| int in_range = 0; |
| int cur_reg = -1; |
| |
| str++; |
| do |
| { |
| int reg; |
| const char apsr_str[] = "apsr"; |
| int apsr_str_len = strlen (apsr_str); |
| |
| reg = arm_reg_parse (&str, REG_TYPE_RN); |
| if (etype == REGLIST_CLRM) |
| { |
| if (reg == REG_SP || reg == REG_PC) |
| reg = FAIL; |
| else if (reg == FAIL |
| && !strncasecmp (str, apsr_str, apsr_str_len) |
| && !ISALPHA (*(str + apsr_str_len))) |
| { |
| reg = 15; |
| str += apsr_str_len; |
| } |
| |
| if (reg == FAIL) |
| { |
| first_error (_("r0-r12, lr or APSR expected")); |
| return FAIL; |
| } |
| } |
| else /* etype == REGLIST_RN. */ |
| { |
| if (reg == FAIL) |
| { |
| first_error (_(reg_expected_msgs[REGLIST_RN])); |
| return FAIL; |
| } |
| } |
| |
| if (in_range) |
| { |
| int i; |
| |
| if (reg <= cur_reg) |
| { |
| first_error (_("bad range in register list")); |
| return FAIL; |
| } |
| |
| for (i = cur_reg + 1; i < reg; i++) |
| { |
| if (range & (1 << i)) |
| as_tsktsk |
| (_("Warning: duplicated register (r%d) in register list"), |
| i); |
| else |
| range |= 1 << i; |
| } |
| in_range = 0; |
| } |
| |
| if (range & (1 << reg)) |
| as_tsktsk (_("Warning: duplicated register (r%d) in register list"), |
| reg); |
| else if (reg <= cur_reg) |
| as_tsktsk (_("Warning: register range not in ascending order")); |
| |
| range |= 1 << reg; |
| cur_reg = reg; |
| } |
| while (skip_past_comma (&str) != FAIL |
| || (in_range = 1, *str++ == '-')); |
| str--; |
| |
| if (skip_past_char (&str, '}') == FAIL) |
| { |
| first_error (_("missing `}'")); |
| return FAIL; |
| } |
| } |
| else if (etype == REGLIST_RN) |
| { |
| expressionS exp; |
| |
| if (my_get_expression (&exp, &str, GE_NO_PREFIX)) |
| return FAIL; |
| |
| if (exp.X_op == O_constant) |
| { |
| if (exp.X_add_number |
| != (exp.X_add_number & 0x0000ffff)) |
| { |
| inst.error = _("invalid register mask"); |
| return FAIL; |
| } |
| |
| if ((range & exp.X_add_number) != 0) |
| { |
| int regno = range & exp.X_add_number; |
| |
| regno &= -regno; |
| regno = (1 << regno) - 1; |
| as_tsktsk |
| (_("Warning: duplicated register (r%d) in register list"), |
| regno); |
| } |
| |
| range |= exp.X_add_number; |
| } |
| else |
| { |
| if (inst.relocs[0].type != 0) |
| { |
| inst.error = _("expression too complex"); |
| return FAIL; |
| } |
| |
| memcpy (&inst.relocs[0].exp, &exp, sizeof (expressionS)); |
| inst.relocs[0].type = BFD_RELOC_ARM_MULTI; |
| inst.relocs[0].pc_rel = 0; |
| } |
| } |
| |
| if (*str == '|' || *str == '+') |
| { |
| str++; |
| another_range = 1; |
| } |
| } |
| while (another_range); |
| |
| *strp = str; |
| return range; |
| } |
| |
| /* Parse a VFP register list. If the string is invalid return FAIL. |
| Otherwise return the number of registers, and set PBASE to the first |
| register. Parses registers of type ETYPE. |
| If REGLIST_NEON_D is used, several syntax enhancements are enabled: |
| - Q registers can be used to specify pairs of D registers |
| - { } can be omitted from around a singleton register list |
| FIXME: This is not implemented, as it would require backtracking in |
| some cases, e.g.: |
| vtbl.8 d3,d4,d5 |
| This could be done (the meaning isn't really ambiguous), but doesn't |
| fit in well with the current parsing framework. |
| - 32 D registers may be used (also true for VFPv3). |
| FIXME: Types are ignored in these register lists, which is probably a |
| bug. */ |
| |
| static int |
| parse_vfp_reg_list (char **ccp, unsigned int *pbase, enum reg_list_els etype, |
| bool *partial_match) |
| { |
| char *str = *ccp; |
| int base_reg; |
| int new_base; |
| enum arm_reg_type regtype = (enum arm_reg_type) 0; |
| int max_regs = 0; |
| int count = 0; |
| int warned = 0; |
| unsigned long mask = 0; |
| int i; |
| bool vpr_seen = false; |
| bool expect_vpr = |
| (etype == REGLIST_VFP_S_VPR) || (etype == REGLIST_VFP_D_VPR); |
| |
| if (skip_past_char (&str, '{') == FAIL) |
| { |
| inst.error = _("expecting {"); |
| return FAIL; |
| } |
| |
| switch (etype) |
| { |
| case REGLIST_VFP_S: |
| case REGLIST_VFP_S_VPR: |
| regtype = REG_TYPE_VFS; |
| max_regs = 32; |
| break; |
| |
| case REGLIST_VFP_D: |
| case REGLIST_VFP_D_VPR: |
| regtype = REG_TYPE_VFD; |
| break; |
| |
| case REGLIST_NEON_D: |
| regtype = REG_TYPE_NDQ; |
| break; |
| |
| default: |
| gas_assert (0); |
| } |
| |
| if (etype != REGLIST_VFP_S && etype != REGLIST_VFP_S_VPR) |
| { |
| /* VFPv3 allows 32 D registers, except for the VFPv3-D16 variant. */ |
| if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_d32)) |
| { |
| max_regs = 32; |
| if (thumb_mode) |
| ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, |
| fpu_vfp_ext_d32); |
| else |
| ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, |
| fpu_vfp_ext_d32); |
| } |
| else |
| max_regs = 16; |
| } |
| |
| base_reg = max_regs; |
| *partial_match = false; |
| |
| do |
| { |
| unsigned int setmask = 1, addregs = 1; |
| const char vpr_str[] = "vpr"; |
| size_t vpr_str_len = strlen (vpr_str); |
| |
| new_base = arm_typed_reg_parse (&str, regtype, ®type, NULL); |
| |
| if (expect_vpr) |
| { |
| if (new_base == FAIL |
| && !strncasecmp (str, vpr_str, vpr_str_len) |
| && !ISALPHA (*(str + vpr_str_len)) |
| && !vpr_seen) |
| { |
| vpr_seen = true; |
| str += vpr_str_len; |
| if (count == 0) |
| base_reg = 0; /* Canonicalize VPR only on d0 with 0 regs. */ |
| } |
| else if (vpr_seen) |
| { |
| first_error (_("VPR expected last")); |
| return FAIL; |
| } |
| else if (new_base == FAIL) |
| { |
| if (regtype == REG_TYPE_VFS) |
| first_error (_("VFP single precision register or VPR " |
| "expected")); |
| else /* regtype == REG_TYPE_VFD. */ |
| first_error (_("VFP/Neon double precision register or VPR " |
| "expected")); |
| return FAIL; |
| } |
| } |
| else if (new_base == FAIL) |
| { |
| first_error (_(reg_expected_msgs[regtype])); |
| return FAIL; |
| } |
| |
| *partial_match = true; |
| if (vpr_seen) |
| continue; |
| |
| if (new_base >= max_regs) |
| { |
| first_error (_("register out of range in list")); |
| return FAIL; |
| } |
| |
| /* Note: a value of 2 * n is returned for the register Q<n>. */ |
| if (regtype == REG_TYPE_NQ) |
| { |
| setmask = 3; |
| addregs = 2; |
| } |
| |
| if (new_base < base_reg) |
| base_reg = new_base; |
| |
| if (mask & (setmask << new_base)) |
| { |
| first_error (_("invalid register list")); |
| return FAIL; |
| } |
| |
| if ((mask >> new_base) != 0 && ! warned && !vpr_seen) |
| { |
| as_tsktsk (_("register list not in ascending order")); |
| warned = 1; |
| } |
| |
| mask |= setmask << new_base; |
| count += addregs; |
| |
| if (*str == '-') /* We have the start of a range expression */ |
| { |
| int high_range; |
| |
| str++; |
| |
| if ((high_range = arm_typed_reg_parse (&str, regtype, NULL, NULL)) |
| == FAIL) |
| { |
| inst.error = gettext (reg_expected_msgs[regtype]); |
| return FAIL; |
| } |
| |
| if (high_range >= max_regs) |
| { |
| first_error (_("register out of range in list")); |
| return FAIL; |
| } |
| |
| if (regtype == REG_TYPE_NQ) |
| high_range = high_range + 1; |
| |
| if (high_range <= new_base) |
| { |
| inst.error = _("register range not in ascending order"); |
| return FAIL; |
| } |
| |
| for (new_base += addregs; new_base <= high_range; new_base += addregs) |
| { |
| if (mask & (setmask << new_base)) |
| { |
| inst.error = _("invalid register list"); |
| return FAIL; |
| } |
| |
| mask |= setmask << new_base; |
| count += addregs; |
| } |
| } |
| } |
| while (skip_past_comma (&str) != FAIL); |
| |
| str++; |
| |
| /* Sanity check -- should have raised a parse error above. */ |
| if ((!vpr_seen && count == 0) || count > max_regs) |
| abort (); |
| |
| *pbase = base_reg; |
| |
| if (expect_vpr && !vpr_seen) |
| { |
| first_error (_("VPR expected last")); |
| return FAIL; |
| } |
| |
| /* Final test -- the registers must be consecutive. */ |
| mask >>= base_reg; |
| for (i = 0; i < count; i++) |
| { |
| if ((mask & (1u << i)) == 0) |
| { |
| inst.error = _("non-contiguous register range"); |
| return FAIL; |
| } |
| } |
| |
| *ccp = str; |
| |
| return count; |
| } |
| |
| /* True if two alias types are the same. */ |
| |
| static bool |
| neon_alias_types_same (struct neon_typed_alias *a, struct neon_typed_alias *b) |
| { |
| if (!a && !b) |
| return true; |
| |
| if (!a || !b) |
| return false; |
| |
| if (a->defined != b->defined) |
| return false; |
| |
| if ((a->defined & NTA_HASTYPE) != 0 |
| && (a->eltype.type != b->eltype.type |
| || a->eltype.size != b->eltype.size)) |
| return false; |
| |
| if ((a->defined & NTA_HASINDEX) != 0 |
| && (a->index != b->index)) |
| return false; |
| |
| return true; |
| } |
| |
| /* Parse element/structure lists for Neon VLD<n> and VST<n> instructions. |
| The base register is put in *PBASE. |
| The lane (or one of the NEON_*_LANES constants) is placed in bits [3:0] of |
| the return value. |
| The register stride (minus one) is put in bit 4 of the return value. |
| Bits [6:5] encode the list length (minus one). |
| The type of the list elements is put in *ELTYPE, if non-NULL. */ |
| |
| #define NEON_LANE(X) ((X) & 0xf) |
| #define NEON_REG_STRIDE(X) ((((X) >> 4) & 1) + 1) |
| #define NEON_REGLIST_LENGTH(X) ((((X) >> 5) & 3) + 1) |
| |
| static int |
| parse_neon_el_struct_list (char **str, unsigned *pbase, |
| int mve, |
| struct neon_type_el *eltype) |
| { |
| char *ptr = *str; |
| int base_reg = -1; |
| int reg_incr = -1; |
| int count = 0; |
| int lane = -1; |
| int leading_brace = 0; |
| enum arm_reg_type rtype = REG_TYPE_NDQ; |
| const char *const incr_error = mve ? _("register stride must be 1") : |
| _("register stride must be 1 or 2"); |
| const char *const type_error = _("mismatched element/structure types in list"); |
| struct neon_typed_alias firsttype; |
| firsttype.defined = 0; |
| firsttype.eltype.type = NT_invtype; |
| firsttype.eltype.size = -1; |
| firsttype.index = -1; |
| |
| if (skip_past_char (&ptr, '{') == SUCCESS) |
| leading_brace = 1; |
| |
| do |
| { |
| struct neon_typed_alias atype; |
| if (mve) |
| rtype = REG_TYPE_MQ; |
| int getreg = parse_typed_reg_or_scalar (&ptr, rtype, &rtype, &atype); |
| |
| if (getreg == FAIL) |
| { |
| first_error (_(reg_expected_msgs[rtype])); |
| return FAIL; |
| } |
| |
| if (base_reg == -1) |
| { |
| base_reg = getreg; |
| if (rtype == REG_TYPE_NQ) |
| { |
| reg_incr = 1; |
| } |
| firsttype = atype; |
| } |
| else if (reg_incr == -1) |
| { |
| reg_incr = getreg - base_reg; |
| if (reg_incr < 1 || reg_incr > 2) |
| { |
| first_error (_(incr_error)); |
| return FAIL; |
| } |
| } |
| else if (getreg != base_reg + reg_incr * count) |
| { |
| first_error (_(incr_error)); |
| return FAIL; |
| } |
| |
| if (! neon_alias_types_same (&atype, &firsttype)) |
| { |
| first_error (_(type_error)); |
| return FAIL; |
| } |
| |
| /* Handle Dn-Dm or Qn-Qm syntax. Can only be used with non-indexed list |
| modes. */ |
| if (ptr[0] == '-') |
| { |
| struct neon_typed_alias htype; |
| int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1; |
| if (lane == -1) |
| lane = NEON_INTERLEAVE_LANES; |
| else if (lane != NEON_INTERLEAVE_LANES) |
| { |
| first_error (_(type_error)); |
| return FAIL; |
| } |
| if (reg_incr == -1) |
| reg_incr = 1; |
| else if (reg_incr != 1) |
| { |
| first_error (_("don't use Rn-Rm syntax with non-unit stride")); |
| return FAIL; |
| } |
| ptr++; |
| hireg = parse_typed_reg_or_scalar (&ptr, rtype, NULL, &htype); |
| if (hireg == FAIL) |
| { |
| first_error (_(reg_expected_msgs[rtype])); |
| return FAIL; |
| } |
| if (! neon_alias_types_same (&htype, &firsttype)) |
| { |
| first_error (_(type_error)); |
| return FAIL; |
| } |
| count += hireg + dregs - getreg; |
| continue; |
| } |
| |
| /* If we're using Q registers, we can't use [] or [n] syntax. */ |
| if (rtype == REG_TYPE_NQ) |
| { |
| count += 2; |
| continue; |
| } |
| |
| if ((atype.defined & NTA_HASINDEX) != 0) |
| { |
| if (lane == -1) |
| lane = atype.index; |
| else if (lane != atype.index) |
| { |
| first_error (_(type_error)); |
| return FAIL; |
| } |
| } |
| else if (lane == -1) |
| lane = NEON_INTERLEAVE_LANES; |
| else if (lane != NEON_INTERLEAVE_LANES) |
| { |
| first_error (_(type_error)); |
| return FAIL; |
| } |
| count++; |
| } |
| while ((count != 1 || leading_brace) && skip_past_comma (&ptr) != FAIL); |
| |
| /* No lane set by [x]. We must be interleaving structures. */ |
| if (lane == -1) |
| lane = NEON_INTERLEAVE_LANES; |
| |
| /* Sanity check. */ |
| if (lane == -1 || base_reg == -1 || count < 1 || (!mve && count > 4) |
| || (count > 1 && reg_incr == -1)) |
| { |
| first_error (_("error parsing element/structure list")); |
| return FAIL; |
| } |
| |
| if ((count > 1 || leading_brace) && skip_past_char (&ptr, '}') == FAIL) |
| { |
| first_error (_("expected }")); |
| return FAIL; |
| } |
| |
| if (reg_incr == -1) |
| reg_incr = 1; |
| |
| if (eltype) |
| *eltype = firsttype.eltype; |
| |
| *pbase = base_reg; |
| *str = ptr; |
| |
| return lane | ((reg_incr - 1) << 4) | ((count - 1) << 5); |
| } |
| |
| /* Parse an explicit relocation suffix on an expression. This is |
| either nothing, or a word in parentheses. Note that if !OBJ_ELF, |
| arm_reloc_hsh contains no entries, so this function can only |
| succeed if there is no () after the word. Returns -1 on error, |
| BFD_RELOC_UNUSED if there wasn't any suffix. */ |
| |
| static int |
| parse_reloc (char **str) |
| { |
| struct reloc_entry *r; |
| char *p, *q; |
| |
| if (**str != '(') |
| return BFD_RELOC_UNUSED; |
| |
| p = *str + 1; |
| q = p; |
| |
| while (*q && *q != ')' && *q != ',') |
| q++; |
| if (*q != ')') |
| return -1; |
| |
| if ((r = (struct reloc_entry *) |
| str_hash_find_n (arm_reloc_hsh, p, q - p)) == NULL) |
| return -1; |
| |
| *str = q + 1; |
| return r->reloc; |
| } |
| |
| /* Directives: register aliases. */ |
| |
| static struct reg_entry * |
| insert_reg_alias (char *str, unsigned number, int type) |
| { |
| struct reg_entry *new_reg; |
| const char *name; |
| |
| if ((new_reg = (struct reg_entry *) str_hash_find (arm_reg_hsh, str)) != 0) |
| { |
| if (new_reg->builtin) |
| as_warn (_("ignoring attempt to redefine built-in register '%s'"), str); |
| |
| /* Only warn about a redefinition if it's not defined as the |
| same register. */ |
| else if (new_reg->number != number || new_reg->type != type) |
| as_warn (_("ignoring redefinition of register alias '%s'"), str); |
| |
| return NULL; |
| } |
| |
| name = xstrdup (str); |
| new_reg = XNEW (struct reg_entry); |
| |
| new_reg->name = name; |
| new_reg->number = number; |
| new_reg->type = type; |
| new_reg->builtin = false; |
| new_reg->neon = NULL; |
| |
| str_hash_insert (arm_reg_hsh, name, new_reg, 0); |
| |
| return new_reg; |
| } |
| |
| static void |
| insert_neon_reg_alias (char *str, int number, int type, |
| struct neon_typed_alias *atype) |
| { |
| struct reg_entry *reg = insert_reg_alias (str, number, type); |
| |
| if (!reg) |
| { |
| first_error (_("attempt to redefine typed alias")); |
| return; |
| } |
| |
| if (atype) |
| { |
| reg->neon = XNEW (struct neon_typed_alias); |
| *reg->neon = *atype; |
| } |
| } |
| |
| /* Look for the .req directive. This is of the form: |
| |
| new_register_name .req existing_register_name |
| |
| If we find one, or if it looks sufficiently like one that we want to |
| handle any error here, return TRUE. Otherwise return FALSE. */ |
| |
| static bool |
| create_register_alias (char * newname, char *p) |
| { |
| struct reg_entry *old; |
| char *oldname, *nbuf; |
| size_t nlen; |
| |
| /* The input scrubber ensures that whitespace after the mnemonic is |
| collapsed to single spaces. */ |
| oldname = p; |
| if (!startswith (oldname, " .req ")) |
| return false; |
| |
| oldname += 6; |
| if (*oldname == '\0') |
| return false; |
| |
| old = (struct reg_entry *) str_hash_find (arm_reg_hsh, oldname); |
| if (!old) |
| { |
| as_warn (_("unknown register '%s' -- .req ignored"), oldname); |
| return true; |
| } |
| |
| /* If TC_CASE_SENSITIVE is defined, then newname already points to |
| the desired alias name, and p points to its end. If not, then |
| the desired alias name is in the global original_case_string. */ |
| #ifdef TC_CASE_SENSITIVE |
| nlen = p - newname; |
| #else |
| newname = original_case_string; |
| nlen = strlen (newname); |
| #endif |
| |
| nbuf = xmemdup0 (newname, nlen); |
| |
| /* Create aliases under the new name as stated; an all-lowercase |
| version of the new name; and an all-uppercase version of the new |
| name. */ |
| if (insert_reg_alias (nbuf, old->number, old->type) != NULL) |
| { |
| for (p = nbuf; *p; p++) |
| *p = TOUPPER (*p); |
| |
| if (strncmp (nbuf, newname, nlen)) |
| { |
| /* If this attempt to create an additional alias fails, do not bother |
| trying to create the all-lower case alias. We will fail and issue |
| a second, duplicate error message. This situation arises when the |
| programmer does something like: |
| foo .req r0 |
| Foo .req r1 |
| The second .req creates the "Foo" alias but then fails to create |
| the artificial FOO alias because it has already been created by the |
| first .req. */ |
| if (insert_reg_alias (nbuf, old->number, old->type) == NULL) |
| { |
| free (nbuf); |
| return true; |
| } |
| } |
| |
| for (p = nbuf; *p; p++) |
| *p = TOLOWER (*p); |
| |
| if (strncmp (nbuf, newname, nlen)) |
| insert_reg_alias (nbuf, old->number, old->type); |
| } |
| |
| free (nbuf); |
| return true; |
| } |
| |
| /* Create a Neon typed/indexed register alias using directives, e.g.: |
| X .dn d5.s32[1] |
| Y .qn 6.s16 |
| Z .dn d7 |
| T .dn Z[0] |
| These typed registers can be used instead of the types specified after the |
| Neon mnemonic, so long as all operands given have types. Types can also be |
| specified directly, e.g.: |
| vadd d0.s32, d1.s32, d2.s32 */ |
| |
| static bool |
| create_neon_reg_alias (char *newname, char *p) |
| { |
| enum arm_reg_type basetype; |
| struct reg_entry *basereg; |
| struct reg_entry mybasereg; |
| struct neon_type ntype; |
| struct neon_typed_alias typeinfo; |
| char *namebuf, *nameend ATTRIBUTE_UNUSED; |
| int namelen; |
| |
| typeinfo.defined = 0; |
| typeinfo.eltype.type = NT_invtype; |
| typeinfo.eltype.size = -1; |
| typeinfo.index = -1; |
| |
| nameend = p; |
| |
| if (startswith (p, " .dn ")) |
| basetype = REG_TYPE_VFD; |
| else if (startswith (p, " .qn ")) |
| basetype = REG_TYPE_NQ; |
| else |
| return false; |
| |
| p += 5; |
| |
| if (*p == '\0') |
| return false; |
| |
| basereg = arm_reg_parse_multi (&p); |
| |
| if (basereg && basereg->type != basetype) |
| { |
| as_bad (_("bad type for register")); |
| return false; |
| } |
| |
| if (basereg == NULL) |
| { |
| expressionS exp; |
| /* Try parsing as an integer. */ |
| my_get_expression (&exp, &p, GE_NO_PREFIX); |
| if (exp.X_op != O_constant) |
| { |
| as_bad (_("expression must be constant")); |
| return false; |
| } |
| basereg = &mybasereg; |
| basereg->number = (basetype == REG_TYPE_NQ) ? exp.X_add_number * 2 |
| : exp.X_add_number; |
| basereg->neon = 0; |
| } |
| |
| if (basereg->neon) |
| typeinfo = *basereg->neon; |
| |
| if (parse_neon_type (&ntype, &p) == SUCCESS) |
| { |
| /* We got a type. */ |
| if (typeinfo.defined & NTA_HASTYPE) |
| { |
| as_bad (_("can't redefine the type of a register alias")); |
| return false; |
| } |
| |
| typeinfo.defined |= NTA_HASTYPE; |
| if (ntype.elems != 1) |
| { |
| as_bad (_("you must specify a single type only")); |
| return false; |
| } |
| typeinfo.eltype = ntype.el[0]; |
| } |
| |
| if (skip_past_char (&p, '[') == SUCCESS) |
| { |
| expressionS exp; |
| /* We got a scalar index. */ |
| |
| if (typeinfo.defined & NTA_HASINDEX) |
| { |
| as_bad (_("can't redefine the index of a scalar alias")); |
| return false; |
| } |
| |
| my_get_expression (&exp, &p, GE_NO_PREFIX); |
| |
| if (exp.X_op != O_constant) |
| { |
| as_bad (_("scalar index must be constant")); |
| return false; |
| } |
| |
| typeinfo.defined |= NTA_HASINDEX; |
| typeinfo.index = exp.X_add_number; |
| |
| if (skip_past_char (&p, ']') == FAIL) |
| { |
| as_bad (_("expecting ]")); |
| return false; |
| } |
| } |
| |
| /* If TC_CASE_SENSITIVE is defined, then newname already points to |
| the desired alias name, and p points to its end. If not, then |
| the desired alias name is in the global original_case_string. */ |
| #ifdef TC_CASE_SENSITIVE |
| namelen = nameend - newname; |
| #else |
| newname = original_case_string; |
| namelen = strlen (newname); |
| #endif |
| |
| namebuf = xmemdup0 (newname, namelen); |
| |
| insert_neon_reg_alias (namebuf, basereg->number, basetype, |
| typeinfo.defined != 0 ? &typeinfo : NULL); |
| |
| /* Insert name in all uppercase. */ |
| for (p = namebuf; *p; p++) |
| *p = TOUPPER (*p); |
| |
| if (strncmp (namebuf, newname, namelen)) |
| insert_neon_reg_alias (namebuf, basereg->number, basetype, |
| typeinfo.defined != 0 ? &typeinfo : NULL); |
| |
| /* Insert name in all lowercase. */ |
| for (p = namebuf; *p; p++) |
| *p = TOLOWER (*p); |
| |
| if (strncmp (namebuf, newname, namelen)) |
| insert_neon_reg_alias (namebuf, basereg->number, basetype, |
| typeinfo.defined != 0 ? &typeinfo : NULL); |
| |
| free (namebuf); |
| return true; |
| } |
| |
| /* Should never be called, as .req goes between the alias and the |
| register name, not at the beginning of the line. */ |
| |
| static void |
| s_req (int a ATTRIBUTE_UNUSED) |
| { |
| as_bad (_("invalid syntax for .req directive")); |
| } |
| |
| static void |
| s_dn (int a ATTRIBUTE_UNUSED) |
| { |
| as_bad (_("invalid syntax for .dn directive")); |
| } |
| |
| static void |
| s_qn (int a ATTRIBUTE_UNUSED) |
| { |
| as_bad (_("invalid syntax for .qn directive")); |
| } |
| |
| /* The .unreq directive deletes an alias which was previously defined |
| by .req. For example: |
| |
| my_alias .req r11 |
| .unreq my_alias */ |
| |
| static void |
| s_unreq (int a ATTRIBUTE_UNUSED) |
| { |
| char * name; |
| char saved_char; |
| |
| name = input_line_pointer; |
| |
| while (*input_line_pointer != 0 |
| && *input_line_pointer != ' ' |
| && *input_line_pointer != '\n') |
| ++input_line_pointer; |
| |
| saved_char = *input_line_pointer; |
| *input_line_pointer = 0; |
| |
| if (!*name) |
| as_bad (_("invalid syntax for .unreq directive")); |
| else |
| { |
| struct reg_entry *reg |
| = (struct reg_entry *) str_hash_find (arm_reg_hsh, name); |
| |
| if (!reg) |
| as_bad (_("unknown register alias '%s'"), name); |
| else if (reg->builtin) |
| as_warn (_("ignoring attempt to use .unreq on fixed register name: '%s'"), |
| name); |
| else |
| { |
| char * p; |
| char * nbuf; |
| |
| str_hash_delete (arm_reg_hsh, name); |
| free ((char *) reg->name); |
| free (reg->neon); |
| free (reg); |
| |
| /* Also locate the all upper case and all lower case versions. |
| Do not complain if we cannot find one or the other as it |
| was probably deleted above. */ |
| |
| nbuf = strdup (name); |
| for (p = nbuf; *p; p++) |
| *p = TOUPPER (*p); |
| reg = (struct reg_entry *) str_hash_find (arm_reg_hsh, nbuf); |
| if (reg) |
| { |
| str_hash_delete (arm_reg_hsh, nbuf); |
| free ((char *) reg->name); |
| free (reg->neon); |
| free (reg); |
| } |
| |
| for (p = nbuf; *p; p++) |
| *p = TOLOWER (*p); |
| reg = (struct reg_entry *) str_hash_find (arm_reg_hsh, nbuf); |
| if (reg) |
| { |
| str_hash_delete (arm_reg_hsh, nbuf); |
| free ((char *) reg->name); |
| free (reg->neon); |
| free (reg); |
| } |
| |
| free (nbuf); |
| } |
| } |
| |
| *input_line_pointer = saved_char; |
| demand_empty_rest_of_line (); |
| } |
| |
| /* Directives: Instruction set selection. */ |
| |
| #ifdef OBJ_ELF |
| /* This code is to handle mapping symbols as defined in the ARM ELF spec. |
| (See "Mapping symbols", section 4.5.5, ARM AAELF version 1.0). |
| Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag), |
| and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped. */ |
| |
| /* Create a new mapping symbol for the transition to STATE. */ |
| |
| static void |
| make_mapping_symbol (enum mstate state, valueT value, fragS *frag) |
| { |
| symbolS * symbolP; |
| const char * symname; |
| int type; |
| |
| switch (state) |
| { |
| case MAP_DATA: |
| symname = "$d"; |
| type = BSF_NO_FLAGS; |
| break; |
| case MAP_ARM: |
| symname = "$a"; |
| type = BSF_NO_FLAGS; |
| break; |
| case MAP_THUMB: |
| symname = "$t"; |
| type = BSF_NO_FLAGS; |
| break; |
| default: |
| abort (); |
| } |
| |
| symbolP = symbol_new (symname, now_seg, frag, value); |
| symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL; |
| |
| switch (state) |
| { |
| case MAP_ARM: |
| THUMB_SET_FUNC (symbolP, 0); |
| ARM_SET_THUMB (symbolP, 0); |
| ARM_SET_INTERWORK (symbolP, support_interwork); |
| break; |
| |
| case MAP_THUMB: |
| THUMB_SET_FUNC (symbolP, 1); |
| ARM_SET_THUMB (symbolP, 1); |
| ARM_SET_INTERWORK (symbolP, support_interwork); |
| break; |
| |
| case MAP_DATA: |
| default: |
| break; |
| } |
| |
| /* Save the mapping symbols for future reference. Also check that |
| we do not place two mapping symbols at the same offset within a |
| frag. We'll handle overlap between frags in |
| check_mapping_symbols. |
| |
| If .fill or other data filling directive generates zero sized data, |
| the mapping symbol for the following code will have the same value |
| as the one generated for the data filling directive. In this case, |
| we replace the old symbol with the new one at the same address. */ |
| if (value == 0) |
| { |
| if (frag->tc_frag_data.first_map != NULL) |
| { |
| know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0); |
| symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP, &symbol_lastP); |
| } |
| frag->tc_frag_data.first_map = symbolP; |
| } |
| if (frag->tc_frag_data.last_map != NULL) |
| { |
| know (S_GET_VALUE (frag->tc_frag_data.last_map) <= S_GET_VALUE (symbolP)); |
| if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP)) |
| symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP, &symbol_lastP); |
| } |
| frag->tc_frag_data.last_map = symbolP; |
| } |
| |
| /* We must sometimes convert a region marked as code to data during |
| code alignment, if an odd number of bytes have to be padded. The |
| code mapping symbol is pushed to an aligned address. */ |
| |
| static void |
| insert_data_mapping_symbol (enum mstate state, |
| valueT value, fragS *frag, offsetT bytes) |
| { |
| /* If there was already a mapping symbol, remove it. */ |
| if (frag->tc_frag_data.last_map != NULL |
| && S_GET_VALUE (frag->tc_frag_data.last_map) == frag->fr_address + value) |
| { |
| symbolS *symp = frag->tc_frag_data.last_map; |
| |
| if (value == 0) |
| { |
| know (frag->tc_frag_data.first_map == symp); |
| frag->tc_frag_data.first_map = NULL; |
| } |
| frag->tc_frag_data.last_map = NULL; |
| symbol_remove (symp, &symbol_rootP, &symbol_lastP); |
| } |
| |
| make_mapping_symbol (MAP_DATA, value, frag); |
| make_mapping_symbol (state, value + bytes, frag); |
| } |
| |
| static void mapping_state_2 (enum mstate state, int max_chars); |
| |
| /* Set the mapping state to STATE. Only call this when about to |
| emit some STATE bytes to the file. */ |
| |
| #define TRANSITION(from, to) (mapstate == (from) && state == (to)) |
| void |
| mapping_state (enum mstate state) |
| { |
| enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate; |
| |
| if (mapstate == state) |
| /* The mapping symbol has already been emitted. |
| There is nothing else to do. */ |
| return; |
| |
| if (state == MAP_ARM || state == MAP_THUMB) |
| /* PR gas/12931 |
| All ARM instructions require 4-byte alignment. |
| (Almost) all Thumb instructions require 2-byte alignment. |
| |
| When emitting instructions into any section, mark the section |
| appropriately. |
| |
| Some Thumb instructions are alignment-sensitive modulo 4 bytes, |
| but themselves require 2-byte alignment; this applies to some |
| PC- relative forms. However, these cases will involve implicit |
| literal pool generation or an explicit .align >=2, both of |
| which will cause the section to me marked with sufficient |
| alignment. Thus, we don't handle those cases here. */ |
| record_alignment (now_seg, state == MAP_ARM ? 2 : 1); |
| |
| if (TRANSITION (MAP_UNDEFINED, MAP_DATA)) |
| /* This case will be evaluated later. */ |
| return; |
| |
| mapping_state_2 (state, 0); |
| } |
| |
| /* Same as mapping_state, but MAX_CHARS bytes have already been |
| allocated. Put the mapping symbol that far back. */ |
| |
| static void |
| mapping_state_2 (enum mstate state, int max_chars) |
| { |
| enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate; |
| |
| if (!SEG_NORMAL (now_seg)) |
| return; |
| |
| if (mapstate == state) |
| /* The mapping symbol has already been emitted. |
| There is nothing else to do. */ |
| return; |
| |
| if (TRANSITION (MAP_UNDEFINED, MAP_ARM) |
| || TRANSITION (MAP_UNDEFINED, MAP_THUMB)) |
| { |
| struct frag * const frag_first = seg_info (now_seg)->frchainP->frch_root; |
| const int add_symbol = (frag_now != frag_first) || (frag_now_fix () > 0); |
| |
| if (add_symbol) |
| make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first); |
| } |
| |
| seg_info (now_seg)->tc_segment_info_data.mapstate = state; |
| make_mapping_symbol (state, (valueT) frag_now_fix () - max_chars, frag_now); |
| } |
| #undef TRANSITION |
| #else |
| #define mapping_state(x) ((void)0) |
| #define mapping_state_2(x, y) ((void)0) |
| #endif |
| |
| /* Find the real, Thumb encoded start of a Thumb function. */ |
| |
| #ifdef OBJ_COFF |
| static symbolS * |
| find_real_start (symbolS * symbolP) |
| { |
| char * real_start; |
| const char * name = S_GET_NAME (symbolP); |
| symbolS * new_target; |
| |
| /* This definition must agree with the one in gcc/config/arm/thumb.c. */ |
| #define STUB_NAME ".real_start_of" |
| |
| if (name == NULL) |
| abort (); |
| |
| /* The compiler may generate BL instructions to local labels because |
| it needs to perform a branch to a far away location. These labels |
| do not have a corresponding ".real_start_of" label. We check |
| both for S_IS_LOCAL and for a leading dot, to give a way to bypass |
| the ".real_start_of" convention for nonlocal branches. */ |
| if (S_IS_LOCAL (symbolP) || name[0] == '.') |
| return symbolP; |
| |
| real_start = concat (STUB_NAME, name, NULL); |
| new_target = symbol_find (real_start); |
| free (real_start); |
| |
| if (new_target == NULL) |
| { |
| as_warn (_("Failed to find real start of function: %s\n"), name); |
| new_target = symbolP; |
| } |
| |
| return new_target; |
| } |
| #endif |
| |
| static void |
| opcode_select (int width) |
| { |
| switch (width) |
| { |
| case 16: |
| if (! thumb_mode) |
| { |
| if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v4t)) |
| as_bad (_("selected processor does not support THUMB opcodes")); |
| |
| thumb_mode = 1; |
| /* No need to force the alignment, since we will have been |
| coming from ARM mode, which is word-aligned. */ |
| record_alignment (now_seg, 1); |
| } |
| break; |
| |
| case 32: |
| if (thumb_mode) |
| { |
| if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1)) |
| as_bad (_("selected processor does not support ARM opcodes")); |
| |
| thumb_mode = 0; |
| |
| if (!need_pass_2) |
| frag_align (2, 0, 0); |
| |
| record_alignment (now_seg, 1); |
| } |
| break; |
| |
| default: |
| as_bad (_("invalid instruction size selected (%d)"), width); |
| } |
| } |
| |
| static void |
| s_arm (int ignore ATTRIBUTE_UNUSED) |
| { |
| opcode_select (32); |
| demand_empty_rest_of_line (); |
| } |
| |
| static void |
| s_thumb (int ignore ATTRIBUTE_UNUSED) |
| { |
| opcode_select (16); |
| demand_empty_rest_of_line (); |
| } |
| |
| static void |
| s_code (int unused ATTRIBUTE_UNUSED) |
| { |
| int temp; |
| |
| temp = get_absolute_expression (); |
| switch (temp) |
| { |
| case 16: |
| case 32: |
| opcode_select (temp); |
| break; |
| |
| default: |
| as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp); |
| } |
| } |
| |
| static void |
| s_force_thumb (int ignore ATTRIBUTE_UNUSED) |
| { |
| /* If we are not already in thumb mode go into it, EVEN if |
| the target processor does not support thumb instructions. |
| This is used by gcc/config/arm/lib1funcs.asm for example |
| to compile interworking support functions even if the |
| target processor should not support interworking. */ |
| if (! thumb_mode) |
| { |
| thumb_mode = 2; |
| record_alignment (now_seg, 1); |
| } |
| |
| demand_empty_rest_of_line (); |
| } |
| |
| static void |
| s_thumb_func (int ignore ATTRIBUTE_UNUSED) |
| { |
| s_thumb (0); |
| |
| /* The following label is the name/address of the start of a Thumb function. |
| We need to know this for the interworking support. */ |
| label_is_thumb_function_name = true; |
| } |
| |
| /* Perform a .set directive, but also mark the alias as |
| being a thumb function. */ |
| |
| static void |
| s_thumb_set (int equiv) |
| { |
| /* XXX the following is a duplicate of the code for s_set() in read.c |
| We cannot just call that code as we need to get at the symbol that |
| is created. */ |
| char * name; |
| char delim; |
| char * end_name; |
| symbolS * symbolP; |
| |
| /* Especial apologies for the random logic: |
| This just grew, and could be parsed much more simply! |
| Dean - in haste. */ |
| delim = get_symbol_name (& name); |
| end_name = input_line_pointer; |
| (void) restore_line_pointer (delim); |
| |
| if (*input_line_pointer != ',') |
| { |
| *end_name = 0; |
| as_bad (_("expected comma after name \"%s\""), name); |
| *end_name = delim; |
| ignore_rest_of_line (); |
| return; |
| } |
| |
| input_line_pointer++; |
| *end_name = 0; |
| |
| if (name[0] == '.' && name[1] == '\0') |
| { |
| /* XXX - this should not happen to .thumb_set. */ |
| abort (); |
| } |
| |
| if ((symbolP = symbol_find (name)) == NULL |
| && (symbolP = md_undefined_symbol (name)) == NULL) |
| { |
| #ifndef NO_LISTING |
| /* When doing symbol listings, play games with dummy fragments living |
| outside the normal fragment chain to record the file and line info |
| for this symbol. */ |
| if (listing & LISTING_SYMBOLS) |
| { |
| extern struct list_info_struct * listing_tail; |
| fragS * dummy_frag = (fragS * ) xmalloc (sizeof (fragS)); |
| |
| memset (dummy_frag, 0, sizeof (fragS)); |
| dummy_frag->fr_type = rs_fill; |
| dummy_frag->line = listing_tail; |
| symbolP = symbol_new (name, undefined_section, dummy_frag, 0); |
| dummy_frag->fr_symbol = symbolP; |
| } |
| else |
| #endif |
| symbolP = symbol_new (name, undefined_section, &zero_address_frag, 0); |
| |
| #ifdef OBJ_COFF |
| /* "set" symbols are local unless otherwise specified. */ |
|