aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-arm.c
diff options
context:
space:
mode:
authorZack Weinberg <zackw@panix.com>2005-05-18 05:40:12 +0000
committerZack Weinberg <zackw@panix.com>2005-05-18 05:40:12 +0000
commitc19d120533277585363d82db26dec8f9ff603d0f (patch)
treed17b3c388d043d7edf85359239d7d5c53ac5a50d /gas/config/tc-arm.c
parent76ff342d252acc41e28986a6d6e5f96a8ce3adf5 (diff)
downloadgdb-c19d120533277585363d82db26dec8f9ff603d0f.zip
gdb-c19d120533277585363d82db26dec8f9ff603d0f.tar.gz
gdb-c19d120533277585363d82db26dec8f9ff603d0f.tar.bz2
include/elf:
* arm.h: Import complete list of official relocation names and numbers from AAELF. Define FAKE_RELOCs for old names. Remove a few old names no longer used anywhere. bfd: * elf32-arm.c: Wherever possible, use official reloc names from AAELF. (elf32_arm_howto_table, elf32_arm_tls_gd32_howto) (elf32_arm_tls_ldo32_howto, elf32_arm_tls_ldm32_howto) (elf32_arm_tls_le32_howto, elf32_arm_tls_ie32_howto) (elf32_arm_vtinherit_howto, elf32_arm_vtentry_howto) (elf32_arm_pc11_howto, elf32_arm_thm_pc9_howto, elf32_arm_got_prel) (elf32_arm_r_howto): Replace with elf32_arm_howto_table_1, elf32_arm_howto_table_2, and elf32_arm_howto_table_3. Add many new relocations from AAELF. (elf32_arm_howto_from_type): Update to match. (elf32_arm_reloc_map): Add entries for R_ARM_THM_JUMP24, R_ARM_THM_JUMP11, R_ARM_THM_JUMP19, R_ARM_THM_JUMP8, R_ARM_THM_JUMP6, R_ARM_GNU_VTINHERIT, and R_ARM_GNU_VTENTRY. (elf32_arm_reloc_type_lookup): Use elf32_arm_howto_from_type. (elf32_arm_final_link_relocate): Add support for R_ARM_THM_JUMP24, R_ARM_THM_JUMP19, R_ARM_THM_JUMP6. Remove case entries redundant with default. * reloc.c: Reorganize ARM relocations. Add Thumb assembler-internal relocations BFD_RELOC_ARM_T32_OFFSET_U8, BFD_RELOC_ARM_T32_OFFSET_IMM, BFD_RELOC_ARM_T32_IMMEDIATE. Add visible relocations BFD_RELOC_THUMB_PCREL_BRANCH7, BFD_RELOC_THUMB_BRANCH20, BFD_RELOC_THUMB_BRANCH25. Delete unused relocations BFD_RELOC_ARM_GOT12, BFD_RELOC_ARM_COPY. * bfd-in2.h, libbfd.h: Regenerate. opcodes: * arm-dis.c (thumb_opcodes): Add disassembly for V6T2 16-bit instructions. Adjust disassembly of some opcodes to match unified syntax. (thumb32_opcodes): New table. (print_insn_thumb): Rename print_insn_thumb16; don't handle two-halfword branches here. (print_insn_thumb32): New function. (print_insn): Choose among print_insn_arm, print_insn_thumb16, and print_insn_thumb32. Be consistent about order of halfwords when printing 32-bit instructions. gas: * hash.c (hash_lookup): Add len parameter. All callers changed. (hash_find_n): New interface. * hash.h: Prototype hash_find_n. * sb.c: Include as.h. (scrub_from_sb, sb_to_scrub, scrub_position): New statics. (sb_scrub_and_add_sb): New interface. * sb.h: Prototype sb_scrub_and_add_sb. * input-scrub.c (input_scrub_include_sb): Use sb_scrub_and_add_sb. * config/tc-arm.h (TC_FORCE_RELOCATION_LOCAL): Remove reference to BFD_RELOC_ARM_GOT12 which is never generated. * config/tc-arm.c: Rewrite, adding Thumb-2 support. gas/testsuite: * gas/arm/arm.exp: Convert all existing "gas_test" tests to "run_dump_test" tests. Run more tests unconditionally. Run new tests. * gas/arm/arch4t.s, gas/arm/arch6zk.s, gas/arm/arm3.s, gas/arm/arm6.s * gas/arm/arm7dm.s, gas/arm/bignum1.s, gas/arm/float.s * gas/arm/immed.s, gas/arm/iwmmxt.s, gas/arm/offset.s, gas/arm/thumb.s: Adjust to work as a dump test. * gas/arm/arch4t.d, gas/arm/arch6zk.d, gas/arm/arm3.d, gas/arm/arm6.d * gas/arm/arm7dm.d, gas/arm/bignum1.d, gas/arm/float.d * gas/arm/immed.d, gas/arm/iwmmxt.d, gas/arm/offset.d, gas/arm/thumb.d: New files. * gas/arm/armv1-bad.l, gas/arm/armv1-bad.s: Remove tests for diagnostics that don't happen in the first pass anymore. * gas/arm/iwmmxt-bad.l, gas/arm/r15-bad.l, gas/arm/req.l * gas/arm/vfp-bad.l: Update expected diagnostics. * gas/arm/pic.d: Update expected reloc name. * gas/arm/thumbv6.d: CPY no longer appears in disassembly. * gas/arm/r15-bad.s: Avoid two-argument mul. * gas/arm/req.s: Adjust comments. * gas/arm/maverick.d, gas/arm/maverick.s: Avoid inappropriate use of PC. * gas/arm/macro-1.d, gas/arm/macro1.s * gas/arm/t16-bad.l, gas/arm/t16-bad.s * gas/arm/tcompat.d, gas/arm/tcompat.s * gas/arm/tcompat2.d, gas/arm/tcompat2.s * gas/arm/thumb32.d, gas/arm/thumb32.s New test pair. ld/testsuite: * ld-arm/mixed-app.d: Adjust expected disassembly a little.
Diffstat (limited to 'gas/config/tc-arm.c')
-rw-r--r--gas/config/tc-arm.c19802
1 files changed, 8493 insertions, 11309 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index 4ca8d49..a0804f3 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -17,7 +17,7 @@
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
+ 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
@@ -26,7 +26,7 @@
02110-1301, USA. */
#include <string.h>
-#define NO_RELOC 0
+#define NO_RELOC 0
#include "as.h"
#include "safe-ctype.h"
@@ -56,31 +56,31 @@
static struct
{
- symbolS * proc_start;
- symbolS * table_entry;
- symbolS * personality_routine;
- int personality_index;
+ symbolS * proc_start;
+ symbolS * table_entry;
+ symbolS * personality_routine;
+ int personality_index;
/* The segment containing the function. */
- segT saved_seg;
- subsegT saved_subseg;
+ segT saved_seg;
+ subsegT saved_subseg;
/* Opcodes generated from this function. */
unsigned char * opcodes;
- int opcode_count;
- int opcode_alloc;
+ int opcode_count;
+ int opcode_alloc;
/* The number of bytes pushed to the stack. */
- offsetT frame_size;
+ 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;
+ 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;
+ 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;
+ unsigned fp_used:1;
/* Nonzero if the last opcode restores sp from fp_reg. */
- unsigned sp_restored:1;
+ unsigned sp_restored:1;
} unwind;
/* Bit N indicates that an R_ARM_NONE relocation has been output for
@@ -97,7 +97,7 @@ enum arm_float_abi
ARM_FLOAT_ABI_SOFT
};
-/* Types of processor to assemble for. */
+/* Types of processor to assemble for. */
#define ARM_1 ARM_ARCH_V1
#define ARM_2 ARM_ARCH_V2
#define ARM_3 ARM_ARCH_V2S
@@ -107,16 +107,16 @@ enum arm_float_abi
#define ARM_8 ARM_ARCH_V4
#define ARM_9 ARM_ARCH_V4T
#define ARM_STRONG ARM_ARCH_V4
-#define ARM_CPU_MASK 0x0000000f /* XXX? */
+#define ARM_CPU_MASK 0x0000000f /* XXX? */
#ifndef CPU_DEFAULT
#if defined __XSCALE__
#define CPU_DEFAULT (ARM_ARCH_XSCALE)
#else
#if defined __thumb__
-#define CPU_DEFAULT (ARM_ARCH_V5T)
+#define CPU_DEFAULT (ARM_ARCH_V5T)
#else
-#define CPU_DEFAULT ARM_ANY
+#define CPU_DEFAULT ARM_ANY
#endif
#endif
#endif
@@ -139,17 +139,16 @@ enum arm_float_abi
# endif
#endif /* ifndef FPU_DEFAULT */
-#define streq(a, b) (strcmp (a, b) == 0)
-#define skip_whitespace(str) while (*(str) == ' ') ++(str)
+#define streq(a, b) (strcmp (a, b) == 0)
static unsigned long cpu_variant;
/* Flags stored in private area of BFD structure. */
-static int uses_apcs_26 = FALSE;
-static int atpcs = FALSE;
+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 pic_code = 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
@@ -171,37 +170,8 @@ static int meabi_flags = EF_ARM_EABI_UNKNOWN;
# endif
#endif
-/* This array holds the chars that always start a comment. If the
- pre-processor is disabled, these aren't very useful. */
-const char 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[] = "#";
-
-const char 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[] = "rRsSfFdDxXeEpP";
-
-/* Prefix characters that indicate the start of an immediate
- value. */
-#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
-
#ifdef OBJ_ELF
-/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
+/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
symbolS * GOT_symbol;
#endif
@@ -214,84 +184,63 @@ const int md_reloc_size = 8;
instructions. */
static int thumb_mode = 0;
-typedef struct arm_fix
-{
- int thumb_mode;
-} arm_fix_data;
+/* 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 bfd_boolean unified_syntax = FALSE;
struct arm_it
{
- const char * error;
+ const char * error;
unsigned long instruction;
- int size;
+ int size;
+ int size_req;
+ int cond;
struct
{
bfd_reloc_code_real_type type;
- expressionS exp;
- int pc_rel;
+ expressionS exp;
+ int pc_rel;
} reloc;
-};
-
-struct arm_it inst;
-enum asm_shift_index
-{
- SHIFT_LSL = 0,
- SHIFT_LSR,
- SHIFT_ASR,
- SHIFT_ROR,
- SHIFT_RRX
-};
-
-struct asm_shift_properties
-{
- enum asm_shift_index index;
- unsigned long bit_field;
- unsigned int allows_0 : 1;
- unsigned int allows_32 : 1;
-};
-
-static const struct asm_shift_properties shift_properties [] =
-{
- { SHIFT_LSL, 0, 1, 0},
- { SHIFT_LSR, 0x20, 0, 1},
- { SHIFT_ASR, 0x40, 0, 1},
- { SHIFT_ROR, 0x60, 0, 0},
- { SHIFT_RRX, 0x60, 0, 0}
-};
-
-struct asm_shift_name
-{
- const char * name;
- const struct asm_shift_properties * properties;
-};
-
-static const struct asm_shift_name shift_names [] =
-{
- { "asl", shift_properties + SHIFT_LSL },
- { "lsl", shift_properties + SHIFT_LSL },
- { "lsr", shift_properties + SHIFT_LSR },
- { "asr", shift_properties + SHIFT_ASR },
- { "ror", shift_properties + SHIFT_ROR },
- { "rrx", shift_properties + SHIFT_RRX },
- { "ASL", shift_properties + SHIFT_LSL },
- { "LSL", shift_properties + SHIFT_LSL },
- { "LSR", shift_properties + SHIFT_LSR },
- { "ASR", shift_properties + SHIFT_ASR },
- { "ROR", shift_properties + SHIFT_ROR },
- { "RRX", shift_properties + SHIFT_RRX }
+ struct
+ {
+ unsigned reg;
+ unsigned imm;
+ unsigned present : 1; /* operand present */
+ unsigned isreg : 1; /* operand was a register */
+ unsigned immisreg : 1; /* .imm field is a second 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[6];
};
-/* Any kind of shift is accepted. */
-#define NO_SHIFT_RESTRICT 1
-/* The shift operand must be an immediate value, not a register. */
-#define SHIFT_IMMEDIATE 0
-/* The shift must be LSL or ASR and the operand must be an immediate. */
-#define SHIFT_LSL_OR_ASR_IMMEDIATE 2
-/* The shift must be ASR and the operand must be an immediate. */
-#define SHIFT_ASR_IMMEDIATE 3
-/* The shift must be LSL and the operand must be an immediate. */
-#define SHIFT_LSL_IMMEDIATE 4
+static struct arm_it inst;
#define NUM_FLOAT_VALS 8
@@ -300,7 +249,7 @@ const char * fp_const[] =
"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
};
-/* Number of littlenums required to hold an extended precision number. */
+/* Number of littlenums required to hold an extended precision number. */
#define MAX_LITTLENUMS 6
LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
@@ -308,246 +257,46 @@ LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
#define FAIL (-1)
#define SUCCESS (0)
-/* Whether a Co-processor load/store operation accepts write-back forms. */
-#define CP_WB_OK 1
-#define CP_NO_WB 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 CP_T_Pre 0x01000000
-#define CP_T_UD 0x00800000
-#define CP_T_WB 0x00200000
+#define CP_T_X 0x00008000
+#define CP_T_Y 0x00400000
-#define CONDS_BIT 0x00100000
-#define LOAD_BIT 0x00100000
+#define CONDS_BIT 0x00100000
+#define LOAD_BIT 0x00100000
#define DOUBLE_LOAD_FLAG 0x00000001
struct asm_cond
{
- const char * template;
+ const char * template;
unsigned long value;
};
-#define COND_ALWAYS 0xe0000000
-#define COND_MASK 0xf0000000
-
-static const struct asm_cond conds[] =
-{
- {"eq", 0x00000000},
- {"ne", 0x10000000},
- {"cs", 0x20000000}, {"hs", 0x20000000},
- {"cc", 0x30000000}, {"ul", 0x30000000}, {"lo", 0x30000000},
- {"mi", 0x40000000},
- {"pl", 0x50000000},
- {"vs", 0x60000000},
- {"vc", 0x70000000},
- {"hi", 0x80000000},
- {"ls", 0x90000000},
- {"ge", 0xa0000000},
- {"lt", 0xb0000000},
- {"gt", 0xc0000000},
- {"le", 0xd0000000},
- {"al", 0xe0000000},
- {"nv", 0xf0000000}
-};
+#define COND_ALWAYS 0xE
struct asm_psr
{
const char *template;
- bfd_boolean cpsr;
unsigned long field;
};
/* The bit that distinguishes CPSR and SPSR. */
#define SPSR_BIT (1 << 22)
-/* How many bits to shift the PSR_xxx bits up by. */
-#define PSR_SHIFT 16
+/* 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)
-#define PSR_c (1 << 0)
-#define PSR_x (1 << 1)
-#define PSR_s (1 << 2)
-#define PSR_f (1 << 3)
-
-static const struct asm_psr psrs[] =
-{
- {"CPSR", TRUE, PSR_c | PSR_f},
- {"CPSR_all", TRUE, PSR_c | PSR_f},
- {"SPSR", FALSE, PSR_c | PSR_f},
- {"SPSR_all", FALSE, PSR_c | PSR_f},
- {"CPSR_flg", TRUE, PSR_f},
- {"CPSR_f", TRUE, PSR_f},
- {"SPSR_flg", FALSE, PSR_f},
- {"SPSR_f", FALSE, PSR_f},
- {"CPSR_c", TRUE, PSR_c},
- {"CPSR_ctl", TRUE, PSR_c},
- {"SPSR_c", FALSE, PSR_c},
- {"SPSR_ctl", FALSE, PSR_c},
- {"CPSR_x", TRUE, PSR_x},
- {"CPSR_s", TRUE, PSR_s},
- {"SPSR_x", FALSE, PSR_x},
- {"SPSR_s", FALSE, PSR_s},
- /* Combinations of flags. */
- {"CPSR_fs", TRUE, PSR_f | PSR_s},
- {"CPSR_fx", TRUE, PSR_f | PSR_x},
- {"CPSR_fc", TRUE, PSR_f | PSR_c},
- {"CPSR_sf", TRUE, PSR_s | PSR_f},
- {"CPSR_sx", TRUE, PSR_s | PSR_x},
- {"CPSR_sc", TRUE, PSR_s | PSR_c},
- {"CPSR_xf", TRUE, PSR_x | PSR_f},
- {"CPSR_xs", TRUE, PSR_x | PSR_s},
- {"CPSR_xc", TRUE, PSR_x | PSR_c},
- {"CPSR_cf", TRUE, PSR_c | PSR_f},
- {"CPSR_cs", TRUE, PSR_c | PSR_s},
- {"CPSR_cx", TRUE, PSR_c | PSR_x},
- {"CPSR_fsx", TRUE, PSR_f | PSR_s | PSR_x},
- {"CPSR_fsc", TRUE, PSR_f | PSR_s | PSR_c},
- {"CPSR_fxs", TRUE, PSR_f | PSR_x | PSR_s},
- {"CPSR_fxc", TRUE, PSR_f | PSR_x | PSR_c},
- {"CPSR_fcs", TRUE, PSR_f | PSR_c | PSR_s},
- {"CPSR_fcx", TRUE, PSR_f | PSR_c | PSR_x},
- {"CPSR_sfx", TRUE, PSR_s | PSR_f | PSR_x},
- {"CPSR_sfc", TRUE, PSR_s | PSR_f | PSR_c},
- {"CPSR_sxf", TRUE, PSR_s | PSR_x | PSR_f},
- {"CPSR_sxc", TRUE, PSR_s | PSR_x | PSR_c},
- {"CPSR_scf", TRUE, PSR_s | PSR_c | PSR_f},
- {"CPSR_scx", TRUE, PSR_s | PSR_c | PSR_x},
- {"CPSR_xfs", TRUE, PSR_x | PSR_f | PSR_s},
- {"CPSR_xfc", TRUE, PSR_x | PSR_f | PSR_c},
- {"CPSR_xsf", TRUE, PSR_x | PSR_s | PSR_f},
- {"CPSR_xsc", TRUE, PSR_x | PSR_s | PSR_c},
- {"CPSR_xcf", TRUE, PSR_x | PSR_c | PSR_f},
- {"CPSR_xcs", TRUE, PSR_x | PSR_c | PSR_s},
- {"CPSR_cfs", TRUE, PSR_c | PSR_f | PSR_s},
- {"CPSR_cfx", TRUE, PSR_c | PSR_f | PSR_x},
- {"CPSR_csf", TRUE, PSR_c | PSR_s | PSR_f},
- {"CPSR_csx", TRUE, PSR_c | PSR_s | PSR_x},
- {"CPSR_cxf", TRUE, PSR_c | PSR_x | PSR_f},
- {"CPSR_cxs", TRUE, PSR_c | PSR_x | PSR_s},
- {"CPSR_fsxc", TRUE, PSR_f | PSR_s | PSR_x | PSR_c},
- {"CPSR_fscx", TRUE, PSR_f | PSR_s | PSR_c | PSR_x},
- {"CPSR_fxsc", TRUE, PSR_f | PSR_x | PSR_s | PSR_c},
- {"CPSR_fxcs", TRUE, PSR_f | PSR_x | PSR_c | PSR_s},
- {"CPSR_fcsx", TRUE, PSR_f | PSR_c | PSR_s | PSR_x},
- {"CPSR_fcxs", TRUE, PSR_f | PSR_c | PSR_x | PSR_s},
- {"CPSR_sfxc", TRUE, PSR_s | PSR_f | PSR_x | PSR_c},
- {"CPSR_sfcx", TRUE, PSR_s | PSR_f | PSR_c | PSR_x},
- {"CPSR_sxfc", TRUE, PSR_s | PSR_x | PSR_f | PSR_c},
- {"CPSR_sxcf", TRUE, PSR_s | PSR_x | PSR_c | PSR_f},
- {"CPSR_scfx", TRUE, PSR_s | PSR_c | PSR_f | PSR_x},
- {"CPSR_scxf", TRUE, PSR_s | PSR_c | PSR_x | PSR_f},
- {"CPSR_xfsc", TRUE, PSR_x | PSR_f | PSR_s | PSR_c},
- {"CPSR_xfcs", TRUE, PSR_x | PSR_f | PSR_c | PSR_s},
- {"CPSR_xsfc", TRUE, PSR_x | PSR_s | PSR_f | PSR_c},
- {"CPSR_xscf", TRUE, PSR_x | PSR_s | PSR_c | PSR_f},
- {"CPSR_xcfs", TRUE, PSR_x | PSR_c | PSR_f | PSR_s},
- {"CPSR_xcsf", TRUE, PSR_x | PSR_c | PSR_s | PSR_f},
- {"CPSR_cfsx", TRUE, PSR_c | PSR_f | PSR_s | PSR_x},
- {"CPSR_cfxs", TRUE, PSR_c | PSR_f | PSR_x | PSR_s},
- {"CPSR_csfx", TRUE, PSR_c | PSR_s | PSR_f | PSR_x},
- {"CPSR_csxf", TRUE, PSR_c | PSR_s | PSR_x | PSR_f},
- {"CPSR_cxfs", TRUE, PSR_c | PSR_x | PSR_f | PSR_s},
- {"CPSR_cxsf", TRUE, PSR_c | PSR_x | PSR_s | PSR_f},
- {"SPSR_fs", FALSE, PSR_f | PSR_s},
- {"SPSR_fx", FALSE, PSR_f | PSR_x},
- {"SPSR_fc", FALSE, PSR_f | PSR_c},
- {"SPSR_sf", FALSE, PSR_s | PSR_f},
- {"SPSR_sx", FALSE, PSR_s | PSR_x},
- {"SPSR_sc", FALSE, PSR_s | PSR_c},
- {"SPSR_xf", FALSE, PSR_x | PSR_f},
- {"SPSR_xs", FALSE, PSR_x | PSR_s},
- {"SPSR_xc", FALSE, PSR_x | PSR_c},
- {"SPSR_cf", FALSE, PSR_c | PSR_f},
- {"SPSR_cs", FALSE, PSR_c | PSR_s},
- {"SPSR_cx", FALSE, PSR_c | PSR_x},
- {"SPSR_fsx", FALSE, PSR_f | PSR_s | PSR_x},
- {"SPSR_fsc", FALSE, PSR_f | PSR_s | PSR_c},
- {"SPSR_fxs", FALSE, PSR_f | PSR_x | PSR_s},
- {"SPSR_fxc", FALSE, PSR_f | PSR_x | PSR_c},
- {"SPSR_fcs", FALSE, PSR_f | PSR_c | PSR_s},
- {"SPSR_fcx", FALSE, PSR_f | PSR_c | PSR_x},
- {"SPSR_sfx", FALSE, PSR_s | PSR_f | PSR_x},
- {"SPSR_sfc", FALSE, PSR_s | PSR_f | PSR_c},
- {"SPSR_sxf", FALSE, PSR_s | PSR_x | PSR_f},
- {"SPSR_sxc", FALSE, PSR_s | PSR_x | PSR_c},
- {"SPSR_scf", FALSE, PSR_s | PSR_c | PSR_f},
- {"SPSR_scx", FALSE, PSR_s | PSR_c | PSR_x},
- {"SPSR_xfs", FALSE, PSR_x | PSR_f | PSR_s},
- {"SPSR_xfc", FALSE, PSR_x | PSR_f | PSR_c},
- {"SPSR_xsf", FALSE, PSR_x | PSR_s | PSR_f},
- {"SPSR_xsc", FALSE, PSR_x | PSR_s | PSR_c},
- {"SPSR_xcf", FALSE, PSR_x | PSR_c | PSR_f},
- {"SPSR_xcs", FALSE, PSR_x | PSR_c | PSR_s},
- {"SPSR_cfs", FALSE, PSR_c | PSR_f | PSR_s},
- {"SPSR_cfx", FALSE, PSR_c | PSR_f | PSR_x},
- {"SPSR_csf", FALSE, PSR_c | PSR_s | PSR_f},
- {"SPSR_csx", FALSE, PSR_c | PSR_s | PSR_x},
- {"SPSR_cxf", FALSE, PSR_c | PSR_x | PSR_f},
- {"SPSR_cxs", FALSE, PSR_c | PSR_x | PSR_s},
- {"SPSR_fsxc", FALSE, PSR_f | PSR_s | PSR_x | PSR_c},
- {"SPSR_fscx", FALSE, PSR_f | PSR_s | PSR_c | PSR_x},
- {"SPSR_fxsc", FALSE, PSR_f | PSR_x | PSR_s | PSR_c},
- {"SPSR_fxcs", FALSE, PSR_f | PSR_x | PSR_c | PSR_s},
- {"SPSR_fcsx", FALSE, PSR_f | PSR_c | PSR_s | PSR_x},
- {"SPSR_fcxs", FALSE, PSR_f | PSR_c | PSR_x | PSR_s},
- {"SPSR_sfxc", FALSE, PSR_s | PSR_f | PSR_x | PSR_c},
- {"SPSR_sfcx", FALSE, PSR_s | PSR_f | PSR_c | PSR_x},
- {"SPSR_sxfc", FALSE, PSR_s | PSR_x | PSR_f | PSR_c},
- {"SPSR_sxcf", FALSE, PSR_s | PSR_x | PSR_c | PSR_f},
- {"SPSR_scfx", FALSE, PSR_s | PSR_c | PSR_f | PSR_x},
- {"SPSR_scxf", FALSE, PSR_s | PSR_c | PSR_x | PSR_f},
- {"SPSR_xfsc", FALSE, PSR_x | PSR_f | PSR_s | PSR_c},
- {"SPSR_xfcs", FALSE, PSR_x | PSR_f | PSR_c | PSR_s},
- {"SPSR_xsfc", FALSE, PSR_x | PSR_s | PSR_f | PSR_c},
- {"SPSR_xscf", FALSE, PSR_x | PSR_s | PSR_c | PSR_f},
- {"SPSR_xcfs", FALSE, PSR_x | PSR_c | PSR_f | PSR_s},
- {"SPSR_xcsf", FALSE, PSR_x | PSR_c | PSR_s | PSR_f},
- {"SPSR_cfsx", FALSE, PSR_c | PSR_f | PSR_s | PSR_x},
- {"SPSR_cfxs", FALSE, PSR_c | PSR_f | PSR_x | PSR_s},
- {"SPSR_csfx", FALSE, PSR_c | PSR_s | PSR_f | PSR_x},
- {"SPSR_csxf", FALSE, PSR_c | PSR_s | PSR_x | PSR_f},
- {"SPSR_cxfs", FALSE, PSR_c | PSR_x | PSR_f | PSR_s},
- {"SPSR_cxsf", FALSE, PSR_c | PSR_x | PSR_s | PSR_f},
-};
-
-enum wreg_type
- {
- IWMMXT_REG_WR = 0,
- IWMMXT_REG_WC = 1,
- IWMMXT_REG_WR_OR_WC = 2,
- IWMMXT_REG_WCG
- };
-
-enum iwmmxt_insn_type
-{
- check_rd,
- check_wr,
- check_wrwr,
- check_wrwrwr,
- check_wrwrwcg,
- check_tbcst,
- check_tmovmsk,
- check_tmia,
- check_tmcrr,
- check_tmrrc,
- check_tmcr,
- check_tmrc,
- check_tinsr,
- check_textrc,
- check_waligni,
- check_textrm,
- check_wshufh
-};
-
-enum vfp_dp_reg_pos
+struct reloc_entry
{
- VFP_REG_Dd, VFP_REG_Dm, VFP_REG_Dn
+ char *name;
+ bfd_reloc_code_real_type reloc;
};
enum vfp_sp_reg_pos
@@ -560,291 +309,101 @@ enum vfp_ldstm_type
VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
};
-/* VFP system registers. */
-struct vfp_reg
-{
- const char *name;
- unsigned long regno;
-};
-
-static const struct vfp_reg vfp_regs[] =
+/* ARM register categories. This includes coprocessor numbers and various
+ architecture extensions' registers. */
+enum arm_reg_type
{
- {"fpsid", 0x00000000},
- {"FPSID", 0x00000000},
- {"fpscr", 0x00010000},
- {"FPSCR", 0x00010000},
- {"fpexc", 0x00080000},
- {"FPEXC", 0x00080000}
+ REG_TYPE_RN,
+ REG_TYPE_CP,
+ REG_TYPE_CN,
+ REG_TYPE_FN,
+ REG_TYPE_VFS,
+ REG_TYPE_VFD,
+ REG_TYPE_VFC,
+ REG_TYPE_MVF,
+ REG_TYPE_MVD,
+ REG_TYPE_MVFX,
+ REG_TYPE_MVDX,
+ REG_TYPE_MVAX,
+ REG_TYPE_DSPSC,
+ REG_TYPE_MMXWR,
+ REG_TYPE_MMXWC,
+ REG_TYPE_MMXWCG,
+ REG_TYPE_XSCALE,
};
/* Structure for a hash table entry for a register. */
struct reg_entry
{
- const char * name;
- int number;
- bfd_boolean builtin;
-};
-
-/* Some well known registers that we refer to directly elsewhere. */
-#define REG_SP 13
-#define REG_LR 14
-#define REG_PC 15
-
-#define wr_register(reg) ((reg ^ WR_PREFIX) >= 0 && (reg ^ WR_PREFIX) <= 15)
-#define wc_register(reg) ((reg ^ WC_PREFIX) >= 0 && (reg ^ WC_PREFIX) <= 15)
-#define wcg_register(reg) ((reg ^ WC_PREFIX) >= 8 && (reg ^ WC_PREFIX) <= 11)
-
-/* These are the standard names. Users can add aliases with .req.
- and delete them with .unreq. */
-
-/* Integer Register Numbers. */
-static const struct reg_entry rn_table[] =
-{
- {"r0", 0, TRUE}, {"r1", 1, TRUE}, {"r2", 2, TRUE}, {"r3", 3, TRUE},
- {"r4", 4, TRUE}, {"r5", 5, TRUE}, {"r6", 6, TRUE}, {"r7", 7, TRUE},
- {"r8", 8, TRUE}, {"r9", 9, TRUE}, {"r10", 10, TRUE}, {"r11", 11, TRUE},
- {"r12", 12, TRUE}, {"r13", REG_SP, TRUE}, {"r14", REG_LR, TRUE}, {"r15", REG_PC, TRUE},
- /* ATPCS Synonyms. */
- {"a1", 0, TRUE}, {"a2", 1, TRUE}, {"a3", 2, TRUE}, {"a4", 3, TRUE},
- {"v1", 4, TRUE}, {"v2", 5, TRUE}, {"v3", 6, TRUE}, {"v4", 7, TRUE},
- {"v5", 8, TRUE}, {"v6", 9, TRUE}, {"v7", 10, TRUE}, {"v8", 11, TRUE},
- /* Well-known aliases. */
- {"wr", 7, TRUE}, {"sb", 9, TRUE}, {"sl", 10, TRUE}, {"fp", 11, TRUE},
- {"ip", 12, TRUE}, {"sp", REG_SP, TRUE}, {"lr", REG_LR, TRUE}, {"pc", REG_PC, TRUE},
- {NULL, 0, TRUE}
-};
-
-#define WR_PREFIX 0x200
-#define WC_PREFIX 0x400
-
-static const struct reg_entry iwmmxt_table[] =
-{
- /* Intel Wireless MMX technology register names. */
- { "wr0", 0x0 | WR_PREFIX, TRUE}, {"wr1", 0x1 | WR_PREFIX, TRUE},
- { "wr2", 0x2 | WR_PREFIX, TRUE}, {"wr3", 0x3 | WR_PREFIX, TRUE},
- { "wr4", 0x4 | WR_PREFIX, TRUE}, {"wr5", 0x5 | WR_PREFIX, TRUE},
- { "wr6", 0x6 | WR_PREFIX, TRUE}, {"wr7", 0x7 | WR_PREFIX, TRUE},
- { "wr8", 0x8 | WR_PREFIX, TRUE}, {"wr9", 0x9 | WR_PREFIX, TRUE},
- { "wr10", 0xa | WR_PREFIX, TRUE}, {"wr11", 0xb | WR_PREFIX, TRUE},
- { "wr12", 0xc | WR_PREFIX, TRUE}, {"wr13", 0xd | WR_PREFIX, TRUE},
- { "wr14", 0xe | WR_PREFIX, TRUE}, {"wr15", 0xf | WR_PREFIX, TRUE},
- { "wcid", 0x0 | WC_PREFIX, TRUE}, {"wcon", 0x1 | WC_PREFIX, TRUE},
- {"wcssf", 0x2 | WC_PREFIX, TRUE}, {"wcasf", 0x3 | WC_PREFIX, TRUE},
- {"wcgr0", 0x8 | WC_PREFIX, TRUE}, {"wcgr1", 0x9 | WC_PREFIX, TRUE},
- {"wcgr2", 0xa | WC_PREFIX, TRUE}, {"wcgr3", 0xb | WC_PREFIX, TRUE},
-
- { "wR0", 0x0 | WR_PREFIX, TRUE}, {"wR1", 0x1 | WR_PREFIX, TRUE},
- { "wR2", 0x2 | WR_PREFIX, TRUE}, {"wR3", 0x3 | WR_PREFIX, TRUE},
- { "wR4", 0x4 | WR_PREFIX, TRUE}, {"wR5", 0x5 | WR_PREFIX, TRUE},
- { "wR6", 0x6 | WR_PREFIX, TRUE}, {"wR7", 0x7 | WR_PREFIX, TRUE},
- { "wR8", 0x8 | WR_PREFIX, TRUE}, {"wR9", 0x9 | WR_PREFIX, TRUE},
- { "wR10", 0xa | WR_PREFIX, TRUE}, {"wR11", 0xb | WR_PREFIX, TRUE},
- { "wR12", 0xc | WR_PREFIX, TRUE}, {"wR13", 0xd | WR_PREFIX, TRUE},
- { "wR14", 0xe | WR_PREFIX, TRUE}, {"wR15", 0xf | WR_PREFIX, TRUE},
- { "wCID", 0x0 | WC_PREFIX, TRUE}, {"wCon", 0x1 | WC_PREFIX, TRUE},
- {"wCSSF", 0x2 | WC_PREFIX, TRUE}, {"wCASF", 0x3 | WC_PREFIX, TRUE},
- {"wCGR0", 0x8 | WC_PREFIX, TRUE}, {"wCGR1", 0x9 | WC_PREFIX, TRUE},
- {"wCGR2", 0xa | WC_PREFIX, TRUE}, {"wCGR3", 0xb | WC_PREFIX, TRUE},
- {NULL, 0, TRUE}
-};
-
-/* Co-processor Numbers. */
-static const struct reg_entry cp_table[] =
-{
- {"p0", 0, TRUE}, {"p1", 1, TRUE}, {"p2", 2, TRUE}, {"p3", 3, TRUE},
- {"p4", 4, TRUE}, {"p5", 5, TRUE}, {"p6", 6, TRUE}, {"p7", 7, TRUE},
- {"p8", 8, TRUE}, {"p9", 9, TRUE}, {"p10", 10, TRUE}, {"p11", 11, TRUE},
- {"p12", 12, TRUE}, {"p13", 13, TRUE}, {"p14", 14, TRUE}, {"p15", 15, TRUE},
- {NULL, 0, TRUE}
-};
-
-/* Co-processor Register Numbers. */
-static const struct reg_entry cn_table[] =
-{
- {"c0", 0, TRUE}, {"c1", 1, TRUE}, {"c2", 2, TRUE}, {"c3", 3, TRUE},
- {"c4", 4, TRUE}, {"c5", 5, TRUE}, {"c6", 6, TRUE}, {"c7", 7, TRUE},
- {"c8", 8, TRUE}, {"c9", 9, TRUE}, {"c10", 10, TRUE}, {"c11", 11, TRUE},
- {"c12", 12, TRUE}, {"c13", 13, TRUE}, {"c14", 14, TRUE}, {"c15", 15, TRUE},
- /* Not really valid, but kept for back-wards compatibility. */
- {"cr0", 0, TRUE}, {"cr1", 1, TRUE}, {"cr2", 2, TRUE}, {"cr3", 3, TRUE},
- {"cr4", 4, TRUE}, {"cr5", 5, TRUE}, {"cr6", 6, TRUE}, {"cr7", 7, TRUE},
- {"cr8", 8, TRUE}, {"cr9", 9, TRUE}, {"cr10", 10, TRUE}, {"cr11", 11, TRUE},
- {"cr12", 12, TRUE}, {"cr13", 13, TRUE}, {"cr14", 14, TRUE}, {"cr15", 15, TRUE},
- {NULL, 0, TRUE}
-};
-
-/* FPA Registers. */
-static const struct reg_entry fn_table[] =
-{
- {"f0", 0, TRUE}, {"f1", 1, TRUE}, {"f2", 2, TRUE}, {"f3", 3, TRUE},
- {"f4", 4, TRUE}, {"f5", 5, TRUE}, {"f6", 6, TRUE}, {"f7", 7, TRUE},
- {NULL, 0, TRUE}
-};
-
-/* VFP SP Registers. */
-static const struct reg_entry sn_table[] =
-{
- {"s0", 0, TRUE}, {"s1", 1, TRUE}, {"s2", 2, TRUE}, {"s3", 3, TRUE},
- {"s4", 4, TRUE}, {"s5", 5, TRUE}, {"s6", 6, TRUE}, {"s7", 7, TRUE},
- {"s8", 8, TRUE}, {"s9", 9, TRUE}, {"s10", 10, TRUE}, {"s11", 11, TRUE},
- {"s12", 12, TRUE}, {"s13", 13, TRUE}, {"s14", 14, TRUE}, {"s15", 15, TRUE},
- {"s16", 16, TRUE}, {"s17", 17, TRUE}, {"s18", 18, TRUE}, {"s19", 19, TRUE},
- {"s20", 20, TRUE}, {"s21", 21, TRUE}, {"s22", 22, TRUE}, {"s23", 23, TRUE},
- {"s24", 24, TRUE}, {"s25", 25, TRUE}, {"s26", 26, TRUE}, {"s27", 27, TRUE},
- {"s28", 28, TRUE}, {"s29", 29, TRUE}, {"s30", 30, TRUE}, {"s31", 31, TRUE},
- {NULL, 0, TRUE}
-};
-
-/* VFP DP Registers. */
-static const struct reg_entry dn_table[] =
-{
- {"d0", 0, TRUE}, {"d1", 1, TRUE}, {"d2", 2, TRUE}, {"d3", 3, TRUE},
- {"d4", 4, TRUE}, {"d5", 5, TRUE}, {"d6", 6, TRUE}, {"d7", 7, TRUE},
- {"d8", 8, TRUE}, {"d9", 9, TRUE}, {"d10", 10, TRUE}, {"d11", 11, TRUE},
- {"d12", 12, TRUE}, {"d13", 13, TRUE}, {"d14", 14, TRUE}, {"d15", 15, TRUE},
- {NULL, 0, TRUE}
-};
-
-/* Maverick DSP coprocessor registers. */
-static const struct reg_entry mav_mvf_table[] =
-{
- {"mvf0", 0, TRUE}, {"mvf1", 1, TRUE}, {"mvf2", 2, TRUE}, {"mvf3", 3, TRUE},
- {"mvf4", 4, TRUE}, {"mvf5", 5, TRUE}, {"mvf6", 6, TRUE}, {"mvf7", 7, TRUE},
- {"mvf8", 8, TRUE}, {"mvf9", 9, TRUE}, {"mvf10", 10, TRUE}, {"mvf11", 11, TRUE},
- {"mvf12", 12, TRUE}, {"mvf13", 13, TRUE}, {"mvf14", 14, TRUE}, {"mvf15", 15, TRUE},
- {NULL, 0, TRUE}
-};
-
-static const struct reg_entry mav_mvd_table[] =
-{
- {"mvd0", 0, TRUE}, {"mvd1", 1, TRUE}, {"mvd2", 2, TRUE}, {"mvd3", 3, TRUE},
- {"mvd4", 4, TRUE}, {"mvd5", 5, TRUE}, {"mvd6", 6, TRUE}, {"mvd7", 7, TRUE},
- {"mvd8", 8, TRUE}, {"mvd9", 9, TRUE}, {"mvd10", 10, TRUE}, {"mvd11", 11, TRUE},
- {"mvd12", 12, TRUE}, {"mvd13", 13, TRUE}, {"mvd14", 14, TRUE}, {"mvd15", 15, TRUE},
- {NULL, 0, TRUE}
+ const char *name;
+ unsigned char number;
+ unsigned char type;
+ unsigned char builtin;
};
-static const struct reg_entry mav_mvfx_table[] =
-{
- {"mvfx0", 0, TRUE}, {"mvfx1", 1, TRUE}, {"mvfx2", 2, TRUE}, {"mvfx3", 3, TRUE},
- {"mvfx4", 4, TRUE}, {"mvfx5", 5, TRUE}, {"mvfx6", 6, TRUE}, {"mvfx7", 7, TRUE},
- {"mvfx8", 8, TRUE}, {"mvfx9", 9, TRUE}, {"mvfx10", 10, TRUE}, {"mvfx11", 11, TRUE},
- {"mvfx12", 12, TRUE}, {"mvfx13", 13, TRUE}, {"mvfx14", 14, TRUE}, {"mvfx15", 15, TRUE},
- {NULL, 0, TRUE}
-};
-
-static const struct reg_entry mav_mvdx_table[] =
-{
- {"mvdx0", 0, TRUE}, {"mvdx1", 1, TRUE}, {"mvdx2", 2, TRUE}, {"mvdx3", 3, TRUE},
- {"mvdx4", 4, TRUE}, {"mvdx5", 5, TRUE}, {"mvdx6", 6, TRUE}, {"mvdx7", 7, TRUE},
- {"mvdx8", 8, TRUE}, {"mvdx9", 9, TRUE}, {"mvdx10", 10, TRUE}, {"mvdx11", 11, TRUE},
- {"mvdx12", 12, TRUE}, {"mvdx13", 13, TRUE}, {"mvdx14", 14, TRUE}, {"mvdx15", 15, TRUE},
- {NULL, 0, TRUE}
+/* Diagnostics used when we don't get a register of the expected type. */
+const char *const reg_expected_msgs[] =
+{
+ N_("ARM register expected"),
+ N_("bad or missing co-processor number"),
+ N_("co-processor register expected"),
+ N_("FPA register expected"),
+ N_("VFP single precision register expected"),
+ N_("VFP double precision register expected"),
+ N_("VFP system register expected"),
+ N_("Maverick MVF register expected"),
+ N_("Maverick MVD register expected"),
+ N_("Maverick MVFX register expected"),
+ N_("Maverick MVDX register expected"),
+ N_("Maverick MVAX register expected"),
+ N_("Maverick DSPSC register expected"),
+ N_("iWMMXt data register expected"),
+ N_("iWMMXt control register expected"),
+ N_("iWMMXt scalar register expected"),
+ N_("XScale accumulator register expected"),
};
-static const struct reg_entry mav_mvax_table[] =
-{
- {"mvax0", 0, TRUE}, {"mvax1", 1, TRUE}, {"mvax2", 2, TRUE}, {"mvax3", 3, TRUE},
- {NULL, 0, TRUE}
-};
-
-static const struct reg_entry mav_dspsc_table[] =
-{
- {"dspsc", 0, TRUE},
- {NULL, 0, TRUE}
-};
-
-struct reg_map
-{
- const struct reg_entry * names;
- int max_regno;
- struct hash_control * htab;
- const char * expected;
-};
-
-struct reg_map all_reg_maps[] =
-{
- {rn_table, 15, NULL, N_("ARM register expected")},
- {cp_table, 15, NULL, N_("bad or missing co-processor number")},
- {cn_table, 15, NULL, N_("co-processor register expected")},
- {fn_table, 7, NULL, N_("FPA register expected")},
- {sn_table, 31, NULL, N_("VFP single precision register expected")},
- {dn_table, 15, NULL, N_("VFP double precision register expected")},
- {mav_mvf_table, 15, NULL, N_("Maverick MVF register expected")},
- {mav_mvd_table, 15, NULL, N_("Maverick MVD register expected")},
- {mav_mvfx_table, 15, NULL, N_("Maverick MVFX register expected")},
- {mav_mvdx_table, 15, NULL, N_("Maverick MVDX register expected")},
- {mav_mvax_table, 3, NULL, N_("Maverick MVAX register expected")},
- {mav_dspsc_table, 0, NULL, N_("Maverick DSPSC register expected")},
- {iwmmxt_table, 23, NULL, N_("Intel Wireless MMX technology register expected")},
-};
-
-/* Enumeration matching entries in table above. */
-enum arm_reg_type
-{
- REG_TYPE_RN = 0,
-#define REG_TYPE_FIRST REG_TYPE_RN
- REG_TYPE_CP = 1,
- REG_TYPE_CN = 2,
- REG_TYPE_FN = 3,
- REG_TYPE_SN = 4,
- REG_TYPE_DN = 5,
- REG_TYPE_MVF = 6,
- REG_TYPE_MVD = 7,
- REG_TYPE_MVFX = 8,
- REG_TYPE_MVDX = 9,
- REG_TYPE_MVAX = 10,
- REG_TYPE_DSPSC = 11,
- REG_TYPE_IWMMXT = 12,
-
- REG_TYPE_MAX = 13
-};
+/* Some well known registers that we refer to directly elsewhere. */
+#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
-
-/* "INSN<cond> X,Y" where X:bit12, Y:bit16. */
-#define MAV_MODE1 0x100c
-
-/* "INSN<cond> X,Y" where X:bit16, Y:bit12. */
-#define MAV_MODE2 0x0c10
-
-/* "INSN<cond> X,Y" where X:bit12, Y:bit16. */
-#define MAV_MODE3 0x100c
-
-/* "INSN<cond> X,Y,Z" where X:16, Y:0, Z:12. */
-#define MAV_MODE4 0x0c0010
-
-/* "INSN<cond> X,Y,Z" where X:12, Y:16, Z:0. */
-#define MAV_MODE5 0x00100c
-
-/* "INSN<cond> W,X,Y,Z" where W:5, X:12, Y:16, Z:0. */
-#define MAV_MODE6 0x00100c05
+#define INSN_SIZE 4
struct asm_opcode
{
/* Basic string to match. */
- const char * template;
+ const char *template;
+
+ /* Parameters to instruction. */
+ unsigned char operands[8];
+
+ /* Conditional tag - see opcode_lookup. */
+ unsigned int tag : 4;
/* Basic instruction code. */
- unsigned long value;
+ unsigned int avalue : 28;
- /* Offset into the template where the condition code (if any) will be.
- If zero, then the instruction is never conditional. */
- unsigned cond_offset;
+ /* Thumb-format instruction code. */
+ unsigned int tvalue;
/* Which architecture variant provides this instruction. */
- unsigned long variant;
+ unsigned long avariant;
+ unsigned long tvariant;
+
+ /* Function to call to encode instruction in ARM format. */
+ void (* aencode) (void);
- /* Function to call to parse args. */
- void (* parms) (char *);
+ /* Function to call to encode instruction in Thumb format. */
+ void (* tencode) (void);
};
/* Defines for various bits that we will want to toggle. */
#define INST_IMMEDIATE 0x02000000
#define OFFSET_REG 0x02000000
-#define HWOFFSET_IMM 0x00400000
+#define HWOFFSET_IMM 0x00400000
#define SHIFT_BY_REG 0x00000010
#define PRE_INDEX 0x01000000
#define INDEX_UP 0x00800000
@@ -895,7 +454,8 @@ struct asm_opcode
#define T_OPCODE_ASR_R 0x4100
#define T_OPCODE_LSL_R 0x4080
-#define T_OPCODE_LSR_R 0x40c0
+#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
@@ -928,58 +488,22 @@ struct asm_opcode
#define T_OPCODE_BRANCH 0xe7fe
#define THUMB_SIZE 2 /* Size of thumb instruction. */
-#define THUMB_REG_LO 0x1
-#define THUMB_REG_HI 0x2
-#define THUMB_REG_ANY 0x3
-
-#define THUMB_H1 0x0080
-#define THUMB_H2 0x0040
-
-#define THUMB_ASR 0
-#define THUMB_LSL 1
-#define THUMB_LSR 2
-
-#define THUMB_MOVE 0
-#define THUMB_COMPARE 1
-#define THUMB_CPY 2
-
-#define THUMB_LOAD 0
-#define THUMB_STORE 1
-
#define THUMB_PP_PC_LR 0x0100
-
-/* These three are used for immediate shifts, do not alter. */
-#define THUMB_WORD 2
-#define THUMB_HALFWORD 1
-#define THUMB_BYTE 0
-
-struct thumb_opcode
-{
- /* Basic string to match. */
- const char * template;
-
- /* Basic instruction code. */
- unsigned long value;
-
- int size;
-
- /* Which CPU variants this exists for. */
- unsigned long variant;
-
- /* Function to call to parse args. */
- void (* parms) (char *);
-};
-
-#define BAD_ARGS _("bad arguments to instruction")
-#define BAD_PC _("r15 not allowed here")
-#define BAD_COND _("instruction is not conditional")
-#define ERR_NO_ACCUM _("acc0 expected")
-
-static struct hash_control * arm_ops_hsh = NULL;
-static struct hash_control * arm_tops_hsh = NULL;
-static struct hash_control * arm_cond_hsh = NULL;
-static struct hash_control * arm_shift_hsh = NULL;
-static struct hash_control * arm_psr_hsh = NULL;
+#define THUMB_LOAD_BIT 0x0800
+
+#define BAD_ARGS _("bad arguments to instruction")
+#define BAD_PC _("r15 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")
+
+static struct hash_control *arm_ops_hsh;
+static struct hash_control *arm_cond_hsh;
+static struct hash_control *arm_shift_hsh;
+static struct hash_control *arm_psr_hsh;
+static struct hash_control *arm_reg_hsh;
+static struct hash_control *arm_reloc_hsh;
/* Stuff needed to resolve the label ambiguity
As:
@@ -988,255 +512,822 @@ static struct hash_control * arm_psr_hsh = NULL;
may differ from:
...
label:
- <insn>
+ <insn>
*/
symbolS * last_label_seen;
static int label_is_thumb_function_name = FALSE;
-/* Literal Pool stuff. */
-
-#define MAX_LITERAL_POOL_SIZE 1024
-
/* 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;
+ expressionS literals [MAX_LITERAL_POOL_SIZE];
+ unsigned int next_free_entry;
+ unsigned int id;
+ symbolS * symbol;
+ segT section;
+ subsegT sub_section;
struct literal_pool * next;
} literal_pool;
/* Pointer to a linked list of literal pools. */
literal_pool * list_of_pools = NULL;
+
+/* Pure syntax. */
-static literal_pool *
-find_literal_pool (void)
+/* This array holds the chars that always start a comment. If the
+ pre-processor is disabled, these aren't very useful. */
+const char 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[] = "#";
+
+const char 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[] = "rRsSfFdDxXeEpP";
+
+/* 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)
+
+static inline int
+skip_past_char (char ** str, char c)
{
- literal_pool * pool;
+ if (**str == c)
+ {
+ (*str)++;
+ return SUCCESS;
+ }
+ else
+ return FAIL;
+}
+#define skip_past_comma(str) skip_past_char (str, ',')
- for (pool = list_of_pools; pool != NULL; pool = pool->next)
+/* Arithmetic expressions (possibly involving symbols). */
+
+/* Return TRUE if anything in the expression is a bignum. */
+
+static int
+walk_no_bignums (symbolS * sp)
+{
+ if (symbol_get_value_expression (sp)->X_op == O_big)
+ return 1;
+
+ if (symbol_get_value_expression (sp)->X_add_symbol)
{
- if (pool->section == now_seg
- && pool->sub_section == now_subseg)
- break;
+ 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 pool;
+ return 0;
}
-static literal_pool *
-find_or_make_literal_pool (void)
+static int in_my_get_expression = 0;
+
+/* Third argument to my_get_expression. */
+#define GE_NO_PREFIX 0
+#define GE_IMM_PREFIX 1
+#define GE_OPT_PREFIX 2
+
+static int
+my_get_expression (expressionS * ep, char ** str, int prefix_mode)
{
- /* Next literal pool ID number. */
- static unsigned int latest_pool_num = 1;
- literal_pool * pool;
+ char * save_in;
+ segT seg;
- pool = find_literal_pool ();
+ /* In unified syntax, all prefixes are optional. */
+ if (unified_syntax)
+ prefix_mode = GE_OPT_PREFIX;
- if (pool == NULL)
+ switch (prefix_mode)
{
- /* Create a new pool. */
- pool = xmalloc (sizeof (* pool));
- if (! pool)
- return NULL;
+ 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:
+ if (is_immediate_prefix (**str))
+ (*str)++;
+ break;
+ default: abort ();
+ }
- pool->next_free_entry = 0;
- pool->section = now_seg;
- pool->sub_section = now_subseg;
- pool->next = list_of_pools;
- pool->symbol = NULL;
+ memset (ep, 0, sizeof (expressionS));
- /* Add it to the list. */
- list_of_pools = pool;
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ in_my_get_expression = 1;
+ seg = expression (ep);
+ in_my_get_expression = 0;
+
+ if (ep->X_op == O_illegal)
+ {
+ /* We found a bad expression in md_operand(). */
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ if (inst.error == NULL)
+ inst.error = _("bad expression");
+ return 1;
}
- /* New pools, and emptied pools, will have a NULL symbol. */
- if (pool->symbol == NULL)
+#ifdef OBJ_AOUT
+ if (seg != absolute_section
+ && seg != text_section
+ && seg != data_section
+ && seg != bss_section
+ && seg != undefined_section)
{
- pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
- (valueT) 0, &zero_address_frag);
- pool->id = latest_pool_num ++;
+ inst.error = _("bad segment");
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return 1;
}
+#endif
- /* Done. */
- return pool;
+ /* 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 (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 0;
}
-/* Add the literal in the global 'inst'
- structure to the relevent literal pool. */
+/* 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.
-static int
-add_to_lit_pool (void)
-{
- literal_pool * pool;
- unsigned int entry;
+ 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.
- pool = find_or_make_literal_pool ();
+ ??? The format of 12 byte floats is uncertain according to gcc's arm.h. */
- /* Check if this literal value is already in the pool. */
- for (entry = 0; entry < pool->next_free_entry; entry ++)
+char *
+md_atof (int type, char * litP, int * sizeP)
+{
+ int prec;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char *t;
+ int i;
+
+ switch (type)
{
- if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
- && (inst.reloc.exp.X_op == O_constant)
- && (pool->literals[entry].X_add_number
- == inst.reloc.exp.X_add_number)
- && (pool->literals[entry].X_unsigned
- == inst.reloc.exp.X_unsigned))
- break;
+ case 'f':
+ case 'F':
+ case 's':
+ case 'S':
+ prec = 2;
+ break;
- if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
- && (inst.reloc.exp.X_op == O_symbol)
- && (pool->literals[entry].X_add_number
- == inst.reloc.exp.X_add_number)
- && (pool->literals[entry].X_add_symbol
- == inst.reloc.exp.X_add_symbol)
- && (pool->literals[entry].X_op_symbol
- == inst.reloc.exp.X_op_symbol))
- break;
+ case 'd':
+ case 'D':
+ case 'r':
+ case 'R':
+ prec = 4;
+ break;
+
+ case 'x':
+ case 'X':
+ prec = 6;
+ break;
+
+ case 'p':
+ case 'P':
+ prec = 6;
+ break;
+
+ default:
+ *sizeP = 0;
+ return _("bad call to MD_ATOF()");
}
- /* Do we need to create a new entry? */
- if (entry == pool->next_free_entry)
+ t = atof_ieee (input_line_pointer, type, words);
+ if (t)
+ input_line_pointer = t;
+ *sizeP = prec * 2;
+
+ if (target_big_endian)
{
- if (entry >= MAX_LITERAL_POOL_SIZE)
+ for (i = 0; i < prec; i++)
{
- inst.error = _("literal pool overflow");
- return FAIL;
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
}
-
- pool->literals[entry] = inst.reloc.exp;
- pool->next_free_entry += 1;
+ }
+ else
+ {
+ if (cpu_variant & FPU_ARCH_VFP)
+ for (i = prec - 1; i >= 0; i--)
+ {
+ md_number_to_chars (litP, (valueT) words[i], 2);
+ litP += 2;
+ }
+ 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], 2);
+ md_number_to_chars (litP + 2, (valueT) words[i], 2);
+ litP += 4;
+ }
}
- inst.reloc.exp.X_op = O_symbol;
- inst.reloc.exp.X_add_number = ((int) entry) * 4 - 8;
- inst.reloc.exp.X_add_symbol = pool->symbol;
+ return 0;
+}
- return SUCCESS;
+/* We handle all bad expressions here, so that we can report the faulty
+ instruction in the error message. */
+void
+md_operand (expressionS * expr)
+{
+ if (in_my_get_expression)
+ expr->X_op = O_illegal;
}
-/* Can't use symbol_new here, so have to create a symbol and then at
- a later date assign it a value. Thats what these functions do. */
+/* Immediate values. */
-static void
-symbol_locate (symbolS * symbolP,
- const char * name, /* It is copied, the caller can modify. */
- segT segment, /* Segment identifier (SEG_<something>). */
- valueT valu, /* Symbol value. */
- fragS * frag) /* Associated fragment. */
+/* Generic immediate-value read function for use in directives.
+ Accepts anything that 'expression' can fold to a constant.
+ *val receives the number. */
+#ifdef OBJ_ELF
+static int
+immediate_for_directive (int *val)
{
- unsigned int name_length;
- char * preserved_copy_of_name;
+ expressionS exp;
+ exp.X_op = O_illegal;
- name_length = strlen (name) + 1; /* +1 for \0. */
- obstack_grow (&notes, name, name_length);
- preserved_copy_of_name = obstack_finish (&notes);
+ if (is_immediate_prefix (*input_line_pointer))
+ {
+ input_line_pointer++;
+ expression (&exp);
+ }
-#ifdef tc_canonicalize_symbol_name
- preserved_copy_of_name =
- tc_canonicalize_symbol_name (preserved_copy_of_name);
+ if (exp.X_op != O_constant)
+ {
+ as_bad (_("expected #constant"));
+ ignore_rest_of_line ();
+ return FAIL;
+ }
+ *val = exp.X_add_number;
+ return SUCCESS;
+}
#endif
- S_SET_NAME (symbolP, preserved_copy_of_name);
+/* Register parsing. */
- S_SET_SEGMENT (symbolP, segment);
- S_SET_VALUE (symbolP, valu);
- symbol_clear_list_pointers (symbolP);
+/* 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. */
- symbol_set_frag (symbolP, frag);
+static struct reg_entry *
+arm_reg_parse_multi (char **ccp)
+{
+ char *start = *ccp;
+ char *p;
+ struct reg_entry *reg;
- /* Link to end of symbol chain. */
- {
- extern int symbol_table_frozen;
+#ifdef REGISTER_PREFIX
+ if (*start != REGISTER_PREFIX)
+ return FAIL;
+ start++;
+#endif
+#ifdef OPTIONAL_REGISTER_PREFIX
+ if (*start == OPTIONAL_REGISTER_PREFIX)
+ start++;
+#endif
- if (symbol_table_frozen)
- abort ();
- }
+ p = start;
+ if (!ISALPHA (*p) || !is_name_beginner (*p))
+ return NULL;
- symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
+ do
+ p++;
+ while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
- obj_symbol_new_hook (symbolP);
+ reg = (struct reg_entry *) hash_find_n (arm_reg_hsh, start, p - start);
-#ifdef tc_symbol_new_hook
- tc_symbol_new_hook (symbolP);
-#endif
+ if (!reg)
+ return NULL;
-#ifdef DEBUG_SYMS
- verify_symbol_chain (symbol_rootP, symbol_lastP);
-#endif /* DEBUG_SYMS */
+ *ccp = p;
+ return reg;
}
-/* Check that an immediate is valid.
- If so, convert it to the right format. */
+/* As above, but the register must be of type TYPE, and the return
+ value is the register number or NULL. */
-static unsigned int
-validate_immediate (unsigned int val)
+static int
+arm_reg_parse (char **ccp, enum arm_reg_type type)
{
- unsigned int a;
- unsigned int i;
+ char *start = *ccp;
+ struct reg_entry *reg = arm_reg_parse_multi (ccp);
-#define rotate_left(v, n) (v << n | v >> (32 - n))
+ if (reg && reg->type == type)
+ return reg->number;
- for (i = 0; i < 32; i += 2)
- if ((a = rotate_left (val, i)) <= 0xff)
- return a | (i << 7); /* 12-bit pack: [shift-cnt,const]. */
+ /* 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->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;
+ }
+
+ case REG_TYPE_MMXWC:
+ /* WC includes WCG. ??? I'm not sure this is true for all
+ instructions that take WC registers. */
+ if (reg->type == REG_TYPE_MMXWCG)
+ return reg->number;
+ break;
+
+ default:
+ break;
+ }
+ *ccp = start;
return FAIL;
}
-/* Check to see if an immediate can be computed as two separate immediate
- values, added together. We already know that this value cannot be
- computed by just one ARM instruction. */
-
-static unsigned int
-validate_immediate_twopart (unsigned int val,
- unsigned int * highpart)
+/* Parse an ARM register list. Returns the bitmask, or FAIL. */
+static long
+parse_reg_list (char ** strp)
{
- unsigned int a;
- unsigned int i;
+ char * str = * strp;
+ long range = 0;
+ int another_range;
- for (i = 0; i < 32; i += 2)
- if (((a = rotate_left (val, i)) & 0xff) != 0)
- {
- if (a & 0xff00)
- {
- if (a & ~ 0xffff)
- continue;
- * highpart = (a >> 8) | ((i + 24) << 7);
- }
- else if (a & 0xff0000)
- {
- if (a & 0xff000000)
- continue;
- * highpart = (a >> 16) | ((i + 16) << 7);
- }
- else
- {
- assert (a & 0xff000000);
- * highpart = (a >> 24) | ((i + 8) << 7);
- }
+ /* We come back here if we get ranges concatenated by '+' or '|'. */
+ do
+ {
+ another_range = 0;
- return (a & 0xff) | (i << 7);
- }
+ if (*str == '{')
+ {
+ int in_range = 0;
+ int cur_reg = -1;
- return FAIL;
+ str++;
+ do
+ {
+ int reg;
+
+ if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
+ {
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return FAIL;
+ }
+
+ if (in_range)
+ {
+ int i;
+
+ if (reg <= cur_reg)
+ {
+ inst.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 (*str++ != '}')
+ {
+ inst.error = _("missing `}'");
+ return FAIL;
+ }
+ }
+ else
+ {
+ expressionS expr;
+
+ if (my_get_expression (&expr, &str, GE_NO_PREFIX))
+ return FAIL;
+
+ if (expr.X_op == O_constant)
+ {
+ if (expr.X_add_number
+ != (expr.X_add_number & 0x0000ffff))
+ {
+ inst.error = _("invalid register mask");
+ return FAIL;
+ }
+
+ if ((range & expr.X_add_number) != 0)
+ {
+ int regno = range & expr.X_add_number;
+
+ regno &= -regno;
+ regno = (1 << regno) - 1;
+ as_tsktsk
+ (_("Warning: duplicated register (r%d) in register list"),
+ regno);
+ }
+
+ range |= expr.X_add_number;
+ }
+ else
+ {
+ if (inst.reloc.type != 0)
+ {
+ inst.error = _("expression too complex");
+ return FAIL;
+ }
+
+ memcpy (&inst.reloc.exp, &expr, sizeof (expressionS));
+ inst.reloc.type = BFD_RELOC_ARM_MULTI;
+ inst.reloc.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. Double precision registers are matched if DP is nonzero. */
+
static int
-validate_offset_imm (unsigned int val, int hwse)
+parse_vfp_reg_list (char **str, int *pbase, int dp)
{
- if ((hwse && val > 255) || val > 4095)
+ int base_reg;
+ int new_base;
+ int regtype;
+ int max_regs;
+ int count = 0;
+ int warned = 0;
+ unsigned long mask = 0;
+ int i;
+
+ if (**str != '{')
return FAIL;
- return val;
+
+ (*str)++;
+
+ if (dp)
+ {
+ regtype = REG_TYPE_VFD;
+ max_regs = 16;
+ }
+ else
+ {
+ regtype = REG_TYPE_VFS;
+ max_regs = 32;
+ }
+
+ base_reg = max_regs;
+
+ do
+ {
+ new_base = arm_reg_parse (str, regtype);
+ if (new_base == FAIL)
+ {
+ inst.error = gettext (reg_expected_msgs[regtype]);
+ return FAIL;
+ }
+
+ if (new_base < base_reg)
+ base_reg = new_base;
+
+ if (mask & (1 << new_base))
+ {
+ inst.error = _("invalid register list");
+ return FAIL;
+ }
+
+ if ((mask >> new_base) != 0 && ! warned)
+ {
+ as_tsktsk (_("register list not in ascending order"));
+ warned = 1;
+ }
+
+ mask |= 1 << new_base;
+ count++;
+
+ if (**str == '-') /* We have the start of a range expression */
+ {
+ int high_range;
+
+ (*str)++;
+
+ if ((high_range = arm_reg_parse (str, regtype)) == FAIL)
+ {
+ inst.error = gettext (reg_expected_msgs[regtype]);
+ return FAIL;
+ }
+
+ if (high_range <= new_base)
+ {
+ inst.error = _("register range not in ascending order");
+ return FAIL;
+ }
+
+ for (new_base++; new_base <= high_range; new_base++)
+ {
+ if (mask & (1 << new_base))
+ {
+ inst.error = _("invalid register list");
+ return FAIL;
+ }
+
+ mask |= 1 << new_base;
+ count++;
+ }
+ }
+ }
+ while (skip_past_comma (str) != FAIL);
+
+ (*str)++;
+
+ /* Sanity check -- should have raised a parse error above. */
+ if (count == 0 || count > max_regs)
+ abort ();
+
+ *pbase = base_reg;
+
+ /* 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;
+ }
+ }
+
+ return count;
}
-
+/* 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 = hash_find_n (arm_reloc_hsh, p, q - p)) == NULL)
+ return -1;
+
+ *str = q + 1;
+ return r->reloc;
+}
+
+/* Directives: register aliases. */
+
+static void
+insert_reg_alias (char *str, int number, int type)
+{
+ struct reg_entry *new;
+ const char *name;
+
+ if ((new = hash_find (arm_reg_hsh, str)) != 0)
+ {
+ if (new->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->number != number || new->type != type)
+ as_warn (_("ignoring redefinition of register alias '%s'"), str);
+
+ return;
+ }
+
+ name = xstrdup (str);
+ new = xmalloc (sizeof (struct reg_entry));
+
+ new->name = name;
+ new->number = number;
+ new->type = type;
+ new->builtin = FALSE;
+
+ if (hash_insert (arm_reg_hsh, name, (PTR) new))
+ abort ();
+}
+
+/* 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 non-zero. Otherwise return zero. */
+
+static int
+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 (strncmp (oldname, " .req ", 6) != 0)
+ return 0;
+
+ oldname += 6;
+ if (*oldname == '\0')
+ return 0;
+
+ old = hash_find (arm_reg_hsh, oldname);
+ if (!old)
+ {
+ as_warn (_("unknown register '%s' -- .req ignored"), oldname);
+ return 1;
+ }
+
+ /* 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 = alloca (nlen + 1);
+ memcpy (nbuf, newname, nlen);
+ nbuf[nlen] = '\0';
+
+ /* 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. */
+ insert_reg_alias (nbuf, old->number, old->type);
+
+ for (p = nbuf; *p; p++)
+ *p = TOUPPER (*p);
+
+ if (strncmp (nbuf, newname, nlen))
+ insert_reg_alias (nbuf, old->number, old->type);
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+
+ if (strncmp (nbuf, newname, nlen))
+ insert_reg_alias (nbuf, old->number, old->type);
+
+ return 1;
+}
+
+/* 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"));
+}
+
+/* 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 = hash_find (arm_reg_hsh, name);
+
+ if (!reg)
+ as_bad (_("unknown register alias '%s'"), name);
+ else if (reg->builtin)
+ as_warn (_("ignoring attempt to undefine built-in register '%s'"),
+ name);
+ else
+ {
+ hash_delete (arm_reg_hsh, name);
+ free ((char *) reg->name);
+ free (reg);
+ }
+ }
+
+ *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).
@@ -1304,102 +1395,45 @@ mapping_state (enum mstate state)
return;
}
}
+#else
+#define mapping_state(x) /* nothing */
+#endif
-/* When we change sections we need to issue a new mapping symbol. */
-
-void
-arm_elf_change_section (void)
-{
- flagword flags;
- segment_info_type *seginfo;
-
- /* Link an unlinked unwind index table section to the .text section. */
- if (elf_section_type (now_seg) == SHT_ARM_EXIDX
- && elf_linked_to_section (now_seg) == NULL)
- elf_linked_to_section (now_seg) = text_section;
-
- if (!SEG_NORMAL (now_seg))
- return;
-
- flags = bfd_get_section_flags (stdoutput, now_seg);
-
- /* We can ignore sections that only contain debug info. */
- if ((flags & SEC_ALLOC) == 0)
- return;
-
- seginfo = seg_info (now_seg);
- mapstate = seginfo->tc_segment_info_data.mapstate;
- marked_pr_dependency = seginfo->tc_segment_info_data.marked_pr_dependency;
-}
+/* Find the real, Thumb encoded start of a Thumb function. */
-int
-arm_elf_section_type (const char * str, size_t len)
+static symbolS *
+find_real_start (symbolS * symbolP)
{
- if (len == 5 && strncmp (str, "exidx", 5) == 0)
- return SHT_ARM_EXIDX;
+ char * real_start;
+ const char * name = S_GET_NAME (symbolP);
+ symbolS * new_target;
- return -1;
-}
-#else
-#define mapping_state(a)
-#endif /* OBJ_ELF */
-
-/* arm_reg_parse () := if it looks like a register, return its token and
- advance the pointer. */
+ /* This definition must agree with the one in gcc/config/arm/thumb.c. */
+#define STUB_NAME ".real_start_of"
-static int
-arm_reg_parse (char ** ccp, struct hash_control * htab)
-{
- char * start = * ccp;
- char c;
- char * p;
- struct reg_entry * reg;
+ if (name == NULL)
+ abort ();
-#ifdef REGISTER_PREFIX
- if (*start != REGISTER_PREFIX)
- return FAIL;
- p = start + 1;
-#else
- p = start;
-#ifdef OPTIONAL_REGISTER_PREFIX
- if (*p == OPTIONAL_REGISTER_PREFIX)
- p++, start++;
-#endif
-#endif
- if (!ISALPHA (*p) || !is_name_beginner (*p))
- return FAIL;
+ /* Names that start with '.' are local labels, not function entry points.
+ The compiler may generate BL instructions to these labels because it
+ needs to perform a branch to a far away location. */
+ if (name[0] == '.')
+ return symbolP;
- c = *p++;
- while (ISALPHA (c) || ISDIGIT (c) || c == '_')
- c = *p++;
+ real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1);
+ sprintf (real_start, "%s%s", STUB_NAME, name);
- *--p = 0;
- reg = (struct reg_entry *) hash_find (htab, start);
- *p = c;
+ new_target = symbol_find (real_start);
- if (reg)
+ if (new_target == NULL)
{
- *ccp = p;
- return reg->number;
+ as_warn ("Failed to find real start of function: %s\n", name);
+ new_target = symbolP;
}
- return FAIL;
-}
-
-/* Search for the following register name in each of the possible reg name
- tables. Return the classification if found, or REG_TYPE_MAX if not
- present. */
-
-static enum arm_reg_type
-arm_reg_parse_any (char *cp)
-{
- int i;
-
- for (i = (int) REG_TYPE_FIRST; i < (int) REG_TYPE_MAX; i++)
- if (arm_reg_parse (&cp, all_reg_maps[i].htab) != FAIL)
- return (enum arm_reg_type) i;
+ free (real_start);
- return REG_TYPE_MAX;
+ return new_target;
}
static void
@@ -1415,7 +1449,7 @@ opcode_select (int width)
thumb_mode = 1;
/* No need to force the alignment, since we will have been
- coming from ARM mode, which is word-aligned. */
+ coming from ARM mode, which is word-aligned. */
record_alignment (now_seg, 1);
}
mapping_state (MAP_THUMB);
@@ -1443,182 +1477,35 @@ opcode_select (int width)
}
static void
-s_req (int a ATTRIBUTE_UNUSED)
-{
- as_bad (_("invalid syntax for .req 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;
-
- skip_whitespace (input_line_pointer);
- 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)
- {
- enum arm_reg_type req_type = arm_reg_parse_any (name);
-
- if (req_type != REG_TYPE_MAX)
- {
- char *temp_name = name;
- int req_no = arm_reg_parse (&temp_name, all_reg_maps[req_type].htab);
-
- if (req_no != FAIL)
- {
- struct reg_entry *req_entry;
-
- /* Check to see if this alias is a builtin one. */
- req_entry = hash_delete (all_reg_maps[req_type].htab, name);
-
- if (!req_entry)
- as_bad (_("unreq: missing hash entry for \"%s\""), name);
- else if (req_entry->builtin)
- /* FIXME: We are deleting a built in register alias which
- points to a const data structure, so we only need to
- free up the memory used by the key in the hash table.
- Unfortunately we have not recorded this value, so this
- is a memory leak. */
- /* FIXME: Should we issue a warning message ? */
- ;
- else
- {
- /* Deleting a user defined alias. We need to free the
- key and the value, but fortunately the key is the same
- as the value->name field. */
- free ((char *) req_entry->name);
- free (req_entry);
- }
- }
- else
- as_bad (_(".unreq: unrecognized symbol \"%s\""), name);
- }
- else
- as_bad (_(".unreq: unrecognized symbol \"%s\""), name);
- }
- else
- as_bad (_("invalid syntax for .unreq directive"));
-
- *input_line_pointer = saved_char;
- demand_empty_rest_of_line ();
-}
-
-static void
-s_bss (int ignore ATTRIBUTE_UNUSED)
+s_arm (int ignore ATTRIBUTE_UNUSED)
{
- /* We don't support putting frags in the BSS segment, we fake it by
- marking in_bss, then looking at s_skip for clues. */
- subseg_set (bss_section, 0);
+ opcode_select (32);
demand_empty_rest_of_line ();
- mapping_state (MAP_DATA);
}
static void
-s_even (int ignore ATTRIBUTE_UNUSED)
+s_thumb (int ignore ATTRIBUTE_UNUSED)
{
- /* Never make frag if expect extra pass. */
- if (!need_pass_2)
- frag_align (1, 0, 0);
-
- record_alignment (now_seg, 1);
-
+ opcode_select (16);
demand_empty_rest_of_line ();
}
static void
-s_ltorg (int ignored ATTRIBUTE_UNUSED)
-{
- unsigned int entry;
- literal_pool * pool;
- char sym_name[20];
-
- pool = find_literal_pool ();
- if (pool == NULL
- || pool->symbol == NULL
- || pool->next_free_entry == 0)
- return;
-
- mapping_state (MAP_DATA);
-
- /* Align pool as you have word accesses.
- Only make a frag if we have to. */
- if (!need_pass_2)
- frag_align (2, 0, 0);
-
- record_alignment (now_seg, 2);
-
- sprintf (sym_name, "$$lit_\002%x", pool->id);
-
- symbol_locate (pool->symbol, sym_name, now_seg,
- (valueT) frag_now_fix (), frag_now);
- symbol_table_insert (pool->symbol);
-
- ARM_SET_THUMB (pool->symbol, thumb_mode);
-
-#if defined OBJ_COFF || defined OBJ_ELF
- ARM_SET_INTERWORK (pool->symbol, support_interwork);
-#endif
-
- for (entry = 0; entry < pool->next_free_entry; entry ++)
- /* First output the expression in the instruction to the pool. */
- emit_expr (&(pool->literals[entry]), 4); /* .word */
-
- /* Mark the pool as empty. */
- pool->next_free_entry = 0;
- pool->symbol = NULL;
-}
-
-/* Same as s_align_ptwo but align 0 => align 2. */
-
-static void
-s_align (int unused ATTRIBUTE_UNUSED)
+s_code (int unused ATTRIBUTE_UNUSED)
{
int temp;
- long temp_fill;
- long max_alignment = 15;
temp = get_absolute_expression ();
- if (temp > max_alignment)
- as_bad (_("alignment too large: %d assumed"), temp = max_alignment);
- else if (temp < 0)
+ switch (temp)
{
- as_bad (_("alignment negative. 0 assumed."));
- temp = 0;
- }
+ case 16:
+ case 32:
+ opcode_select (temp);
+ break;
- if (*input_line_pointer == ',')
- {
- input_line_pointer++;
- temp_fill = get_absolute_expression ();
+ default:
+ as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
}
- else
- temp_fill = 0;
-
- if (!temp)
- temp = 2;
-
- /* Only make a frag if we HAVE to. */
- if (temp && !need_pass_2)
- frag_align (temp, (int) temp_fill, 0);
- demand_empty_rest_of_line ();
-
- record_alignment (now_seg, temp);
}
static void
@@ -1628,11 +1515,10 @@ s_force_thumb (int ignore ATTRIBUTE_UNUSED)
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. */
+ target processor should not support interworking. */
if (! thumb_mode)
{
thumb_mode = 2;
-
record_alignment (now_seg, 1);
}
@@ -1642,14 +1528,11 @@ s_force_thumb (int ignore ATTRIBUTE_UNUSED)
static void
s_thumb_func (int ignore ATTRIBUTE_UNUSED)
{
- if (! thumb_mode)
- opcode_select (16);
+ 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. */
+ We need to know this for the interworking support. */
label_is_thumb_function_name = TRUE;
-
- demand_empty_rest_of_line ();
}
/* Perform a .set directive, but also mark the alias as
@@ -1662,20 +1545,18 @@ s_thumb_set (int equiv)
We cannot just call that code as we need to get at the symbol that
is created. */
char * name;
- char delim;
+ 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. */
- name = input_line_pointer;
- delim = get_symbol_end ();
+ name = input_line_pointer;
+ delim = get_symbol_end ();
end_name = input_line_pointer;
*end_name = delim;
- SKIP_WHITESPACE ();
-
if (*input_line_pointer != ',')
{
*end_name = 0;
@@ -1700,7 +1581,7 @@ s_thumb_set (int equiv)
#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. */
+ for this symbol. */
if (listing & LISTING_SYMBOLS)
{
extern struct list_info_struct * listing_tail;
@@ -1735,7 +1616,7 @@ s_thumb_set (int equiv)
demand_empty_rest_of_line ();
- /* XXX Now we come to the Thumb specific bit of code. */
+ /* XXX Now we come to the Thumb specific bit of code. */
THUMB_SET_FUNC (symbolP, 1);
ARM_SET_THUMB (symbolP, 1);
@@ -1744,4055 +1625,2996 @@ s_thumb_set (int equiv)
#endif
}
-static void
-s_arm (int ignore ATTRIBUTE_UNUSED)
-{
- opcode_select (32);
- demand_empty_rest_of_line ();
-}
+/* Directives: Mode selection. */
+/* .syntax [unified|divided] - choose the new unified syntax
+ (same for Arm and Thumb encoding, modulo slight differences in what
+ can be represented) or the old divergent syntax for each mode. */
static void
-s_thumb (int ignore ATTRIBUTE_UNUSED)
+s_syntax (int unused ATTRIBUTE_UNUSED)
{
- opcode_select (16);
+ char *name, delim;
+
+ name = input_line_pointer;
+ delim = get_symbol_end ();
+
+ if (!strcasecmp (name, "unified"))
+ unified_syntax = TRUE;
+ else if (!strcasecmp (name, "divided"))
+ unified_syntax = FALSE;
+ else
+ {
+ as_bad (_("unrecognized syntax mode \"%s\""), name);
+ return;
+ }
+ *input_line_pointer = delim;
demand_empty_rest_of_line ();
}
+/* Directives: sectioning and alignment. */
+
+/* Same as s_align_ptwo but align 0 => align 2. */
+
static void
-s_code (int unused ATTRIBUTE_UNUSED)
+s_align (int unused ATTRIBUTE_UNUSED)
{
int temp;
+ long temp_fill;
+ long max_alignment = 15;
temp = get_absolute_expression ();
- switch (temp)
+ if (temp > max_alignment)
+ as_bad (_("alignment too large: %d assumed"), temp = max_alignment);
+ else if (temp < 0)
{
- case 16:
- case 32:
- opcode_select (temp);
- break;
-
- default:
- as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
+ as_bad (_("alignment negative. 0 assumed."));
+ temp = 0;
}
-}
-
-static void
-end_of_line (char * str)
-{
- skip_whitespace (str);
- if (*str != '\0' && !inst.error)
- inst.error = _("garbage following instruction");
-}
-
-static int
-skip_past_comma (char ** str)
-{
- char * p = * str, c;
- int comma = 0;
-
- while ((c = *p) == ' ' || c == ',')
+ if (*input_line_pointer == ',')
{
- p++;
- if (c == ',' && comma++)
- return FAIL;
+ input_line_pointer++;
+ temp_fill = get_absolute_expression ();
}
+ else
+ temp_fill = 0;
- if (c == '\0')
- return FAIL;
+ if (!temp)
+ temp = 2;
- *str = p;
- return comma ? SUCCESS : FAIL;
-}
+ /* Only make a frag if we HAVE to. */
+ if (temp && !need_pass_2)
+ frag_align (temp, (int) temp_fill, 0);
+ demand_empty_rest_of_line ();
-/* Return TRUE if anything in the expression is a bignum. */
+ record_alignment (now_seg, temp);
+}
-static int
-walk_no_bignums (symbolS * sp)
+static void
+s_bss (int ignore ATTRIBUTE_UNUSED)
{
- if (symbol_get_value_expression (sp)->X_op == O_big)
- return 1;
-
- 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 0;
+ /* We don't support putting frags in the BSS segment, we fake it by
+ marking in_bss, then looking at s_skip for clues. */
+ subseg_set (bss_section, 0);
+ demand_empty_rest_of_line ();
+ mapping_state (MAP_DATA);
}
-static int in_my_get_expression = 0;
-
-static int
-my_get_expression (expressionS * ep, char ** str)
+static void
+s_even (int ignore ATTRIBUTE_UNUSED)
{
- char * save_in;
- segT seg;
-
- save_in = input_line_pointer;
- input_line_pointer = *str;
- in_my_get_expression = 1;
- seg = expression (ep);
- in_my_get_expression = 0;
-
- if (ep->X_op == O_illegal)
- {
- /* We found a bad expression in md_operand(). */
- *str = input_line_pointer;
- input_line_pointer = save_in;
- return 1;
- }
-
-#ifdef OBJ_AOUT
- if (seg != absolute_section
- && seg != text_section
- && seg != data_section
- && seg != bss_section
- && seg != undefined_section)
- {
- inst.error = _("bad_segment");
- *str = input_line_pointer;
- input_line_pointer = save_in;
- return 1;
- }
-#endif
+ /* Never make frag if expect extra pass. */
+ if (!need_pass_2)
+ frag_align (1, 0, 0);
- /* 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 (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;
- }
+ record_alignment (now_seg, 1);
- *str = input_line_pointer;
- input_line_pointer = save_in;
- return 0;
+ demand_empty_rest_of_line ();
}
-/* A standard register must be given at this point.
- SHIFT is the place to put it in inst.instruction.
- Restores input start point on error.
- Returns the reg#, or FAIL. */
+/* Directives: Literal pools. */
-static int
-reg_required_here (char ** str, int shift)
+static literal_pool *
+find_literal_pool (void)
{
- static char buff [128]; /* XXX */
- int reg;
- char * start = * str;
+ literal_pool * pool;
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_RN].htab)) != FAIL)
+ for (pool = list_of_pools; pool != NULL; pool = pool->next)
{
- if (shift >= 0)
- inst.instruction |= reg << shift;
- return reg;
+ if (pool->section == now_seg
+ && pool->sub_section == now_subseg)
+ break;
}
- /* Restore the start point, we may have got a reg of the wrong class. */
- *str = start;
-
- /* In the few cases where we might be able to accept something else
- this error can be overridden. */
- sprintf (buff, _("register expected, not '%.100s'"), start);
- inst.error = buff;
-
- return FAIL;
-}
-
-/* A Intel Wireless MMX technology register
- must be given at this point.
- Shift is the place to put it in inst.instruction.
- Restores input start point on err.
- Returns the reg#, or FAIL. */
-
-static int
-wreg_required_here (char ** str,
- int shift,
- enum wreg_type reg_type)
-{
- static char buff [128];
- int reg;
- char * start = *str;
-
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_IWMMXT].htab)) != FAIL)
- {
- if (wr_register (reg)
- && (reg_type == IWMMXT_REG_WR || reg_type == IWMMXT_REG_WR_OR_WC))
- {
- if (shift >= 0)
- inst.instruction |= (reg ^ WR_PREFIX) << shift;
- return reg;
- }
- else if (wc_register (reg)
- && (reg_type == IWMMXT_REG_WC || reg_type == IWMMXT_REG_WR_OR_WC))
- {
- if (shift >= 0)
- inst.instruction |= (reg ^ WC_PREFIX) << shift;
- return reg;
- }
- else if ((wcg_register (reg) && reg_type == IWMMXT_REG_WCG))
- {
- if (shift >= 0)
- inst.instruction |= ((reg ^ WC_PREFIX) - 8) << shift;
- return reg;
- }
- }
-
- /* Restore the start point, we may have got a reg of the wrong class. */
- *str = start;
-
- /* In the few cases where we might be able to accept
- something else this error can be overridden. */
- sprintf (buff, _("Intel Wireless MMX technology register expected, not '%.100s'"), start);
- inst.error = buff;
-
- return FAIL;
+ return pool;
}
-static const struct asm_psr *
-arm_psr_parse (char ** ccp)
+static literal_pool *
+find_or_make_literal_pool (void)
{
- char * start = * ccp;
- char c;
- char * p;
- const struct asm_psr * psr;
+ /* Next literal pool ID number. */
+ static unsigned int latest_pool_num = 1;
+ literal_pool * pool;
- p = start;
+ pool = find_literal_pool ();
- /* Skip to the end of the next word in the input stream. */
- do
+ if (pool == NULL)
{
- c = *p++;
- }
- while (ISALPHA (c) || c == '_');
-
- /* Terminate the word. */
- *--p = 0;
-
- /* CPSR's and SPSR's can now be lowercase. This is just a convenience
- feature for ease of use and backwards compatibility. */
- if (!strncmp (start, "cpsr", 4))
- strncpy (start, "CPSR", 4);
- else if (!strncmp (start, "spsr", 4))
- strncpy (start, "SPSR", 4);
+ /* Create a new pool. */
+ pool = xmalloc (sizeof (* pool));
+ if (! pool)
+ return NULL;
- /* Now locate the word in the psr hash table. */
- psr = (const struct asm_psr *) hash_find (arm_psr_hsh, start);
+ pool->next_free_entry = 0;
+ pool->section = now_seg;
+ pool->sub_section = now_subseg;
+ pool->next = list_of_pools;
+ pool->symbol = NULL;
- /* Restore the input stream. */
- *p = c;
+ /* Add it to the list. */
+ list_of_pools = pool;
+ }
- /* If we found a valid match, advance the
- stream pointer past the end of the word. */
- *ccp = p;
+ /* New pools, and emptied pools, will have a NULL symbol. */
+ if (pool->symbol == NULL)
+ {
+ pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
+ (valueT) 0, &zero_address_frag);
+ pool->id = latest_pool_num ++;
+ }
- return psr;
+ /* Done. */
+ return pool;
}
-/* Parse the input looking for a PSR flag. */
+/* Add the literal in the global 'inst'
+ structure to the relevent literal pool. */
static int
-psr_required_here (char ** str)
+add_to_lit_pool (void)
{
- char * start = * str;
- const struct asm_psr * psr;
+ literal_pool * pool;
+ unsigned int entry;
- psr = arm_psr_parse (str);
+ pool = find_or_make_literal_pool ();
- if (psr)
+ /* Check if this literal value is already in the pool. */
+ for (entry = 0; entry < pool->next_free_entry; entry ++)
{
- /* If this is the SPSR that is being modified, set the R bit. */
- if (! psr->cpsr)
- inst.instruction |= SPSR_BIT;
-
- /* Set the psr flags in the MSR instruction. */
- inst.instruction |= psr->field << PSR_SHIFT;
+ if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+ && (inst.reloc.exp.X_op == O_constant)
+ && (pool->literals[entry].X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (pool->literals[entry].X_unsigned
+ == inst.reloc.exp.X_unsigned))
+ break;
- return SUCCESS;
+ if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+ && (inst.reloc.exp.X_op == O_symbol)
+ && (pool->literals[entry].X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (pool->literals[entry].X_add_symbol
+ == inst.reloc.exp.X_add_symbol)
+ && (pool->literals[entry].X_op_symbol
+ == inst.reloc.exp.X_op_symbol))
+ break;
}
- /* In the few cases where we might be able to accept
- something else this error can be overridden. */
- inst.error = _("flag for {c}psr instruction expected");
-
- /* Restore the start point. */
- *str = start;
- return FAIL;
-}
-
-static int
-co_proc_number (char ** str)
-{
- int processor, pchar;
- char *start;
-
- skip_whitespace (*str);
- start = *str;
-
- /* The data sheet seems to imply that just a number on its own is valid
- here, but the RISC iX assembler seems to accept a prefix 'p'. We will
- accept either. */
- if ((processor = arm_reg_parse (str, all_reg_maps[REG_TYPE_CP].htab))
- == FAIL)
+ /* Do we need to create a new entry? */
+ if (entry == pool->next_free_entry)
{
- *str = start;
-
- pchar = *(*str)++;
- if (pchar >= '0' && pchar <= '9')
- {
- processor = pchar - '0';
- if (**str >= '0' && **str <= '9')
- {
- processor = processor * 10 + *(*str)++ - '0';
- if (processor > 15)
- {
- inst.error = _("illegal co-processor number");
- return FAIL;
- }
- }
- }
- else
+ if (entry >= MAX_LITERAL_POOL_SIZE)
{
- inst.error = all_reg_maps[REG_TYPE_CP].expected;
+ inst.error = _("literal pool overflow");
return FAIL;
}
- }
-
- inst.instruction |= processor << 8;
- return SUCCESS;
-}
-
-static int
-cp_opc_expr (char ** str, int where, int length)
-{
- expressionS expr;
- skip_whitespace (* str);
-
- memset (&expr, '\0', sizeof (expr));
-
- if (my_get_expression (&expr, str))
- return FAIL;
- if (expr.X_op != O_constant)
- {
- inst.error = _("bad or missing expression");
- return FAIL;
+ pool->literals[entry] = inst.reloc.exp;
+ pool->next_free_entry += 1;
}
- if ((expr.X_add_number & ((1 << length) - 1)) != expr.X_add_number)
- {
- inst.error = _("immediate co-processor expression too large");
- return FAIL;
- }
+ inst.reloc.exp.X_op = O_symbol;
+ inst.reloc.exp.X_add_number = ((int) entry) * 4;
+ inst.reloc.exp.X_add_symbol = pool->symbol;
- inst.instruction |= expr.X_add_number << where;
return SUCCESS;
}
-static int
-cp_reg_required_here (char ** str, int where)
-{
- int reg;
- char * start = *str;
-
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_CN].htab)) != FAIL)
- {
- inst.instruction |= reg << where;
- return reg;
- }
-
- /* In the few cases where we might be able to accept something else
- this error can be overridden. */
- inst.error = all_reg_maps[REG_TYPE_CN].expected;
-
- /* Restore the start point. */
- *str = start;
- return FAIL;
-}
+/* Can't use symbol_new here, so have to create a symbol and then at
+ a later date assign it a value. Thats what these functions do. */
-static int
-fp_reg_required_here (char ** str, int where)
+static void
+symbol_locate (symbolS * symbolP,
+ const char * name, /* It is copied, the caller can modify. */
+ segT segment, /* Segment identifier (SEG_<something>). */
+ valueT valu, /* Symbol value. */
+ fragS * frag) /* Associated fragment. */
{
- int reg;
- char * start = * str;
-
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_FN].htab)) != FAIL)
- {
- inst.instruction |= reg << where;
- return reg;
- }
-
- /* In the few cases where we might be able to accept something else
- this error can be overridden. */
- inst.error = all_reg_maps[REG_TYPE_FN].expected;
-
- /* Restore the start point. */
- *str = start;
- return FAIL;
-}
+ unsigned int name_length;
+ char * preserved_copy_of_name;
-static int
-cp_address_offset (char ** str)
-{
- int offset;
+ name_length = strlen (name) + 1; /* +1 for \0. */
+ obstack_grow (&notes, name, name_length);
+ preserved_copy_of_name = obstack_finish (&notes);
- skip_whitespace (* str);
+#ifdef tc_canonicalize_symbol_name
+ preserved_copy_of_name =
+ tc_canonicalize_symbol_name (preserved_copy_of_name);
+#endif
- if (! is_immediate_prefix (**str))
- {
- inst.error = _("immediate expression expected");
- return FAIL;
- }
+ S_SET_NAME (symbolP, preserved_copy_of_name);
- (*str)++;
+ S_SET_SEGMENT (symbolP, segment);
+ S_SET_VALUE (symbolP, valu);
+ symbol_clear_list_pointers (symbolP);
- if (my_get_expression (& inst.reloc.exp, str))
- return FAIL;
+ symbol_set_frag (symbolP, frag);
- if (inst.reloc.exp.X_op == O_constant)
- {
- offset = inst.reloc.exp.X_add_number;
+ /* Link to end of symbol chain. */
+ {
+ extern int symbol_table_frozen;
- if (offset & 3)
- {
- inst.error = _("co-processor address must be word aligned");
- return FAIL;
- }
+ if (symbol_table_frozen)
+ abort ();
+ }
- if (offset > 1023 || offset < -1023)
- {
- inst.error = _("offset too large");
- return FAIL;
- }
+ symbol_append (symbolP, symbol_lastP, & symbol_rootP, & symbol_lastP);
- if (offset >= 0)
- inst.instruction |= INDEX_UP;
- else
- offset = -offset;
+ obj_symbol_new_hook (symbolP);
- inst.instruction |= offset >> 2;
- }
- else
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+#ifdef tc_symbol_new_hook
+ tc_symbol_new_hook (symbolP);
+#endif
- return SUCCESS;
+#ifdef DEBUG_SYMS
+ verify_symbol_chain (symbol_rootP, symbol_lastP);
+#endif /* DEBUG_SYMS */
}
-static int
-cp_address_required_here (char ** str, int wb_ok)
+
+static void
+s_ltorg (int ignored ATTRIBUTE_UNUSED)
{
- char * p = * str;
- int pre_inc = 0;
- int write_back = 0;
+ unsigned int entry;
+ literal_pool * pool;
+ char sym_name[20];
- if (*p == '[')
- {
- int reg;
+ pool = find_literal_pool ();
+ if (pool == NULL
+ || pool->symbol == NULL
+ || pool->next_free_entry == 0)
+ return;
- p++;
- skip_whitespace (p);
+ mapping_state (MAP_DATA);
- if ((reg = reg_required_here (& p, 16)) == FAIL)
- return FAIL;
+ /* Align pool as you have word accesses.
+ Only make a frag if we have to. */
+ if (!need_pass_2)
+ frag_align (2, 0, 0);
- skip_whitespace (p);
+ record_alignment (now_seg, 2);
- if (*p == ']')
- {
- p++;
+ sprintf (sym_name, "$$lit_\002%x", pool->id);
- skip_whitespace (p);
+ symbol_locate (pool->symbol, sym_name, now_seg,
+ (valueT) frag_now_fix (), frag_now);
+ symbol_table_insert (pool->symbol);
- if (*p == '\0')
- {
- /* As an extension to the official ARM syntax we allow:
- [Rn]
- as a short hand for:
- [Rn,#0] */
- inst.instruction |= PRE_INDEX | INDEX_UP;
- *str = p;
- return SUCCESS;
- }
+ ARM_SET_THUMB (pool->symbol, thumb_mode);
- if (skip_past_comma (& p) == FAIL)
- {
- inst.error = _("comma expected after closing square bracket");
- return FAIL;
- }
+#if defined OBJ_COFF || defined OBJ_ELF
+ ARM_SET_INTERWORK (pool->symbol, support_interwork);
+#endif
- skip_whitespace (p);
+ for (entry = 0; entry < pool->next_free_entry; entry ++)
+ /* First output the expression in the instruction to the pool. */
+ emit_expr (&(pool->literals[entry]), 4); /* .word */
- if (*p == '#')
- {
- if (wb_ok)
- {
- /* [Rn], #expr */
- write_back = WRITE_BACK;
+ /* Mark the pool as empty. */
+ pool->next_free_entry = 0;
+ pool->symbol = NULL;
+}
- if (reg == REG_PC)
- {
- inst.error = _("pc may not be used in post-increment");
- return FAIL;
- }
+#ifdef OBJ_ELF
+/* Forward declarations for functions below, in the MD interface
+ section. */
+static void fix_new_arm (fragS *, int, short, expressionS *, int, int);
+static valueT create_unwind_entry (int);
+static void start_unwind_section (const segT, int);
+static void add_unwind_opcode (valueT, int);
+static void flush_pending_unwind (void);
- if (cp_address_offset (& p) == FAIL)
- return FAIL;
- }
- else
- pre_inc = PRE_INDEX | INDEX_UP;
- }
- else if (*p == '{')
- {
- int option;
+/* Directives: Data. */
- /* [Rn], {<expr>} */
- p++;
+static void
+s_arm_elf_cons (int nbytes)
+{
+ expressionS exp;
- skip_whitespace (p);
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
- if (my_get_expression (& inst.reloc.exp, & p))
- return FAIL;
+ if (is_it_end_of_statement ())
+ {
+ demand_empty_rest_of_line ();
+ return;
+ }
- if (inst.reloc.exp.X_op == O_constant)
- {
- option = inst.reloc.exp.X_add_number;
+#ifdef md_cons_align
+ md_cons_align (nbytes);
+#endif
- if (option > 255 || option < 0)
- {
- inst.error = _("'option' field too large");
- return FAIL;
- }
+ mapping_state (MAP_DATA);
+ do
+ {
+ int reloc;
+ char *base = input_line_pointer;
- skip_whitespace (p);
+ expression (& exp);
- if (*p != '}')
- {
- inst.error = _("'}' expected at end of 'option' field");
- return FAIL;
- }
- else
- {
- p++;
- inst.instruction |= option;
- inst.instruction |= INDEX_UP;
- }
- }
- else
- {
- inst.error = _("non-constant expressions for 'option' field not supported");
- return FAIL;
- }
- }
- else
- {
- inst.error = _("# or { expected after comma");
- return FAIL;
- }
- }
+ if (exp.X_op != O_symbol)
+ emit_expr (&exp, (unsigned int) nbytes);
else
{
- /* '['Rn, #expr']'[!] */
-
- if (skip_past_comma (& p) == FAIL)
+ char *before_reloc = input_line_pointer;
+ reloc = parse_reloc (&input_line_pointer);
+ if (reloc == -1)
{
- inst.error = _("pre-indexed expression expected");
- return FAIL;
+ as_bad (_("unrecognized relocation suffix"));
+ ignore_rest_of_line ();
+ return;
}
-
- pre_inc = PRE_INDEX;
-
- if (cp_address_offset (& p) == FAIL)
- return FAIL;
-
- skip_whitespace (p);
-
- if (*p++ != ']')
+ else if (reloc == BFD_RELOC_UNUSED)
+ emit_expr (&exp, (unsigned int) nbytes);
+ else
{
- inst.error = _("missing ]");
- return FAIL;
- }
-
- skip_whitespace (p);
+ reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
+ int size = bfd_get_reloc_size (howto);
- if (wb_ok && *p == '!')
- {
- if (reg == REG_PC)
+ if (size > nbytes)
+ as_bad ("%s relocations do not fit in %d bytes",
+ howto->name, nbytes);
+ else
{
- inst.error = _("pc may not be used with write-back");
- return FAIL;
+ /* We've parsed an expression stopping at O_symbol.
+ But there may be more expression left now that we
+ have parsed the relocation marker. Parse it again.
+ XXX Surely there is a cleaner way to do this. */
+ char *p = input_line_pointer;
+ int offset;
+ char *save_buf = alloca (input_line_pointer - base);
+ memcpy (save_buf, base, input_line_pointer - base);
+ memmove (base + (input_line_pointer - before_reloc),
+ base, before_reloc - base);
+
+ input_line_pointer = base + (input_line_pointer-before_reloc);
+ expression (&exp);
+ memcpy (base, save_buf, p - base);
+
+ offset = nbytes - size;
+ p = frag_more ((int) nbytes);
+ fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
+ size, &exp, 0, reloc);
}
-
- p++;
- write_back = WRITE_BACK;
}
}
}
- else
- {
- if (my_get_expression (&inst.reloc.exp, &p))
- return FAIL;
-
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
- inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = PRE_INDEX;
- }
+ while (*input_line_pointer++ == ',');
- inst.instruction |= write_back | pre_inc;
- *str = p;
- return SUCCESS;
+ /* Put terminator back into stream. */
+ input_line_pointer --;
+ demand_empty_rest_of_line ();
}
-static int
-cp_byte_address_offset (char ** str)
-{
- int offset;
- skip_whitespace (* str);
-
- if (! is_immediate_prefix (**str))
- {
- inst.error = _("immediate expression expected");
- return FAIL;
- }
+/* Parse a .rel31 directive. */
- (*str)++;
+static void
+s_arm_rel31 (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+ char *p;
+ valueT highbit;
- if (my_get_expression (& inst.reloc.exp, str))
- return FAIL;
+ highbit = 0;
+ if (*input_line_pointer == '1')
+ highbit = 0x80000000;
+ else if (*input_line_pointer != '0')
+ as_bad (_("expected 0 or 1"));
- if (inst.reloc.exp.X_op == O_constant)
- {
- offset = inst.reloc.exp.X_add_number;
+ input_line_pointer++;
+ if (*input_line_pointer != ',')
+ as_bad (_("missing comma"));
+ input_line_pointer++;
- if (offset > 255 || offset < -255)
- {
- inst.error = _("offset too large");
- return FAIL;
- }
+#ifdef md_flush_pending_output
+ md_flush_pending_output ();
+#endif
- if (offset >= 0)
- inst.instruction |= INDEX_UP;
- else
- offset = -offset;
+#ifdef md_cons_align
+ md_cons_align (4);
+#endif
- inst.instruction |= offset;
- }
- else
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM_S2;
+ mapping_state (MAP_DATA);
- return SUCCESS;
-}
+ expression (&exp);
-static int
-cp_byte_address_required_here (char ** str)
-{
- char * p = * str;
- int pre_inc = 0;
- int write_back = 0;
+ p = frag_more (4);
+ md_number_to_chars (p, highbit, 4);
+ fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 1,
+ BFD_RELOC_ARM_PREL31);
- if (*p == '[')
- {
- int reg;
+ demand_empty_rest_of_line ();
+}
- p++;
- skip_whitespace (p);
+/* Directives: AEABI stack-unwind tables. */
- if ((reg = reg_required_here (& p, 16)) == FAIL)
- return FAIL;
+/* Parse an unwind_fnstart directive. Simply records the current location. */
- skip_whitespace (p);
+static void
+s_arm_unwind_fnstart (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ /* Mark the start of the function. */
+ unwind.proc_start = expr_build_dot ();
- if (*p == ']')
- {
- p++;
-
- if (skip_past_comma (& p) == SUCCESS)
- {
- /* [Rn], #expr */
- write_back = WRITE_BACK;
-
- if (reg == REG_PC)
- {
- inst.error = _("pc may not be used in post-increment");
- return FAIL;
- }
-
- if (cp_byte_address_offset (& p) == FAIL)
- return FAIL;
- }
- else
- pre_inc = PRE_INDEX | INDEX_UP;
- }
- else
- {
- /* '['Rn, #expr']'[!] */
+ /* Reset the rest of the unwind info. */
+ unwind.opcode_count = 0;
+ unwind.table_entry = NULL;
+ unwind.personality_routine = NULL;
+ unwind.personality_index = -1;
+ unwind.frame_size = 0;
+ unwind.fp_offset = 0;
+ unwind.fp_reg = 13;
+ unwind.fp_used = 0;
+ unwind.sp_restored = 0;
+}
- if (skip_past_comma (& p) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return FAIL;
- }
- pre_inc = PRE_INDEX;
+/* Parse a handlerdata directive. Creates the exception handling table entry
+ for the function. */
- if (cp_byte_address_offset (& p) == FAIL)
- return FAIL;
+static void
+s_arm_unwind_handlerdata (int ignored ATTRIBUTE_UNUSED)
+{
+ demand_empty_rest_of_line ();
+ if (unwind.table_entry)
+ as_bad (_("dupicate .handlerdata directive"));
- skip_whitespace (p);
+ create_unwind_entry (1);
+}
- if (*p++ != ']')
- {
- inst.error = _("missing ]");
- return FAIL;
- }
+/* Parse an unwind_fnend directive. Generates the index table entry. */
- skip_whitespace (p);
+static void
+s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED)
+{
+ long where;
+ char *ptr;
+ valueT val;
- if (*p == '!')
- {
- if (reg == REG_PC)
- {
- inst.error = _("pc may not be used with write-back");
- return FAIL;
- }
+ demand_empty_rest_of_line ();
- p++;
- write_back = WRITE_BACK;
- }
- }
- }
+ /* Add eh table entry. */
+ if (unwind.table_entry == NULL)
+ val = create_unwind_entry (0);
else
- {
- if (my_get_expression (&inst.reloc.exp, &p))
- return FAIL;
+ val = 0;
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM_S2;
- inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = PRE_INDEX;
- }
+ /* Add index table entry. This is two words. */
+ start_unwind_section (unwind.saved_seg, 1);
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
- inst.instruction |= write_back | pre_inc;
- *str = p;
- return SUCCESS;
-}
+ ptr = frag_more (8);
+ where = frag_now_fix () - 8;
-static void
-do_nop (char * str)
-{
- skip_whitespace (str);
- if (*str == '{')
+ /* Self relative offset of the function start. */
+ fix_new (frag_now, where, 4, unwind.proc_start, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ /* Indicate dependency on EHABI-defined personality routines to the
+ linker, if it hasn't been done already. */
+ if (unwind.personality_index >= 0 && unwind.personality_index < 3
+ && !(marked_pr_dependency & (1 << unwind.personality_index)))
{
- str++;
+ static const char *const name[] = {
+ "__aeabi_unwind_cpp_pr0",
+ "__aeabi_unwind_cpp_pr1",
+ "__aeabi_unwind_cpp_pr2"
+ };
+ symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
+ fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
+ marked_pr_dependency |= 1 << unwind.personality_index;
+ seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
+ = marked_pr_dependency;
+ }
- if (my_get_expression (&inst.reloc.exp, &str))
- inst.reloc.exp.X_op = O_illegal;
- else
- {
- skip_whitespace (str);
- if (*str == '}')
- str++;
- else
- inst.reloc.exp.X_op = O_illegal;
- }
+ if (val)
+ /* Inline exception table entry. */
+ md_number_to_chars (ptr + 4, val, 4);
+ else
+ /* Self relative offset of the table entry. */
+ fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
+ BFD_RELOC_ARM_PREL31);
- if (inst.reloc.exp.X_op != O_constant
- || inst.reloc.exp.X_add_number > 255
- || inst.reloc.exp.X_add_number < 0)
- {
- inst.error = _("Invalid NOP hint");
- return;
- }
+ /* Restore the original section. */
+ subseg_set (unwind.saved_seg, unwind.saved_subseg);
+}
- /* Arcitectural NOP hints are CPSR sets with no bits selected. */
- inst.instruction &= 0xf0000000;
- inst.instruction |= 0x0320f000 + inst.reloc.exp.X_add_number;
- }
- end_of_line (str);
-}
+/* Parse an unwind_cantunwind directive. */
static void
-do_empty (char * str)
+s_arm_unwind_cantunwind (int ignored ATTRIBUTE_UNUSED)
{
- /* Do nothing really. */
- end_of_line (str);
+ demand_empty_rest_of_line ();
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("personality routine specified for cantunwind frame"));
+
+ unwind.personality_index = -2;
}
-static void
-do_mrs (char * str)
-{
- int skip = 0;
- /* Only one syntax. */
- skip_whitespace (str);
+/* Parse a personalityindex directive. */
- if (reg_required_here (&str, 12) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+static void
+s_arm_unwind_personalityindex (int ignored ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = _("comma expected after register name");
- return;
- }
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("duplicate .personalityindex directive"));
- skip_whitespace (str);
+ expression (&exp);
- if ( streq (str, "CPSR")
- || streq (str, "SPSR")
- /* Lower case versions for backwards compatibility. */
- || streq (str, "cpsr")
- || streq (str, "spsr"))
- skip = 4;
-
- /* This is for backwards compatibility with older toolchains. */
- else if ( streq (str, "cpsr_all")
- || streq (str, "spsr_all"))
- skip = 8;
- else
+ if (exp.X_op != O_constant
+ || exp.X_add_number < 0 || exp.X_add_number > 15)
{
- inst.error = _("CPSR or SPSR expected");
+ as_bad (_("bad personality routine number"));
+ ignore_rest_of_line ();
return;
}
- if (* str == 's' || * str == 'S')
- inst.instruction |= SPSR_BIT;
- str += skip;
+ unwind.personality_index = exp.X_add_number;
- end_of_line (str);
+ demand_empty_rest_of_line ();
}
-/* Two possible forms:
- "{C|S}PSR_<field>, Rm",
- "{C|S}PSR_f, #expression". */
+
+/* Parse a personality directive. */
static void
-do_msr (char * str)
+s_arm_unwind_personality (int ignored ATTRIBUTE_UNUSED)
{
- skip_whitespace (str);
-
- if (psr_required_here (& str) == FAIL)
- return;
+ char *name, *p, c;
- if (skip_past_comma (& str) == FAIL)
- {
- inst.error = _("comma missing after psr flags");
- return;
- }
+ if (unwind.personality_routine || unwind.personality_index != -1)
+ as_bad (_("duplicate .personality directive"));
- skip_whitespace (str);
+ name = input_line_pointer;
+ c = get_symbol_end ();
+ p = input_line_pointer;
+ unwind.personality_routine = symbol_find_or_make (name);
+ *p = c;
+ demand_empty_rest_of_line ();
+}
- if (reg_required_here (& str, 0) != FAIL)
- {
- inst.error = NULL;
- end_of_line (str);
- return;
- }
- if (! is_immediate_prefix (* str))
- {
- inst.error =
- _("only a register or immediate value can follow a psr flag");
- return;
- }
+/* Parse a directive saving core registers. */
- str ++;
- inst.error = NULL;
+static void
+s_arm_unwind_save_core (void)
+{
+ valueT op;
+ long range;
+ int n;
- if (my_get_expression (& inst.reloc.exp, & str))
+ range = parse_reg_list (&input_line_pointer);
+ if (range == FAIL)
{
- inst.error =
- _("only a register or immediate value can follow a psr flag");
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
return;
}
- inst.instruction |= INST_IMMEDIATE;
+ demand_empty_rest_of_line ();
- if (inst.reloc.exp.X_add_symbol)
- {
- inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
- inst.reloc.pc_rel = 0;
- }
- else
+ /* Turn .unwind_movsp ip followed by .unwind_save {..., ip, ...}
+ into .unwind_save {..., sp...}. We aren't bothered about the value of
+ ip because it is clobbered by calls. */
+ if (unwind.sp_restored && unwind.fp_reg == 12
+ && (range & 0x3000) == 0x1000)
{
- unsigned value = validate_immediate (inst.reloc.exp.X_add_number);
-
- if (value == (unsigned) FAIL)
- {
- inst.error = _("invalid constant");
- return;
- }
-
- inst.instruction |= value;
+ unwind.opcode_count--;
+ unwind.sp_restored = 0;
+ range = (range | 0x2000) & ~0x1000;
+ unwind.pending_offset = 0;
}
- inst.error = NULL;
- end_of_line (str);
-}
-
-/* Long Multiply Parser
- UMULL RdLo, RdHi, Rm, Rs
- SMULL RdLo, RdHi, Rm, Rs
- UMLAL RdLo, RdHi, Rm, Rs
- SMLAL RdLo, RdHi, Rm, Rs. */
-
-static void
-do_mull (char * str)
-{
- int rdlo, rdhi, rm, rs;
-
- /* Only one format "rdlo, rdhi, rm, rs". */
- skip_whitespace (str);
-
- if ((rdlo = reg_required_here (&str, 12)) == FAIL)
+ /* See if we can use the short opcodes. These pop a block of upto 8
+ registers starting with r4, plus maybe r14. */
+ for (n = 0; n < 8; n++)
{
- inst.error = BAD_ARGS;
- return;
+ /* Break at the first non-saved register. */
+ if ((range & (1 << (n + 4))) == 0)
+ break;
}
-
- if (skip_past_comma (&str) == FAIL
- || (rdhi = reg_required_here (&str, 16)) == FAIL)
+ /* See if there are any other bits set. */
+ if (n == 0 || (range & (0xfff0 << n) & 0xbff0) != 0)
{
- inst.error = BAD_ARGS;
- return;
+ /* Use the long form. */
+ op = 0x8000 | ((range >> 4) & 0xfff);
+ add_unwind_opcode (op, 2);
}
-
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
+ else
{
- inst.error = BAD_ARGS;
- return;
+ /* Use the short form. */
+ if (range & 0x4000)
+ op = 0xa8; /* Pop r14. */
+ else
+ op = 0xa0; /* Do not pop r14. */
+ op |= (n - 1);
+ add_unwind_opcode (op, 1);
}
- /* rdhi, rdlo and rm must all be different. */
- if (rdlo == rdhi || rdlo == rm || rdhi == rm)
- as_tsktsk (_("rdhi, rdlo and rm must all be different"));
-
- if (skip_past_comma (&str) == FAIL
- || (rs = reg_required_here (&str, 8)) == FAIL)
+ /* Pop r0-r3. */
+ if (range & 0xf)
{
- inst.error = BAD_ARGS;
- return;
+ op = 0xb100 | (range & 0xf);
+ add_unwind_opcode (op, 2);
}
- if (rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC || rdhi == REG_PC)
+ /* Record the number of bytes pushed. */
+ for (n = 0; n < 16; n++)
{
- inst.error = BAD_PC;
- return;
+ if (range & (1 << n))
+ unwind.frame_size += 4;
}
-
- end_of_line (str);
}
+
+/* Parse a directive saving FPA registers. */
+
static void
-do_mul (char * str)
+s_arm_unwind_save_fpa (int reg)
{
- int rd, rm;
-
- /* Only one format "rd, rm, rs". */
- skip_whitespace (str);
+ expressionS exp;
+ int num_regs;
+ valueT op;
- if ((rd = reg_required_here (&str, 16)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+ /* Get Number of registers to transfer. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ expression (&exp);
+ else
+ exp.X_op = O_illegal;
- if (rd == REG_PC)
+ if (exp.X_op != O_constant)
{
- inst.error = BAD_PC;
+ as_bad (_("expected , <constant>"));
+ ignore_rest_of_line ();
return;
}
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+ num_regs = exp.X_add_number;
- if (rm == REG_PC)
+ if (num_regs < 1 || num_regs > 4)
{
- inst.error = BAD_PC;
+ as_bad (_("number of registers must be in the range [1:4]"));
+ ignore_rest_of_line ();
return;
}
- if (rm == rd)
- as_tsktsk (_("rd and rm should be different in mul"));
+ demand_empty_rest_of_line ();
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 8)) == FAIL)
+ if (reg == 4)
{
- inst.error = BAD_ARGS;
- return;
+ /* Short form. */
+ op = 0xb4 | (num_regs - 1);
+ add_unwind_opcode (op, 1);
}
-
- if (rm == REG_PC)
+ else
{
- inst.error = BAD_PC;
- return;
+ /* Long form. */
+ op = 0xc800 | (reg << 4) | (num_regs - 1);
+ add_unwind_opcode (op, 2);
}
-
- end_of_line (str);
+ unwind.frame_size += num_regs * 12;
}
-static void
-do_mlas (char * str, bfd_boolean is_mls)
-{
- int rd, rm;
-
- /* Only one format "rd, rm, rs, rn". */
- skip_whitespace (str);
-
- if ((rd = reg_required_here (&str, 16)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+/* Parse a directive saving VFP registers. */
- if (skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+static void
+s_arm_unwind_save_vfp (void)
+{
+ int count;
+ int reg;
+ valueT op;
- if (rm == REG_PC)
+ count = parse_vfp_reg_list (&input_line_pointer, &reg, 1);
+ if (count == FAIL)
{
- inst.error = BAD_PC;
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
return;
}
- /* This restriction does not apply to mls (nor to mla in v6, but
- that's hard to detect at present). */
- if (rm == rd && !is_mls)
- as_tsktsk (_("rd and rm should be different in mla"));
+ demand_empty_rest_of_line ();
- if (skip_past_comma (&str) == FAIL
- || (rd = reg_required_here (&str, 8)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 12)) == FAIL)
+ if (reg == 8)
{
- inst.error = BAD_ARGS;
- return;
+ /* Short form. */
+ op = 0xb8 | (count - 1);
+ add_unwind_opcode (op, 1);
}
-
- if (rd == REG_PC || rm == REG_PC)
+ else
{
- inst.error = BAD_PC;
- return;
+ /* Long form. */
+ op = 0xb300 | (reg << 4) | (count - 1);
+ add_unwind_opcode (op, 2);
}
-
- end_of_line (str);
-}
-
-static void
-do_mla (char *str)
-{
- do_mlas (str, FALSE);
-}
-
-static void
-do_mls (char *str)
-{
- do_mlas (str, TRUE);
+ unwind.frame_size += count * 8 + 4;
}
-/* Expects *str -> the characters "acc0", possibly with leading blanks.
- Advances *str to the next non-alphanumeric.
- Returns 0, or else FAIL (in which case sets inst.error).
- (In a future XScale, there may be accumulators other than zero.
- At that time this routine and its callers can be upgraded to suit.) */
+/* Parse a directive saving iWMMXt data registers. */
-static int
-accum0_required_here (char ** str)
+static void
+s_arm_unwind_save_mmxwr (void)
{
- static char buff [128]; /* Note the address is taken. Hence, static. */
- char * p = * str;
- char c;
- int result = 0; /* The accum number. */
-
- skip_whitespace (p);
-
- *str = p; /* Advance caller's string pointer too. */
- c = *p++;
- while (ISALNUM (c))
- c = *p++;
+ int reg;
+ int hi_reg;
+ int i;
+ unsigned mask = 0;
+ valueT op;
- *--p = 0; /* Aap nul into input buffer at non-alnum. */
+ if (*input_line_pointer == '{')
+ input_line_pointer++;
- if (! ( streq (*str, "acc0") || streq (*str, "ACC0")))
+ do
{
- sprintf (buff, _("acc0 expected, not '%.100s'"), *str);
- inst.error = buff;
- result = FAIL;
- }
-
- *p = c; /* Unzap. */
- *str = p; /* Caller's string pointer to after match. */
- return result;
-}
+ reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
-static int
-ldst_extend_v4 (char ** str)
-{
- int add = INDEX_UP;
+ if (reg == FAIL)
+ {
+ as_bad (_(reg_expected_msgs[REG_TYPE_MMXWR]));
+ goto error;
+ }
- switch (**str)
- {
- case '#':
- case '$':
- (*str)++;
- if (my_get_expression (& inst.reloc.exp, str))
- return FAIL;
+ if (mask >> reg)
+ as_tsktsk (_("register list not in ascending order"));
+ mask |= 1 << reg;
- if (inst.reloc.exp.X_op == O_constant)
+ if (*input_line_pointer == '-')
{
- int value = inst.reloc.exp.X_add_number;
-
- if (value < -255 || value > 255)
+ input_line_pointer++;
+ hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWR);
+ if (hi_reg == FAIL)
{
- inst.error = _("address offset too large");
- return FAIL;
+ as_bad (_(reg_expected_msgs[REG_TYPE_MMXWR]));
+ goto error;
}
-
- if (value < 0)
+ else if (reg >= hi_reg)
{
- value = -value;
- add = 0;
+ as_bad (_("bad register range"));
+ goto error;
}
-
- /* Halfword and signextension instructions have the
- immediate value split across bits 11..8 and bits 3..0. */
- inst.instruction |= (add | HWOFFSET_IMM
- | ((value >> 4) << 8) | (value & 0xF));
+ for (; reg < hi_reg; reg++)
+ mask |= 1 << reg;
}
- else
- {
- inst.instruction |= HWOFFSET_IMM;
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
- inst.reloc.pc_rel = 0;
- }
- return SUCCESS;
-
- case '-':
- add = 0;
- /* Fall through. */
-
- case '+':
- (*str)++;
- /* Fall through. */
-
- default:
- if (reg_required_here (str, 0) == FAIL)
- return FAIL;
-
- inst.instruction |= add;
- return SUCCESS;
}
-}
-
-/* Expects **str -> after a comma. May be leading blanks.
- Advances *str, recognizing a load mode, and setting inst.instruction.
- Returns rn, or else FAIL (in which case may set inst.error
- and not advance str)
+ while (skip_past_comma (&input_line_pointer) != FAIL);
- Note: doesn't know Rd, so no err checks that require such knowledge. */
+ if (*input_line_pointer == '}')
+ input_line_pointer++;
-static int
-ld_mode_required_here (char ** string)
-{
- char * str = * string;
- int rn;
- int pre_inc = 0;
+ demand_empty_rest_of_line ();
- skip_whitespace (str);
+ /* Generate any deferred opcodes becuuse we're going to be looking at
+ the list. */
+ flush_pending_unwind ();
- if (* str == '[')
+ for (i = 0; i < 16; i++)
{
- str++;
-
- skip_whitespace (str);
-
- if ((rn = reg_required_here (& str, 16)) == FAIL)
- return FAIL;
-
- skip_whitespace (str);
+ if (mask & (1 << i))
+ unwind.frame_size += 8;
+ }
- if (* str == ']')
+ /* Attempt to combine with a previous opcode. We do this because gcc
+ likes to output separate unwind directives for a single block of
+ registers. */
+ if (unwind.opcode_count > 0)
+ {
+ i = unwind.opcodes[unwind.opcode_count - 1];
+ if ((i & 0xf8) == 0xc0)
{
- str ++;
-
- if (skip_past_comma (& str) == SUCCESS)
+ i &= 7;
+ /* Only merge if the blocks are contiguous. */
+ if (i < 6)
{
- /* [Rn],... (post inc) */
- if (ldst_extend_v4 (&str) == FAIL)
- return FAIL;
+ if ((mask & 0xfe00) == (1 << 9))
+ {
+ mask |= ((1 << (i + 11)) - 1) & 0xfc00;
+ unwind.opcode_count--;
+ }
}
- else /* [Rn] */
+ else if (i == 6 && unwind.opcode_count >= 2)
{
- skip_whitespace (str);
+ i = unwind.opcodes[unwind.opcode_count - 2];
+ reg = i >> 4;
+ i &= 0xf;
- if (* str == '!')
+ op = 0xffff << (reg - 1);
+ if (reg > 0
+ || ((mask & op) == (1u << (reg - 1))))
{
- str ++;
- inst.instruction |= WRITE_BACK;
+ op = (1 << (reg + i + 1)) - 1;
+ op &= ~((1 << reg) - 1);
+ mask |= op;
+ unwind.opcode_count -= 2;
}
-
- inst.instruction |= INDEX_UP | HWOFFSET_IMM;
- pre_inc = 1;
}
}
- else /* [Rn,...] */
+ }
+
+ hi_reg = 15;
+ /* We want to generate opcodes in the order the registers have been
+ saved, ie. descending order. */
+ for (reg = 15; reg >= -1; reg--)
+ {
+ /* Save registers in blocks. */
+ if (reg < 0
+ || !(mask & (1 << reg)))
{
- if (skip_past_comma (& str) == FAIL)
+ /* We found an unsaved reg. Generate opcodes to save the
+ preceeding block. */
+ if (reg != hi_reg)
{
- inst.error = _("pre-indexed expression expected");
- return FAIL;
+ if (reg == 9)
+ {
+ /* Short form. */
+ op = 0xc0 | (hi_reg - 10);
+ add_unwind_opcode (op, 1);
+ }
+ else
+ {
+ /* Long form. */
+ op = 0xc600 | ((reg + 1) << 4) | ((hi_reg - reg) - 1);
+ add_unwind_opcode (op, 2);
+ }
}
+ hi_reg = reg - 1;
+ }
+ }
- pre_inc = 1;
+ return;
+error:
+ ignore_rest_of_line ();
+}
- if (ldst_extend_v4 (&str) == FAIL)
- return FAIL;
+static void
+s_arm_unwind_save_mmxwcg (void)
+{
+ int reg;
+ int hi_reg;
+ unsigned mask = 0;
+ valueT op;
- skip_whitespace (str);
+ if (*input_line_pointer == '{')
+ input_line_pointer++;
- if (* str ++ != ']')
- {
- inst.error = _("missing ]");
- return FAIL;
- }
+ do
+ {
+ reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
- skip_whitespace (str);
+ if (reg == FAIL)
+ {
+ as_bad (_(reg_expected_msgs[REG_TYPE_MMXWCG]));
+ goto error;
+ }
- if (* str == '!')
+ reg -= 8;
+ if (mask >> reg)
+ as_tsktsk (_("register list not in ascending order"));
+ mask |= 1 << reg;
+
+ if (*input_line_pointer == '-')
+ {
+ input_line_pointer++;
+ hi_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
+ if (hi_reg == FAIL)
{
- str ++;
- inst.instruction |= WRITE_BACK;
+ as_bad (_(reg_expected_msgs[REG_TYPE_MMXWCG]));
+ goto error;
}
+ else if (reg >= hi_reg)
+ {
+ as_bad (_("bad register range"));
+ goto error;
+ }
+ for (; reg < hi_reg; reg++)
+ mask |= 1 << reg;
}
}
- else if (* str == '=') /* ldr's "r,=label" syntax */
- /* We should never reach here, because <text> = <expression> is
- caught gas/read.c read_a_source_file() as a .set operation. */
- return FAIL;
- else /* PC +- 8 bit immediate offset. */
- {
- if (my_get_expression (& inst.reloc.exp, & str))
- return FAIL;
+ while (skip_past_comma (&input_line_pointer) != FAIL);
- inst.instruction |= HWOFFSET_IMM; /* The I bit. */
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
- inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
+ if (*input_line_pointer == '}')
+ input_line_pointer++;
- rn = REG_PC;
- pre_inc = 1;
- }
+ demand_empty_rest_of_line ();
- inst.instruction |= (pre_inc ? PRE_INDEX : 0);
- * string = str;
+ /* Generate any deferred opcodes becuuse we're going to be looking at
+ the list. */
+ flush_pending_unwind ();
- return rn;
+ for (reg = 0; reg < 16; reg++)
+ {
+ if (mask & (1 << reg))
+ unwind.frame_size += 4;
+ }
+ op = 0xc700 | mask;
+ add_unwind_opcode (op, 2);
+ return;
+error:
+ ignore_rest_of_line ();
}
-/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
- SMLAxy{cond} Rd,Rm,Rs,Rn
- SMLAWy{cond} Rd,Rm,Rs,Rn
- Error if any register is R15. */
-
-static void
-do_smla (char * str)
-{
- int rd, rm, rs, rn;
-
- skip_whitespace (str);
-
- if ((rd = reg_required_here (& str, 16)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rs = reg_required_here (& str, 8)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rn = reg_required_here (& str, 12)) == FAIL)
- inst.error = BAD_ARGS;
-
- else if (rd == REG_PC || rm == REG_PC || rs == REG_PC || rn == REG_PC)
- inst.error = BAD_PC;
- else
- end_of_line (str);
-}
-
-/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
- SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
- Error if any register is R15.
- Warning if Rdlo == Rdhi. */
+/* Parse an unwind_save directive. */
static void
-do_smlal (char * str)
+s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED)
{
- int rdlo, rdhi, rm, rs;
+ char *peek;
+ struct reg_entry *reg;
+ bfd_boolean had_brace = FALSE;
- skip_whitespace (str);
+ /* Figure out what sort of save we have. */
+ peek = input_line_pointer;
- if ((rdlo = reg_required_here (& str, 12)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rdhi = reg_required_here (& str, 16)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rs = reg_required_here (& str, 8)) == FAIL)
+ if (*peek == '{')
{
- inst.error = BAD_ARGS;
- return;
+ had_brace = TRUE;
+ peek++;
}
- if (rdlo == REG_PC || rdhi == REG_PC || rm == REG_PC || rs == REG_PC)
+ reg = arm_reg_parse_multi (&peek);
+
+ if (!reg)
{
- inst.error = BAD_PC;
+ as_bad (_("register expected"));
+ ignore_rest_of_line ();
return;
}
- if (rdlo == rdhi)
- as_tsktsk (_("rdhi and rdlo must be different"));
-
- end_of_line (str);
-}
-
-/* ARM V5E (El Segundo) signed-multiply (argument parse)
- SMULxy{cond} Rd,Rm,Rs
- Error if any register is R15. */
-
-static void
-do_smul (char * str)
-{
- int rd, rm, rs;
-
- skip_whitespace (str);
-
- if ((rd = reg_required_here (& str, 16)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rs = reg_required_here (& str, 8)) == FAIL)
- inst.error = BAD_ARGS;
-
- else if (rd == REG_PC || rm == REG_PC || rs == REG_PC)
- inst.error = BAD_PC;
-
- else
- end_of_line (str);
-}
-
-/* ARM V5E (El Segundo) saturating-add/subtract (argument parse)
- Q[D]{ADD,SUB}{cond} Rd,Rm,Rn
- Error if any register is R15. */
-
-static void
-do_qadd (char * str)
-{
- int rd, rm, rn;
-
- skip_whitespace (str);
-
- if ((rd = reg_required_here (& str, 12)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rn = reg_required_here (& str, 16)) == FAIL)
- inst.error = BAD_ARGS;
+ switch (reg->type)
+ {
+ case REG_TYPE_FN:
+ if (had_brace)
+ {
+ as_bad (_("FPA .unwind_save does not take a register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+ s_arm_unwind_save_fpa (reg->number);
+ return;
- else if (rd == REG_PC || rm == REG_PC || rn == REG_PC)
- inst.error = BAD_PC;
+ case REG_TYPE_RN: s_arm_unwind_save_core (); return;
+ case REG_TYPE_VFD: s_arm_unwind_save_vfp (); return;
+ case REG_TYPE_MMXWR: s_arm_unwind_save_mmxwr (); return;
+ case REG_TYPE_MMXWCG: s_arm_unwind_save_mmxwcg (); return;
- else
- end_of_line (str);
+ default:
+ as_bad (_(".unwind_save does not support this kind of register"));
+ ignore_rest_of_line ();
+ }
}
-/* ARM V5E (el Segundo)
- MCRRcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
- MRRCcc <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
-
- These are equivalent to the XScale instructions MAR and MRA,
- respectively, when coproc == 0, opcode == 0, and CRm == 0.
- Result unpredicatable if Rd or Rn is R15. */
+/* Parse an unwind_movsp directive. */
static void
-do_co_reg2c (char * str)
+s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
{
- int rd, rn;
-
- skip_whitespace (str);
-
- if (co_proc_number (& str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (& str) == FAIL
- || cp_opc_expr (& str, 4, 4) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ int reg;
+ valueT op;
- if (skip_past_comma (& str) == FAIL
- || (rd = reg_required_here (& str, 12)) == FAIL)
+ reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+ if (reg == FAIL)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad (_(reg_expected_msgs[REG_TYPE_RN]));
+ ignore_rest_of_line ();
return;
}
+ demand_empty_rest_of_line ();
- if (skip_past_comma (& str) == FAIL
- || (rn = reg_required_here (& str, 16)) == FAIL)
+ if (reg == REG_SP || reg == REG_PC)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad (_("SP and PC not permitted in .unwind_movsp directive"));
return;
}
- /* Unpredictable result if rd or rn is R15. */
- if (rd == REG_PC || rn == REG_PC)
- as_tsktsk
- (_("Warning: instruction unpredictable when using r15"));
+ if (unwind.fp_reg != REG_SP)
+ as_bad (_("unexpected .unwind_movsp directive"));
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 0) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* Generate opcode to restore the value. */
+ op = 0x90 | reg;
+ add_unwind_opcode (op, 1);
- end_of_line (str);
+ /* Record the information for later. */
+ unwind.fp_reg = reg;
+ unwind.fp_offset = unwind.frame_size;
+ unwind.sp_restored = 1;
}
-/* ARM V5 count-leading-zeroes instruction (argument parse)
- CLZ{<cond>} <Rd>, <Rm>
- Condition defaults to COND_ALWAYS.
- Error if Rd or Rm are R15. */
+/* Parse an unwind_pad directive. */
static void
-do_clz (char * str)
+s_arm_unwind_pad (int ignored ATTRIBUTE_UNUSED)
{
- int rd, rm;
+ int offset;
- skip_whitespace (str);
+ if (immediate_for_directive (&offset) == FAIL)
+ return;
- if (((rd = reg_required_here (& str, 12)) == FAIL)
- || (skip_past_comma (& str) == FAIL)
- || ((rm = reg_required_here (& str, 0)) == FAIL))
- inst.error = BAD_ARGS;
+ if (offset & 3)
+ {
+ as_bad (_("stack increment must be multiple of 4"));
+ ignore_rest_of_line ();
+ return;
+ }
- else if (rd == REG_PC || rm == REG_PC )
- inst.error = BAD_PC;
+ /* Don't generate any opcodes, just record the details for later. */
+ unwind.frame_size += offset;
+ unwind.pending_offset += offset;
- else
- end_of_line (str);
+ demand_empty_rest_of_line ();
}
-/* ARM V5 (argument parse)
- LDC2{L} <coproc>, <CRd>, <addressing mode>
- STC2{L} <coproc>, <CRd>, <addressing mode>
- Instruction is not conditional, and has 0xf in the condition field.
- Otherwise, it's the same as LDC/STC. */
+/* Parse an unwind_setfp directive. */
static void
-do_lstc2 (char * str)
+s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
{
- skip_whitespace (str);
+ int sp_reg;
+ int fp_reg;
+ int offset;
- if (co_proc_number (& str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- }
- else if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- }
- else if (skip_past_comma (& str) == FAIL
- || cp_address_required_here (&str, CP_WB_OK) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- }
+ fp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ sp_reg = FAIL;
else
- end_of_line (str);
-}
-
-/* ARM V5 (argument parse)
- CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>, <opcode_2>
- Instruction is not conditional, and has 0xf in the condition field.
- Otherwise, it's the same as CDP. */
+ sp_reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
-static void
-do_cdp2 (char * str)
-{
- skip_whitespace (str);
-
- if (co_proc_number (& str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (& str) == FAIL
- || cp_opc_expr (& str, 20,4) == FAIL)
+ if (fp_reg == FAIL || sp_reg == FAIL)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad (_("expected <reg>, <reg>"));
+ ignore_rest_of_line ();
return;
}
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 12) == FAIL)
+ /* Optional constant. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
+ if (immediate_for_directive (&offset) == FAIL)
+ return;
}
+ else
+ offset = 0;
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 16) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ demand_empty_rest_of_line ();
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 0) == FAIL)
+ if (sp_reg != 13 && sp_reg != unwind.fp_reg)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad (_("register must be either sp or set by a previous"
+ "unwind_movsp directive"));
return;
}
- if (skip_past_comma (& str) == SUCCESS)
- {
- if (cp_opc_expr (& str, 5, 3) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
- }
-
- end_of_line (str);
+ /* Don't generate any opcodes, just record the information for later. */
+ unwind.fp_reg = fp_reg;
+ unwind.fp_used = 1;
+ if (sp_reg == 13)
+ unwind.fp_offset = unwind.frame_size - offset;
+ else
+ unwind.fp_offset -= offset;
}
-/* ARM V5 (argument parse)
- MCR2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
- MRC2 <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>, <opcode_2>
- Instruction is not conditional, and has 0xf in the condition field.
- Otherwise, it's the same as MCR/MRC. */
+/* Parse an unwind_raw directive. */
static void
-do_co_reg2 (char * str)
+s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED)
{
- skip_whitespace (str);
-
- if (co_proc_number (& str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (& str) == FAIL
- || cp_opc_expr (& str, 21, 3) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ expressionS exp;
+ /* This is an arbitary limit. */
+ unsigned char op[16];
+ int count;
- if (skip_past_comma (& str) == FAIL
- || reg_required_here (& str, 12) == FAIL)
+ expression (&exp);
+ if (exp.X_op == O_constant
+ && skip_past_comma (&input_line_pointer) != FAIL)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
+ unwind.frame_size += exp.X_add_number;
+ expression (&exp);
}
+ else
+ exp.X_op = O_illegal;
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 16) == FAIL)
+ if (exp.X_op != O_constant)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad (_("expected <offset>, <opcode>"));
+ ignore_rest_of_line ();
return;
}
- if (skip_past_comma (& str) == FAIL
- || cp_reg_required_here (& str, 0) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ count = 0;
- if (skip_past_comma (& str) == SUCCESS)
+ /* Parse the opcode. */
+ for (;;)
{
- if (cp_opc_expr (& str, 5, 3) == FAIL)
+ if (count >= 16)
+ {
+ as_bad (_("unwind opcode too long"));
+ ignore_rest_of_line ();
+ }
+ if (exp.X_op != O_constant || exp.X_add_number & ~0xff)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad (_("invalid unwind opcode"));
+ ignore_rest_of_line ();
return;
}
- }
-
- end_of_line (str);
-}
-
-static void
-do_bx (char * str)
-{
- int reg;
+ op[count++] = exp.X_add_number;
- skip_whitespace (str);
+ /* Parse the next byte. */
+ if (skip_past_comma (&input_line_pointer) == FAIL)
+ break;
- if ((reg = reg_required_here (&str, 0)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
+ expression (&exp);
}
- /* Note - it is not illegal to do a "bx pc". Useless, but not illegal. */
- if (reg == REG_PC)
- as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
+ /* Add the opcode bytes in reverse order. */
+ while (count--)
+ add_unwind_opcode (op[count], 1);
- end_of_line (str);
+ demand_empty_rest_of_line ();
}
+#endif /* OBJ_ELF */
-/* ARM v5TEJ. Jump to Jazelle code. */
+/* This table describes all the machine specific pseudo-ops the assembler
+ has to support. The fields are:
+ pseudo-op name without dot
+ function to call to execute this pseudo-op
+ Integer arg to pass to the function. */
-static void
-do_bxj (char * str)
+const pseudo_typeS md_pseudo_table[] =
{
- int reg;
+ /* Never called because '.req' does not start a line. */
+ { "req", s_req, 0 },
+ { "unreq", s_unreq, 0 },
+ { "bss", s_bss, 0 },
+ { "align", s_align, 0 },
+ { "arm", s_arm, 0 },
+ { "thumb", s_thumb, 0 },
+ { "code", s_code, 0 },
+ { "force_thumb", s_force_thumb, 0 },
+ { "thumb_func", s_thumb_func, 0 },
+ { "thumb_set", s_thumb_set, 0 },
+ { "even", s_even, 0 },
+ { "ltorg", s_ltorg, 0 },
+ { "pool", s_ltorg, 0 },
+ { "syntax", s_syntax, 0 },
+#ifdef OBJ_ELF
+ { "word", s_arm_elf_cons, 4 },
+ { "long", s_arm_elf_cons, 4 },
+ { "rel31", s_arm_rel31, 0 },
+ { "fnstart", s_arm_unwind_fnstart, 0 },
+ { "fnend", s_arm_unwind_fnend, 0 },
+ { "cantunwind", s_arm_unwind_cantunwind, 0 },
+ { "personality", s_arm_unwind_personality, 0 },
+ { "personalityindex", s_arm_unwind_personalityindex, 0 },
+ { "handlerdata", s_arm_unwind_handlerdata, 0 },
+ { "save", s_arm_unwind_save, 0 },
+ { "movsp", s_arm_unwind_movsp, 0 },
+ { "pad", s_arm_unwind_pad, 0 },
+ { "setfp", s_arm_unwind_setfp, 0 },
+ { "unwind_raw", s_arm_unwind_raw, 0 },
+#else
+ { "word", cons, 4},
+#endif
+ { "extend", float_cons, 'x' },
+ { "ldouble", float_cons, 'x' },
+ { "packed", float_cons, 'p' },
+ { 0, 0, 0 }
+};
+
+/* Parser functions used exclusively in instruction operands. */
- skip_whitespace (str);
+/* Generic immediate-value read function for use in insn parsing.
+ STR points to the beginning of the immediate (the leading #);
+ VAL receives the value; if the value is outside [MIN, MAX]
+ issue an error. PREFIX_OPT is true if the immediate prefix is
+ optional. */
- if ((reg = reg_required_here (&str, 0)) == FAIL)
+static int
+parse_immediate (char **str, int *val, int min, int max,
+ bfd_boolean prefix_opt)
+{
+ expressionS exp;
+ my_get_expression (&exp, str, prefix_opt ? GE_OPT_PREFIX : GE_IMM_PREFIX);
+ if (exp.X_op != O_constant)
{
- inst.error = BAD_ARGS;
- return;
+ inst.error = _("constant expression required");
+ return FAIL;
}
- /* Note - it is not illegal to do a "bxj pc". Useless, but not illegal. */
- if (reg == REG_PC)
- as_tsktsk (_("use of r15 in bxj is not really useful"));
+ if (exp.X_add_number < min || exp.X_add_number > max)
+ {
+ inst.error = _("immediate value out of range");
+ return FAIL;
+ }
- end_of_line (str);
+ *val = exp.X_add_number;
+ return SUCCESS;
}
-/* ARM V6 umaal (argument parse). */
+/* Returns the pseudo-register number of an FPA immediate constant,
+ or FAIL if there isn't a valid constant here. */
-static void
-do_umaal (char * str)
+static int
+parse_fpa_immediate (char ** str)
{
- int rdlo, rdhi, rm, rs;
+ LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ char * save_in;
+ expressionS exp;
+ int i;
+ int j;
- skip_whitespace (str);
- if ((rdlo = reg_required_here (& str, 12)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rdhi = reg_required_here (& str, 16)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rs = reg_required_here (& str, 8)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+ /* First try and match exact strings, this is to guarantee
+ that some formats will work even for cross assembly. */
- if (rdlo == REG_PC || rdhi == REG_PC || rm == REG_PC || rs == REG_PC)
+ for (i = 0; fp_const[i]; i++)
{
- inst.error = BAD_PC;
- return;
- }
+ if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
+ {
+ char *start = *str;
- end_of_line (str);
-}
+ *str += strlen (fp_const[i]);
+ if (is_end_of_line[(unsigned char) **str])
+ return i + 8;
+ *str = start;
+ }
+ }
-/* ARM V6 strex (argument parse). */
+ /* Just because we didn't get a match doesn't mean that the constant
+ isn't valid, just that it is in a format that we don't
+ automatically recognize. Try parsing it with the standard
+ expression routines. */
-static void
-do_strex (char * str)
-{
- int rd, rm, rn;
+ memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
- /* Parse Rd, Rm,. */
- skip_whitespace (str);
- if ((rd = reg_required_here (& str, 12)) == FAIL
- || skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL
- || skip_past_comma (& str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if (rd == REG_PC || rm == REG_PC)
+ /* Look for a raw floating point number. */
+ if ((save_in = atof_ieee (*str, 'x', words)) != NULL
+ && is_end_of_line[(unsigned char) *save_in])
{
- inst.error = BAD_PC;
- return;
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ {
+ for (j = 0; j < MAX_LITTLENUMS; j++)
+ {
+ if (words[j] != fp_values[i][j])
+ break;
+ }
+
+ if (j == MAX_LITTLENUMS)
+ {
+ *str = save_in;
+ return i + 8;
+ }
+ }
}
- if (rd == rm)
+
+ /* Try and parse a more complex expression, this will probably fail
+ unless the code uses a floating point prefix (eg "0f"). */
+ save_in = input_line_pointer;
+ input_line_pointer = *str;
+ if (expression (&exp) == absolute_section
+ && exp.X_op == O_big
+ && exp.X_add_number < 0)
{
- inst.error = _("Rd equal to Rm or Rn yields unpredictable results");
- return;
+ /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
+ Ditto for 15. */
+ if (gen_to_words (words, 5, (long) 15) == 0)
+ {
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ {
+ for (j = 0; j < MAX_LITTLENUMS; j++)
+ {
+ if (words[j] != fp_values[i][j])
+ break;
+ }
+
+ if (j == MAX_LITTLENUMS)
+ {
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ return i + 8;
+ }
+ }
+ }
}
- /* Skip past '['. */
- if ((strlen (str) >= 1)
- && strncmp (str, "[", 1) == 0)
- str += 1;
+ *str = input_line_pointer;
+ input_line_pointer = save_in;
+ inst.error = _("invalid FPA immediate expression");
+ return FAIL;
+}
- skip_whitespace (str);
+/* Shift operands. */
+enum shift_kind
+{
+ SHIFT_LSL, SHIFT_LSR, SHIFT_ASR, SHIFT_ROR, SHIFT_RRX
+};
- /* Parse Rn. */
- if ((rn = reg_required_here (& str, 16)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- else if (rn == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
- if (rd == rn)
- {
- inst.error = _("Rd equal to Rm or Rn yields unpredictable results");
- return;
- }
- skip_whitespace (str);
+struct asm_shift_name
+{
+ const char *name;
+ enum shift_kind kind;
+};
- /* Skip past ']'. */
- if ((strlen (str) >= 1)
- && strncmp (str, "]", 1) == 0)
- str += 1;
+/* Third argument to parse_shift. */
+enum parse_shift_mode
+{
+ NO_SHIFT_RESTRICT, /* Any kind of shift is accepted. */
+ SHIFT_IMMEDIATE, /* Shift operand must be an immediate. */
+ SHIFT_LSL_OR_ASR_IMMEDIATE, /* Shift must be LSL or ASR immediate. */
+ SHIFT_ASR_IMMEDIATE, /* Shift must be ASR immediate. */
+ SHIFT_LSL_IMMEDIATE, /* Shift must be LSL immediate. */
+};
- end_of_line (str);
-}
+/* Parse a <shift> specifier on an ARM data processing instruction.
+ This has three forms:
+
+ (LSL|LSR|ASL|ASR|ROR) Rs
+ (LSL|LSR|ASL|ASR|ROR) #imm
+ RRX
-/* KIND indicates what kind of shifts are accepted. */
+ Note that ASL is assimilated to LSL in the instruction encoding, and
+ RRX to ROR #0 (which cannot be written as such). */
static int
-decode_shift (char ** str, int kind)
+parse_shift (char **str, int i, enum parse_shift_mode mode)
{
- const struct asm_shift_name * shift;
- char * p;
- char c;
-
- skip_whitespace (* str);
+ const struct asm_shift_name *shift_name;
+ enum shift_kind shift;
+ char *s = *str;
+ char *p = s;
+ int reg;
- for (p = * str; ISALPHA (* p); p ++)
+ for (p = *str; ISALPHA (*p); p++)
;
- if (p == * str)
+ if (p == *str)
{
inst.error = _("shift expression expected");
return FAIL;
}
- c = * p;
- * p = '\0';
- shift = (const struct asm_shift_name *) hash_find (arm_shift_hsh, * str);
- * p = c;
+ shift_name = hash_find_n (arm_shift_hsh, *str, p - *str);
- if (shift == NULL)
+ if (shift_name == NULL)
{
inst.error = _("shift expression expected");
return FAIL;
}
- assert (shift->properties->index == shift_properties[shift->properties->index].index);
-
- if (kind == SHIFT_LSL_OR_ASR_IMMEDIATE
- && shift->properties->index != SHIFT_LSL
- && shift->properties->index != SHIFT_ASR)
- {
- inst.error = _("'LSL' or 'ASR' required");
- return FAIL;
- }
- else if (kind == SHIFT_LSL_IMMEDIATE
- && shift->properties->index != SHIFT_LSL)
- {
- inst.error = _("'LSL' required");
- return FAIL;
- }
- else if (kind == SHIFT_ASR_IMMEDIATE
- && shift->properties->index != SHIFT_ASR)
- {
- inst.error = _("'ASR' required");
- return FAIL;
- }
+ shift = shift_name->kind;
- if (shift->properties->index == SHIFT_RRX)
+ switch (mode)
{
- * str = p;
- inst.instruction |= shift->properties->bit_field;
- return SUCCESS;
- }
-
- skip_whitespace (p);
+ case NO_SHIFT_RESTRICT:
+ case SHIFT_IMMEDIATE: break;
- if (kind == NO_SHIFT_RESTRICT && reg_required_here (& p, 8) != FAIL)
- {
- inst.instruction |= shift->properties->bit_field | SHIFT_BY_REG;
- * str = p;
- return SUCCESS;
- }
- else if (! is_immediate_prefix (* p))
- {
- inst.error = (NO_SHIFT_RESTRICT
- ? _("shift requires register or #expression")
- : _("shift requires #expression"));
- * str = p;
- return FAIL;
- }
-
- inst.error = NULL;
- p ++;
-
- if (my_get_expression (& inst.reloc.exp, & p))
- return FAIL;
+ case SHIFT_LSL_OR_ASR_IMMEDIATE:
+ if (shift != SHIFT_LSL && shift != SHIFT_ASR)
+ {
+ inst.error = _("'LSL' or 'ASR' required");
+ return FAIL;
+ }
+ break;
- /* Validate some simple #expressions. */
- if (inst.reloc.exp.X_op == O_constant)
- {
- unsigned num = inst.reloc.exp.X_add_number;
-
- /* Reject operations greater than 32. */
- if (num > 32
- /* Reject a shift of 0 unless the mode allows it. */
- || (num == 0 && shift->properties->allows_0 == 0)
- /* Reject a shift of 32 unless the mode allows it. */
- || (num == 32 && shift->properties->allows_32 == 0)
- )
- {
- /* As a special case we allow a shift of zero for
- modes that do not support it to be recoded as an
- logical shift left of zero (ie nothing). We warn
- about this though. */
- if (num == 0)
- {
- as_warn (_("shift of 0 ignored."));
- shift = & shift_names[0];
- assert (shift->properties->index == SHIFT_LSL);
- }
- else
- {
- inst.error = _("invalid immediate shift");
- return FAIL;
- }
+ case SHIFT_LSL_IMMEDIATE:
+ if (shift != SHIFT_LSL)
+ {
+ inst.error = _("'LSL' required");
+ return FAIL;
}
+ break;
- /* Shifts of 32 are encoded as 0, for those shifts that
- support it. */
- if (num == 32)
- num = 0;
+ case SHIFT_ASR_IMMEDIATE:
+ if (shift != SHIFT_ASR)
+ {
+ inst.error = _("'ASR' required");
+ return FAIL;
+ }
+ break;
- inst.instruction |= (num << 7) | shift->properties->bit_field;
+ default: abort ();
}
- else
+
+ if (shift != SHIFT_RRX)
{
- inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
- inst.reloc.pc_rel = 0;
- inst.instruction |= shift->properties->bit_field;
- }
+ /* Whitespace can appear here if the next thing is a bare digit. */
+ skip_whitespace (p);
- * str = p;
+ if (mode == NO_SHIFT_RESTRICT
+ && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
+ }
+ else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
+ }
+ inst.operands[i].shift_kind = shift;
+ inst.operands[i].shifted = 1;
+ *str = p;
return SUCCESS;
}
-static void
-do_sat (char ** str, int bias)
-{
- int rd, rm;
- expressionS expr;
+/* Parse a <shifter_operand> for an ARM data processing instruction:
- skip_whitespace (*str);
+ #<immediate>
+ #<immediate>, <rotate>
+ <Rm>
+ <Rm>, <shift>
- /* Parse <Rd>, field. */
- if ((rd = reg_required_here (str, 12)) == FAIL
- || skip_past_comma (str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ where <shift> is defined by parse_shift above, and <rotate> is a
+ multiple of 2 between 0 and 30. Validation of immediate operands
+ is deferred to md_apply_fix3. */
- /* Parse #<immed>, field. */
- if (is_immediate_prefix (**str))
- (*str)++;
- else
- {
- inst.error = _("immediate expression expected");
- return;
- }
- if (my_get_expression (&expr, str))
- {
- inst.error = _("bad expression");
- return;
- }
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return;
- }
- if (expr.X_add_number + bias < 0
- || expr.X_add_number + bias > 31)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction |= (expr.X_add_number + bias) << 16;
- if (skip_past_comma (str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+static int
+parse_shifter_operand (char **str, int i)
+{
+ int value;
+ expressionS expr;
- /* Parse <Rm> field. */
- if ((rm = reg_required_here (str, 0)) == FAIL)
+ if ((value = arm_reg_parse (str, REG_TYPE_RN)) != FAIL)
{
- inst.error = BAD_ARGS;
- return;
- }
- if (rm == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ inst.operands[i].reg = value;
+ inst.operands[i].isreg = 1;
- if (skip_past_comma (str) == SUCCESS)
- decode_shift (str, SHIFT_LSL_OR_ASR_IMMEDIATE);
-}
-
-/* ARM V6 ssat (argument parse). */
+ /* parse_shift will override this if appropriate */
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
-static void
-do_ssat (char * str)
-{
- do_sat (&str, /*bias=*/-1);
- end_of_line (str);
-}
+ if (skip_past_comma (str) == FAIL)
+ return SUCCESS;
-/* ARM V6 usat (argument parse). */
+ /* Shift operation on register. */
+ return parse_shift (str, i, NO_SHIFT_RESTRICT);
+ }
-static void
-do_usat (char * str)
-{
- do_sat (&str, /*bias=*/0);
- end_of_line (str);
-}
+ if (my_get_expression (&inst.reloc.exp, str, GE_IMM_PREFIX))
+ return FAIL;
-static void
-do_sat16 (char ** str, int bias)
-{
- int rd, rm;
- expressionS expr;
+ if (skip_past_comma (str) == SUCCESS)
+ {
+ /* #x, y -- ie explicit rotation by Y. */
+ if (my_get_expression (&expr, str, GE_NO_PREFIX))
+ return FAIL;
- skip_whitespace (*str);
+ if (expr.X_op != O_constant || inst.reloc.exp.X_op != O_constant)
+ {
+ inst.error = _("constant expression expected");
+ return FAIL;
+ }
- /* Parse the <Rd> field. */
- if ((rd = reg_required_here (str, 12)) == FAIL
- || skip_past_comma (str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ value = expr.X_add_number;
+ if (value < 0 || value > 30 || value % 2 != 0)
+ {
+ inst.error = _("invalid rotation");
+ return FAIL;
+ }
+ if (inst.reloc.exp.X_add_number < 0 || inst.reloc.exp.X_add_number > 255)
+ {
+ inst.error = _("invalid constant");
+ return FAIL;
+ }
- /* Parse #<immed>, field. */
- if (is_immediate_prefix (**str))
- (*str)++;
- else
- {
- inst.error = _("immediate expression expected");
- return;
- }
- if (my_get_expression (&expr, str))
- {
- inst.error = _("bad expression");
- return;
- }
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return;
- }
- if (expr.X_add_number + bias < 0
- || expr.X_add_number + bias > 15)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction |= (expr.X_add_number + bias) << 16;
- if (skip_past_comma (str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
+ /* Convert to decoded value. md_apply_fix3 will put it back. */
+ inst.reloc.exp.X_add_number
+ = (((inst.reloc.exp.X_add_number << (32 - value))
+ | (inst.reloc.exp.X_add_number >> value)) & 0xffffffff);
}
- /* Parse <Rm> field. */
- if ((rm = reg_required_here (str, 0)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if (rm == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
+ return SUCCESS;
}
-/* ARM V6 ssat16 (argument parse). */
+/* Parse all forms of an ARM address expression. Information is written
+ to inst.operands[i] and/or inst.reloc.
-static void
-do_ssat16 (char * str)
-{
- do_sat16 (&str, /*bias=*/-1);
- end_of_line (str);
-}
+ Preindexed addressing (.preind=1):
-static void
-do_usat16 (char * str)
-{
- do_sat16 (&str, /*bias=*/0);
- end_of_line (str);
-}
+ [Rn, #offset] .reg=Rn .reloc.exp=offset
+ [Rn, +/-Rm] .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ [Rn, +/-Rm, shift] .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ .shift_kind=shift .reloc.exp=shift_imm
-static void
-do_cps_mode (char ** str)
-{
- expressionS expr;
+ These three may have a trailing ! which causes .writeback to be set also.
- skip_whitespace (*str);
+ Postindexed addressing (.postind=1, .writeback=1):
- if (! is_immediate_prefix (**str))
- {
- inst.error = _("immediate expression expected");
- return;
- }
+ [Rn], #offset .reg=Rn .reloc.exp=offset
+ [Rn], +/-Rm .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ [Rn], +/-Rm, shift .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ .shift_kind=shift .reloc.exp=shift_imm
- (*str)++; /* Strip off the immediate signifier. */
- if (my_get_expression (&expr, str))
- {
- inst.error = _("bad expression");
- return;
- }
+ Unindexed addressing (.preind=0, .postind=0):
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return;
- }
+ [Rn], {option} .reg=Rn .imm=option .immisreg=0
- /* The mode is a 5 bit field. Valid values are 0-31. */
- if (((unsigned) expr.X_add_number) > 31
- || (inst.reloc.exp.X_add_number) < 0)
- {
- inst.error = _("invalid constant");
- return;
- }
+ Other:
- inst.instruction |= expr.X_add_number;
-}
+ [Rn]{!} shorthand for [Rn,#0]{!}
+ =immediate .isreg=0 .reloc.exp=immediate
+ label .reg=PC .reloc.pc_rel=1 .reloc.exp=label
-/* ARM V6 srs (argument parse). */
+ It is the caller's responsibility to check for addressing modes not
+ supported by the instruction, and to set inst.reloc.type. */
-static void
-do_srs (char * str)
+static int
+parse_address (char **str, int i)
{
- char *exclam;
- skip_whitespace (str);
- exclam = strchr (str, '!');
- if (exclam)
- *exclam = '\0';
- do_cps_mode (&str);
- if (exclam)
- *exclam = '!';
- if (*str == '!')
+ char *p = *str;
+ int reg;
+
+ if (skip_past_char (&p, '[') == FAIL)
{
- inst.instruction |= WRITE_BACK;
- str++;
- }
- end_of_line (str);
-}
+ if (skip_past_char (&p, '=') == FAIL)
+ {
+ /* bare address - translate to PC-relative offset */
+ inst.reloc.pc_rel = 1;
+ inst.operands[i].reg = REG_PC;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].preind = 1;
+ }
+ /* else a load-constant pseudo op, no special treatment needed here */
-/* ARM V6 SMMUL (argument parse). */
+ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+ return FAIL;
-static void
-do_smmul (char * str)
-{
- int rd, rm, rs;
+ *str = p;
+ return SUCCESS;
+ }
- skip_whitespace (str);
- if ((rd = reg_required_here (&str, 16)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rs = reg_required_here (&str, 8)) == FAIL)
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
{
- inst.error = BAD_ARGS;
- return;
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return FAIL;
}
+ inst.operands[i].reg = reg;
+ inst.operands[i].isreg = 1;
- if ( rd == REG_PC
- || rm == REG_PC
- || rs == REG_PC)
+ if (skip_past_comma (&p) == SUCCESS)
{
- inst.error = BAD_PC;
- return;
- }
+ inst.operands[i].preind = 1;
- end_of_line (str);
-}
+ if (*p == '+') p++;
+ else if (*p == '-') p++, inst.operands[i].negative = 1;
-/* ARM V6 SMLALD (argument parse). */
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
-static void
-do_smlald (char * str)
-{
- int rdlo, rdhi, rm, rs;
+ if (skip_past_comma (&p) == SUCCESS)
+ if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+ return FAIL;
+ }
+ else
+ {
+ if (inst.operands[i].negative)
+ {
+ inst.operands[i].negative = 0;
+ p--;
+ }
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
+ }
+ }
- skip_whitespace (str);
- if ((rdlo = reg_required_here (&str, 12)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rdhi = reg_required_here (&str, 16)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rs = reg_required_here (&str, 8)) == FAIL)
+ if (skip_past_char (&p, ']') == FAIL)
{
- inst.error = BAD_ARGS;
- return;
+ inst.error = _("']' expected");
+ return FAIL;
}
- if ( rdlo == REG_PC
- || rdhi == REG_PC
- || rm == REG_PC
- || rs == REG_PC)
+ if (skip_past_char (&p, '!') == SUCCESS)
+ inst.operands[i].writeback = 1;
+
+ else if (skip_past_comma (&p) == SUCCESS)
{
- inst.error = BAD_PC;
- return;
- }
+ if (skip_past_char (&p, '{') == SUCCESS)
+ {
+ /* [Rn], {expr} - unindexed, with option */
+ if (parse_immediate (&p, &inst.operands[i].imm,
+ 0, 255, TRUE) == FAIL)
+ return FAIL;
- end_of_line (str);
-}
+ if (skip_past_char (&p, '}') == FAIL)
+ {
+ inst.error = _("'}' expected at end of 'option' field");
+ return FAIL;
+ }
+ if (inst.operands[i].preind)
+ {
+ inst.error = _("cannot combine index with option");
+ return FAIL;
+ }
+ *str = p;
+ return SUCCESS;
+ }
+ else
+ {
+ inst.operands[i].postind = 1;
+ inst.operands[i].writeback = 1;
-/* ARM V6 SMLAD (argument parse). Signed multiply accumulate dual.
- smlad{x}{<cond>} Rd, Rm, Rs, Rn */
+ if (inst.operands[i].preind)
+ {
+ inst.error = _("cannot combine pre- and post-indexing");
+ return FAIL;
+ }
-static void
-do_smlad (char * str)
-{
- int rd, rm, rs, rn;
+ if (*p == '+') p++;
+ else if (*p == '-') p++, inst.operands[i].negative = 1;
- skip_whitespace (str);
- if ((rd = reg_required_here (&str, 16)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rs = reg_required_here (&str, 8)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rn = reg_required_here (&str, 12)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
+
+ if (skip_past_comma (&p) == SUCCESS)
+ if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+ return FAIL;
+ }
+ else
+ {
+ if (inst.operands[i].negative)
+ {
+ inst.operands[i].negative = 0;
+ p--;
+ }
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
+ }
+ }
}
- if ( rd == REG_PC
- || rn == REG_PC
- || rs == REG_PC
- || rm == REG_PC)
+ /* If at this point neither .preind nor .postind is set, we have a
+ bare [Rn]{!}, which is shorthand for [Rn,#0]{!}. */
+ if (inst.operands[i].preind == 0 && inst.operands[i].postind == 0)
{
- inst.error = BAD_PC;
- return;
+ inst.operands[i].preind = 1;
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
}
-
- end_of_line (str);
+ *str = p;
+ return SUCCESS;
}
-/* Returns true if the endian-specifier indicates big-endianness. */
+/* Miscellaneous. */
+/* Parse a PSR flag operand. The value returned is FAIL on syntax error,
+ or a bitmask suitable to be or-ed into the ARM msr instruction. */
static int
-do_endian_specifier (char * str)
+parse_psr (char **str)
{
- int big_endian = 0;
+ char *p;
+ unsigned long psr_field;
- skip_whitespace (str);
- if (strlen (str) < 2)
- inst.error = _("missing endian specifier");
- else if (strncasecmp (str, "BE", 2) == 0)
- {
- str += 2;
- big_endian = 1;
- }
- else if (strncasecmp (str, "LE", 2) == 0)
- str += 2;
+ /* CPSR's and SPSR's can now be lowercase. This is just a convenience
+ feature for ease of use and backwards compatibility. */
+ p = *str;
+ if (*p == 's' || *p == 'S')
+ psr_field = SPSR_BIT;
+ else if (*p == 'c' || *p == 'C')
+ psr_field = 0;
else
- inst.error = _("valid endian specifiers are be or le");
-
- end_of_line (str);
-
- return big_endian;
-}
-
-/* ARM V6 SETEND (argument parse). Sets the E bit in the CPSR while
- preserving the other bits.
-
- setend <endian_specifier>, where <endian_specifier> is either
- BE or LE. */
-
-static void
-do_setend (char * str)
-{
- if (do_endian_specifier (str))
- inst.instruction |= 0x200;
-}
+ goto error;
-/* ARM V6 SXTH.
-
- SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
- Condition defaults to COND_ALWAYS.
- Error if any register uses R15. */
-
-static void
-do_sxth (char * str)
-{
- int rd, rm;
- expressionS expr;
- int rotation_clear_mask = 0xfffff3ff;
- int rotation_eight_mask = 0x00000400;
- int rotation_sixteen_mask = 0x00000800;
- int rotation_twenty_four_mask = 0x00000c00;
+ p++;
+ if (strncasecmp (p, "PSR", 3) != 0)
+ goto error;
+ p += 3;
- skip_whitespace (str);
- if ((rd = reg_required_here (&str, 12)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
+ if (*p == '_')
{
- inst.error = BAD_ARGS;
- return;
- }
+ /* A suffix follows. */
+ const struct asm_psr *psr;
+ char *start;
- else if (rd == REG_PC || rm == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ p++;
+ start = p;
- /* Zero out the rotation field. */
- inst.instruction &= rotation_clear_mask;
+ do
+ p++;
+ while (ISALNUM (*p) || *p == '_');
- /* Check for lack of optional rotation field. */
- if (skip_past_comma (&str) == FAIL)
- {
- end_of_line (str);
- return;
- }
+ psr = hash_find_n (arm_psr_hsh, start, p - start);
+ if (!psr)
+ goto error;
- /* Move past 'ROR'. */
- skip_whitespace (str);
- if (strncasecmp (str, "ROR", 3) == 0)
- str += 3;
- else
- {
- inst.error = _("missing rotation field after comma");
- return;
+ psr_field |= psr->field;
}
-
- /* Get the immediate constant. */
- skip_whitespace (str);
- if (is_immediate_prefix (* str))
- str++;
else
{
- inst.error = _("immediate expression expected");
- return;
- }
+ if (ISALNUM (*p))
+ goto error; /* Garbage after "[CS]PSR". */
- if (my_get_expression (&expr, &str))
- {
- inst.error = _("bad expression");
- return;
+ psr_field |= (PSR_c | PSR_f);
}
+ *str = p;
+ return psr_field;
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return;
- }
+ error:
+ inst.error = _("flag for {c}psr instruction expected");
+ return FAIL;
+}
- switch (expr.X_add_number)
- {
- case 0:
- /* Rotation field has already been zeroed. */
- break;
- case 8:
- inst.instruction |= rotation_eight_mask;
- break;
+/* Parse the flags argument to CPSI[ED]. Returns FAIL on error, or a
+ value suitable for splatting into the AIF field of the instruction. */
- case 16:
- inst.instruction |= rotation_sixteen_mask;
- break;
+static int
+parse_cps_flags (char **str)
+{
+ int val = 0;
+ int saw_a_flag = 0;
+ char *s = *str;
- case 24:
- inst.instruction |= rotation_twenty_four_mask;
- break;
+ for (;;)
+ switch (*s++)
+ {
+ case '\0': case ',':
+ goto done;
- default:
- inst.error = _("rotation can be 8, 16, 24 or 0 when field is ommited");
- break;
+ case 'a': case 'A': saw_a_flag = 1; val |= 0x4; break;
+ case 'i': case 'I': saw_a_flag = 1; val |= 0x2; break;
+ case 'f': case 'F': saw_a_flag = 1; val |= 0x1; break;
+
+ default:
+ inst.error = _("unrecognized CPS flag");
+ return FAIL;
+ }
+
+ done:
+ if (saw_a_flag == 0)
+ {
+ inst.error = _("missing CPS flags");
+ return FAIL;
}
- end_of_line (str);
+ *str = s - 1;
+ return val;
}
-/* ARM V6 SXTAH extracts a 16-bit value from a register, sign
- extends it to 32-bits, and adds the result to a value in another
- register. You can specify a rotation by 0, 8, 16, or 24 bits
- before extracting the 16-bit value.
- SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
- Condition defaults to COND_ALWAYS.
- Error if any register uses R15. */
+/* Parse an endian specifier ("BE" or "LE", case insensitive);
+ returns 0 for big-endian, 1 for little-endian, FAIL for an error. */
-static void
-do_sxtah (char * str)
+static int
+parse_endian_specifier (char **str)
{
- int rd, rn, rm;
- expressionS expr;
- int rotation_clear_mask = 0xfffff3ff;
- int rotation_eight_mask = 0x00000400;
- int rotation_sixteen_mask = 0x00000800;
- int rotation_twenty_four_mask = 0x00000c00;
+ int little_endian;
+ char *s = *str;
- skip_whitespace (str);
- if ((rd = reg_required_here (&str, 12)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rn = reg_required_here (&str, 16)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
+ if (strncasecmp (s, "BE", 2))
+ little_endian = 0;
+ else if (strncasecmp (s, "LE", 2))
+ little_endian = 1;
+ else
{
- inst.error = BAD_ARGS;
- return;
+ inst.error = _("valid endian specifiers are be or le");
+ return FAIL;
}
- else if (rd == REG_PC || rn == REG_PC || rm == REG_PC)
+ if (ISALNUM (s[2]) || s[2] == '_')
{
- inst.error = BAD_PC;
- return;
+ inst.error = _("valid endian specifiers are be or le");
+ return FAIL;
}
- /* Zero out the rotation field. */
- inst.instruction &= rotation_clear_mask;
+ *str = s + 2;
+ return little_endian;
+}
- /* Check for lack of optional rotation field. */
- if (skip_past_comma (&str) == FAIL)
- {
- end_of_line (str);
- return;
- }
+/* Parse a rotation specifier: ROR #0, #8, #16, #24. *val receives a
+ value suitable for poking into the rotate field of an sxt or sxta
+ instruction, or FAIL on error. */
- /* Move past 'ROR'. */
- skip_whitespace (str);
- if (strncasecmp (str, "ROR", 3) == 0)
- str += 3;
- else
- {
- inst.error = _("missing rotation field after comma");
- return;
- }
+static int
+parse_ror (char **str)
+{
+ int rot;
+ char *s = *str;
- /* Get the immediate constant. */
- skip_whitespace (str);
- if (is_immediate_prefix (* str))
- str++;
+ if (strncasecmp (s, "ROR", 3) == 0)
+ s += 3;
else
{
- inst.error = _("immediate expression expected");
- return;
- }
-
- if (my_get_expression (&expr, &str))
- {
- inst.error = _("bad expression");
- return;
+ inst.error = _("missing rotation field after comma");
+ return FAIL;
}
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return;
- }
+ if (parse_immediate (&s, &rot, 0, 24, FALSE) == FAIL)
+ return FAIL;
- switch (expr.X_add_number)
+ switch (rot)
{
- case 0:
- /* Rotation field has already been zeroed. */
- break;
-
- case 8:
- inst.instruction |= rotation_eight_mask;
- break;
-
- case 16:
- inst.instruction |= rotation_sixteen_mask;
- break;
-
- case 24:
- inst.instruction |= rotation_twenty_four_mask;
- break;
+ case 0: *str = s; return 0x0;
+ case 8: *str = s; return 0x1;
+ case 16: *str = s; return 0x2;
+ case 24: *str = s; return 0x3;
default:
- inst.error = _("rotation can be 8, 16, 24 or 0 when field is ommited");
- break;
+ inst.error = _("rotation can only be 0, 8, 16, or 24");
+ return FAIL;
}
-
- end_of_line (str);
}
-
-/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
- word at the specified address and the following word
- respectively.
- Unconditionally executed.
- Error if Rn is R15. */
-
-static void
-do_rfe (char * str)
+/* Parse a conditional code (from conds[] below). The value returned is in the
+ range 0 .. 14, or FAIL. */
+static int
+parse_cond (char **str)
{
- int rn;
+ char *p, *q;
+ const struct asm_cond *c;
- skip_whitespace (str);
+ p = q = *str;
+ while (ISALPHA (*q))
+ q++;
- if ((rn = reg_required_here (&str, 16)) == FAIL)
- return;
-
- if (rn == REG_PC)
+ c = hash_find_n (arm_cond_hsh, p, q - p);
+ if (!c)
{
- inst.error = BAD_PC;
- return;
+ inst.error = _("condition required");
+ return FAIL;
}
- skip_whitespace (str);
-
- if (*str == '!')
- {
- inst.instruction |= WRITE_BACK;
- str++;
- }
- end_of_line (str);
-}
+ *str = q;
+ return c->value;
+}
+
+/* Matcher codes for parse_operands. */
+enum operand_parse_code
+{
+ OP_stop, /* end of line */
+
+ OP_RR, /* ARM register */
+ OP_RRnpc, /* ARM register, not r15 */
+ OP_RRnpcb, /* ARM register, not r15, in square brackets */
+ OP_RRw, /* ARM register, not r15, optional trailing ! */
+ OP_RCP, /* Coprocessor number */
+ OP_RCN, /* Coprocessor register */
+ OP_RF, /* FPA register */
+ OP_RVS, /* VFP single precision register */
+ OP_RVD, /* VFP double precision register */
+ OP_RVC, /* VFP control register */
+ OP_RMF, /* Maverick F register */
+ OP_RMD, /* Maverick D register */
+ OP_RMFX, /* Maverick FX register */
+ OP_RMDX, /* Maverick DX register */
+ OP_RMAX, /* Maverick AX register */
+ OP_RMDS, /* Maverick DSPSC register */
+ OP_RIWR, /* iWMMXt wR register */
+ OP_RIWC, /* iWMMXt wC register */
+ OP_RIWG, /* iWMMXt wCG register */
+ OP_RXA, /* XScale accumulator register */
+
+ OP_REGLST, /* ARM register list */
+ OP_VRSLST, /* VFP single-precision register list */
+ OP_VRDLST, /* VFP double-precision register list */
+
+ OP_I7, /* immediate value 0 .. 7 */
+ OP_I15, /* 0 .. 15 */
+ OP_I16, /* 1 .. 16 */
+ OP_I31, /* 0 .. 31 */
+ OP_I31w, /* 0 .. 31, optional trailing ! */
+ OP_I32, /* 1 .. 32 */
+ OP_I63s, /* -64 .. 63 */
+ OP_I255, /* 0 .. 255 */
+ OP_Iffff, /* 0 .. 65535 */
+
+ OP_I4b, /* immediate, prefix optional, 1 .. 4 */
+ OP_I7b, /* 0 .. 7 */
+ OP_I15b, /* 0 .. 15 */
+ OP_I31b, /* 0 .. 31 */
+
+ OP_SH, /* shifter operand */
+ OP_ADDR, /* Memory address expression (any mode) */
+ OP_EXP, /* arbitrary expression */
+ OP_EXPi, /* same, with optional immediate prefix */
+ OP_EXPr, /* same, with optional relocation suffix */
+
+ OP_CPSF, /* CPS flags */
+ OP_ENDI, /* Endianness specifier */
+ OP_PSR, /* CPSR/SPSR mask for msr */
+ OP_COND, /* conditional code */
+
+ OP_RRnpc_I0, /* ARM register or literal 0 */
+ OP_RR_EXr, /* ARM register or expression with opt. reloc suff. */
+ OP_RR_EXi, /* ARM register or expression with imm prefix */
+ OP_RF_IF, /* FPA register or immediate */
+ OP_RIWR_RIWC, /* iWMMXt R or C reg */
+
+ /* Optional operands. */
+ OP_oI7b, /* immediate, prefix optional, 0 .. 7 */
+ OP_oI31b, /* 0 .. 31 */
+ OP_oIffffb, /* 0 .. 65535 */
+ OP_oI255c, /* curly-brace enclosed, 0 .. 255 */
+
+ OP_oRR, /* ARM register */
+ OP_oRRnpc, /* ARM register, not the PC */
+ OP_oSHll, /* LSL immediate */
+ OP_oSHar, /* ASR immediate */
+ OP_oSHllar, /* LSL or ASR immediate */
+ OP_oROR, /* ROR 0/8/16/24 */
+
+ OP_FIRST_OPTIONAL = OP_oI7b
+};
-/* ARM V6 REV (Byte Reverse Word) reverses the byte order in a 32-bit
- register (argument parse).
- REV{<cond>} Rd, Rm.
- Condition defaults to COND_ALWAYS.
- Error if Rd or Rm are R15. */
+/* Generic instruction operand parser. This does no encoding and no
+ semantic validation; it merely squirrels values away in the inst
+ structure. Returns SUCCESS or FAIL depending on whether the
+ specified grammar matched. */
+static int
+parse_operands (char *str, const char *pattern)
+{
+ unsigned const char *upat = pattern;
+ char *backtrack_pos = 0;
+ const char *backtrack_error = 0;
+ int i, val, backtrack_index = 0;
+
+#define po_char_or_fail(chr) do { \
+ if (skip_past_char (&str, chr) == FAIL) \
+ goto bad_args; \
+} while (0)
+
+#define po_reg_or_fail(regtype) do { \
+ val = arm_reg_parse (&str, regtype); \
+ if (val == FAIL) \
+ { \
+ inst.error = _(reg_expected_msgs[regtype]); \
+ goto failure; \
+ } \
+ inst.operands[i].reg = val; \
+ inst.operands[i].isreg = 1; \
+} while (0)
+
+#define po_reg_or_goto(regtype, label) do { \
+ val = arm_reg_parse (&str, regtype); \
+ if (val == FAIL) \
+ goto label; \
+ \
+ inst.operands[i].reg = val; \
+ inst.operands[i].isreg = 1; \
+} while (0)
+
+#define po_imm_or_fail(min, max, popt) do { \
+ if (parse_immediate (&str, &val, min, max, popt) == FAIL) \
+ goto failure; \
+ inst.operands[i].imm = val; \
+} while (0)
+
+#define po_misc_or_fail(expr) do { \
+ if (expr) \
+ goto failure; \
+} while (0)
+
+ skip_whitespace (str);
+
+ for (i = 0; upat[i] != OP_stop; i++)
+ {
+ if (upat[i] >= OP_FIRST_OPTIONAL)
+ {
+ /* Remember where we are in case we need to backtrack. */
+ assert (!backtrack_pos);
+ backtrack_pos = str;
+ backtrack_error = inst.error;
+ backtrack_index = i;
+ }
+
+ if (i > 0)
+ po_char_or_fail (',');
+
+ switch (upat[i])
+ {
+ /* Registers */
+ case OP_oRRnpc:
+ case OP_RRnpc:
+ case OP_oRR:
+ case OP_RR: po_reg_or_fail (REG_TYPE_RN); break;
+ case OP_RCP: po_reg_or_fail (REG_TYPE_CP); break;
+ case OP_RCN: po_reg_or_fail (REG_TYPE_CN); break;
+ case OP_RF: po_reg_or_fail (REG_TYPE_FN); break;
+ case OP_RVS: po_reg_or_fail (REG_TYPE_VFS); break;
+ case OP_RVD: po_reg_or_fail (REG_TYPE_VFD); break;
+ case OP_RVC: po_reg_or_fail (REG_TYPE_VFC); break;
+ case OP_RMF: po_reg_or_fail (REG_TYPE_MVF); break;
+ case OP_RMD: po_reg_or_fail (REG_TYPE_MVD); break;
+ case OP_RMFX: po_reg_or_fail (REG_TYPE_MVFX); break;
+ case OP_RMDX: po_reg_or_fail (REG_TYPE_MVDX); break;
+ case OP_RMAX: po_reg_or_fail (REG_TYPE_MVAX); break;
+ case OP_RMDS: po_reg_or_fail (REG_TYPE_DSPSC); break;
+ case OP_RIWR: po_reg_or_fail (REG_TYPE_MMXWR); break;
+ case OP_RIWC: po_reg_or_fail (REG_TYPE_MMXWC); break;
+ case OP_RIWG: po_reg_or_fail (REG_TYPE_MMXWCG); break;
+ case OP_RXA: po_reg_or_fail (REG_TYPE_XSCALE); break;
+
+ case OP_RRnpcb:
+ po_char_or_fail ('[');
+ po_reg_or_fail (REG_TYPE_RN);
+ po_char_or_fail (']');
+ break;
-static void
-do_rev (char * str)
-{
- int rd, rm;
+ case OP_RRw:
+ po_reg_or_fail (REG_TYPE_RN);
+ if (skip_past_char (&str, '!') == SUCCESS)
+ inst.operands[i].writeback = 1;
+ break;
- skip_whitespace (str);
+ /* Immediates */
+ case OP_I7: po_imm_or_fail ( 0, 7, FALSE); break;
+ case OP_I15: po_imm_or_fail ( 0, 15, FALSE); break;
+ case OP_I16: po_imm_or_fail ( 1, 16, FALSE); break;
+ case OP_I31: po_imm_or_fail ( 0, 31, FALSE); break;
+ case OP_I32: po_imm_or_fail ( 1, 32, FALSE); break;
+ case OP_I63s: po_imm_or_fail (-64, 63, FALSE); break;
+ case OP_I255: po_imm_or_fail ( 0, 255, FALSE); break;
+ case OP_Iffff: po_imm_or_fail ( 0, 0xffff, FALSE); break;
+
+ case OP_I4b: po_imm_or_fail ( 1, 4, TRUE); break;
+ case OP_oI7b:
+ case OP_I7b: po_imm_or_fail ( 0, 7, TRUE); break;
+ case OP_I15b: po_imm_or_fail ( 0, 15, TRUE); break;
+ case OP_oI31b:
+ case OP_I31b: po_imm_or_fail ( 0, 31, TRUE); break;
+ case OP_oIffffb: po_imm_or_fail ( 0, 0xffff, TRUE); break;
+
+ /* Immediate variants */
+ case OP_oI255c:
+ po_char_or_fail ('{');
+ po_imm_or_fail (0, 255, TRUE);
+ po_char_or_fail ('}');
+ break;
- if ((rd = reg_required_here (&str, 12)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
- inst.error = BAD_ARGS;
+ case OP_I31w:
+ /* The expression parser chokes on a trailing !, so we have
+ to find it first and zap it. */
+ {
+ char *s = str;
+ while (*s && *s != ',')
+ s++;
+ if (s[-1] == '!')
+ {
+ s[-1] = '\0';
+ inst.operands[i].writeback = 1;
+ }
+ po_imm_or_fail (0, 31, TRUE);
+ if (str == s - 1)
+ str = s;
+ }
+ break;
- else if (rd == REG_PC || rm == REG_PC)
- inst.error = BAD_PC;
+ /* Expressions */
+ case OP_EXPi: EXPi:
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_OPT_PREFIX));
+ break;
- else
- end_of_line (str);
-}
+ case OP_EXP:
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_NO_PREFIX));
+ break;
-/* ARM V6 Perform Two Sixteen Bit Integer Additions. (argument parse).
- QADD16{<cond>} <Rd>, <Rn>, <Rm>
- Condition defaults to COND_ALWAYS.
- Error if Rd, Rn or Rm are R15. */
+ case OP_EXPr: EXPr:
+ po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
+ GE_NO_PREFIX));
+ if (inst.reloc.exp.X_op == O_symbol)
+ {
+ val = parse_reloc (&str);
+ if (val == -1)
+ {
+ inst.error = _("unrecognized relocation suffix");
+ goto failure;
+ }
+ else if (val != BFD_RELOC_UNUSED)
+ {
+ inst.operands[i].imm = val;
+ inst.operands[i].hasreloc = 1;
+ }
+ }
+ break;
-static void
-do_qadd16 (char * str)
-{
- int rd, rm, rn;
+ /* Register or expression */
+ case OP_RR_EXr: po_reg_or_goto (REG_TYPE_RN, EXPr); break;
+ case OP_RR_EXi: po_reg_or_goto (REG_TYPE_RN, EXPi); break;
- skip_whitespace (str);
+ /* Register or immediate */
+ case OP_RRnpc_I0: po_reg_or_goto (REG_TYPE_RN, I0); break;
+ I0: po_imm_or_fail (0, 0, FALSE); break;
- if ((rd = reg_required_here (&str, 12)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rn = reg_required_here (&str, 16)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (rm = reg_required_here (&str, 0)) == FAIL)
- inst.error = BAD_ARGS;
+ case OP_RF_IF: po_reg_or_goto (REG_TYPE_FN, IF); break;
+ IF:
+ if (!is_immediate_prefix (*str))
+ goto bad_args;
+ str++;
+ val = parse_fpa_immediate (&str);
+ if (val == FAIL)
+ goto failure;
+ /* FPA immediates are encoded as registers 8-15.
+ parse_fpa_immediate has already applied the offset. */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ break;
- else if (rd == REG_PC || rm == REG_PC || rn == REG_PC)
- inst.error = BAD_PC;
+ /* Two kinds of register */
+ case OP_RIWR_RIWC:
+ {
+ struct reg_entry *rege = arm_reg_parse_multi (&str);
+ if (rege->type != REG_TYPE_MMXWR
+ && rege->type != REG_TYPE_MMXWC
+ && rege->type != REG_TYPE_MMXWCG)
+ {
+ inst.error = _("iWMMXt data or control register expected");
+ goto failure;
+ }
+ inst.operands[i].reg = rege->number;
+ inst.operands[i].isreg = (rege->type == REG_TYPE_MMXWR);
+ }
+ break;
- else
- end_of_line (str);
-}
+ /* Misc */
+ case OP_CPSF: val = parse_cps_flags (&str); break;
+ case OP_ENDI: val = parse_endian_specifier (&str); break;
+ case OP_oROR: val = parse_ror (&str); break;
+ case OP_PSR: val = parse_psr (&str); break;
+ case OP_COND: val = parse_cond (&str); break;
+
+ /* Register lists */
+ case OP_REGLST:
+ val = parse_reg_list (&str);
+ if (*str == '^')
+ {
+ inst.operands[1].writeback = 1;
+ str++;
+ }
+ break;
-static void
-do_pkh_core (char * str, int shift)
-{
- int rd, rn, rm;
+ case OP_VRSLST:
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg, 0);
+ break;
- skip_whitespace (str);
- if (((rd = reg_required_here (&str, 12)) == FAIL)
- || (skip_past_comma (&str) == FAIL)
- || ((rn = reg_required_here (&str, 16)) == FAIL)
- || (skip_past_comma (&str) == FAIL)
- || ((rm = reg_required_here (&str, 0)) == FAIL))
- {
- inst.error = BAD_ARGS;
- return;
- }
+ case OP_VRDLST:
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg, 1);
+ break;
- else if (rd == REG_PC || rn == REG_PC || rm == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ /* Addressing modes */
+ case OP_ADDR:
+ po_misc_or_fail (parse_address (&str, i));
+ break;
- /* Check for optional shift immediate constant. */
- if (skip_past_comma (&str) == FAIL)
- {
- if (shift == SHIFT_ASR_IMMEDIATE)
- {
- /* If the shift specifier is ommited, turn the instruction
- into pkhbt rd, rm, rn. First, switch the instruction
- code, and clear the rn and rm fields. */
- inst.instruction &= 0xfff0f010;
- /* Now, re-encode the registers. */
- inst.instruction |= (rm << 16) | rn;
- }
- return;
- }
+ case OP_SH:
+ po_misc_or_fail (parse_shifter_operand (&str, i));
+ break;
- decode_shift (&str, shift);
-}
+ case OP_oSHll:
+ po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE));
+ break;
-/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
- PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
- Condition defaults to COND_ALWAYS.
- Error if Rd, Rn or Rm are R15. */
+ case OP_oSHar:
+ po_misc_or_fail (parse_shift (&str, i, SHIFT_ASR_IMMEDIATE));
+ break;
-static void
-do_pkhbt (char * str)
-{
- do_pkh_core (str, SHIFT_LSL_IMMEDIATE);
-}
+ case OP_oSHllar:
+ po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_OR_ASR_IMMEDIATE));
+ break;
-/* ARM V6 PKHTB (Argument Parse). */
+ default:
+ as_fatal ("unhandled operand code %d", upat[i]);
+ }
-static void
-do_pkhtb (char * str)
-{
- do_pkh_core (str, SHIFT_ASR_IMMEDIATE);
-}
+ /* Various value-based sanity checks and shared operations. We
+ do not signal immediate failures for the register constraints;
+ this allows a syntax error to take precedence. */
+ switch (upat[i])
+ {
+ case OP_oRRnpc:
+ case OP_RRnpc:
+ case OP_RRnpcb:
+ case OP_RRw:
+ case OP_RRnpc_I0:
+ if (inst.operands[i].isreg && inst.operands[i].reg == REG_PC)
+ inst.error = BAD_PC;
+ break;
-/* ARM V6 Load Register Exclusive instruction (argument parse).
- LDREX{,B,D,H}{<cond>} <Rd, [<Rn>]
- Condition defaults to COND_ALWAYS.
- Error if Rd or Rn are R15.
- See ARMARMv6 A4.1.27: LDREX. */
+ case OP_CPSF:
+ case OP_ENDI:
+ case OP_oROR:
+ case OP_PSR:
+ case OP_COND:
+ case OP_REGLST:
+ case OP_VRSLST:
+ case OP_VRDLST:
+ if (val == FAIL)
+ goto failure;
+ inst.operands[i].imm = val;
+ break;
-static void
-do_ldrex (char * str)
-{
- int rd, rn;
+ default:
+ break;
+ }
- skip_whitespace (str);
+ /* If we get here, this operand was successfully parsed. */
+ inst.operands[i].present = 1;
+ continue;
- /* Parse Rd. */
- if (((rd = reg_required_here (&str, 12)) == FAIL)
- || (skip_past_comma (&str) == FAIL))
- {
+ bad_args:
inst.error = BAD_ARGS;
- return;
- }
- else if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
- skip_whitespace (str);
- /* Skip past '['. */
- if ((strlen (str) >= 1)
- &&strncmp (str, "[", 1) == 0)
- str += 1;
- skip_whitespace (str);
+ failure:
+ if (!backtrack_pos)
+ return FAIL;
- /* Parse Rn. */
- if ((rn = reg_required_here (&str, 16)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- else if (rn == REG_PC)
- {
- inst.error = BAD_PC;
- return;
+ /* Do not backtrack over a trailing optional argument that
+ absorbed some text. We will only fail again, with the
+ 'garbage following instruction' error message, which is
+ probably less helpful than the current one. */
+ if (backtrack_index == i && backtrack_pos != str
+ && upat[i+1] == OP_stop)
+ return FAIL;
+
+ /* Try again, skipping the optional argument at backtrack_pos. */
+ str = backtrack_pos;
+ inst.error = backtrack_error;
+ inst.operands[backtrack_index].present = 0;
+ i = backtrack_index;
+ backtrack_pos = 0;
}
- skip_whitespace (str);
- /* Skip past ']'. */
- if ((strlen (str) >= 1)
- && strncmp (str, "]", 1) == 0)
- str += 1;
+ /* Check that we have parsed all the arguments. */
+ if (*str != '\0' && !inst.error)
+ inst.error = _("garbage following instruction");
- end_of_line (str);
+ return inst.error ? FAIL : SUCCESS;
}
-/* ARM V6 change processor state instruction (argument parse)
- CPS, CPSIE, CSPID . */
-
-static void
-do_cps (char * str)
-{
- do_cps_mode (&str);
- end_of_line (str);
-}
+#undef po_char_or_fail
+#undef po_reg_or_fail
+#undef po_reg_or_goto
+#undef po_imm_or_fail
+
+/* Shorthand macro for instruction encoding functions issuing errors. */
+#define constraint(expr, err) do { \
+ if (expr) \
+ { \
+ inst.error = err; \
+ return; \
+ } \
+} while (0)
-static void
-do_cps_flags (char ** str, int thumb_p)
-{
- struct cps_flag
- {
- char character;
- unsigned long arm_value;
- unsigned long thumb_value;
- };
- static struct cps_flag flag_table[] =
- {
- {'a', 0x100, 0x4 },
- {'i', 0x080, 0x2 },
- {'f', 0x040, 0x1 }
- };
+/* Functions for operand encoding. ARM, then Thumb. */
- int saw_a_flag = 0;
+#define rotate_left(v, n) (v << n | v >> (32 - n))
- skip_whitespace (*str);
+/* If VAL can be encoded in the immediate field of an ARM instruction,
+ return the encoded form. Otherwise, return FAIL. */
- /* Get the a, f and i flags. */
- while (**str && **str != ',')
- {
- struct cps_flag *p;
- struct cps_flag *q = flag_table + sizeof (flag_table)/sizeof (*p);
+static unsigned int
+encode_arm_immediate (unsigned int val)
+{
+ unsigned int a, i;
- for (p = flag_table; p < q; ++p)
- if (strncasecmp (*str, &p->character, 1) == 0)
- {
- inst.instruction |= (thumb_p ? p->thumb_value : p->arm_value);
- saw_a_flag = 1;
- break;
- }
- if (p == q)
- {
- inst.error = _("unrecognized flag");
- return;
- }
- (*str)++;
- }
+ for (i = 0; i < 32; i += 2)
+ if ((a = rotate_left (val, i)) <= 0xff)
+ return a | (i << 7); /* 12-bit pack: [shift-cnt,const]. */
- if (!saw_a_flag)
- inst.error = _("no 'a', 'i', or 'f' flags for 'cps'");
+ return FAIL;
}
-static void
-do_cpsi (char * str)
+/* If VAL can be encoded in the immediate field of a Thumb32 instruction,
+ return the encoded form. Otherwise, return FAIL. */
+static unsigned int
+encode_thumb32_immediate (unsigned int val)
{
- do_cps_flags (&str, /*thumb_p=*/0);
+ unsigned int a, i;
- if (skip_past_comma (&str) == SUCCESS)
+ if (val <= 255)
+ return val;
+
+ for (i = 0; i < 32; i++)
{
- skip_whitespace (str);
- do_cps_mode (&str);
+ a = rotate_left (val, i);
+ if (a >= 128 && a <= 255)
+ return (a & 0x7f) | (i << 7);
}
- end_of_line (str);
-}
-/* ARM V6T2 bitfield manipulation instructions. */
+ a = val & 0xff;
+ if (val == ((a << 16) | a))
+ return 0x100 | a;
+ if (val == ((a << 24) | (a << 16) | (a << 8) | a))
+ return 0x300 | a;
-static int
-five_bit_unsigned_immediate (char **str)
-{
- expressionS expr;
+ a = val & 0xff00;
+ if (val == ((a << 16) | a))
+ return 0x200 | (a >> 8);
- skip_whitespace (*str);
- if (!is_immediate_prefix (**str))
- {
- inst.error = _("immediate expression expected");
- return -1;
- }
- (*str)++;
- if (my_get_expression (&expr, str))
- {
- inst.error = _("bad expression");
- return -1;
- }
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return -1;
- }
- if (expr.X_add_number < 0 || expr.X_add_number > 32)
- {
- inst.error = _("immediate value out of range");
- return -1;
- }
-
- return expr.X_add_number;
+ return FAIL;
}
+/* Encode a VFP SP register number into inst.instruction. */
static void
-bfci_lsb_and_width (char *str)
+encode_arm_vfp_sp_reg (int reg, enum vfp_sp_reg_pos pos)
{
- int lsb, width;
-
- if ((lsb = five_bit_unsigned_immediate (&str)) == -1)
- return;
-
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if ((width = five_bit_unsigned_immediate (&str)) == -1)
- return;
-
- end_of_line (str);
-
- if (width == 0 || lsb == 32)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- else if (width + lsb > 32)
+ switch (pos)
{
- inst.error = _("bit-field extends past end of register");
- return;
- }
+ case VFP_REG_Sd:
+ inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
+ break;
- /* Convert to LSB/MSB and write to register. */
- inst.instruction |= lsb << 7;
- inst.instruction |= (width + lsb - 1) << 16;
-}
+ case VFP_REG_Sn:
+ inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
+ break;
-static void
-do_bfc (char *str)
-{
- int rd;
+ case VFP_REG_Sm:
+ inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
+ break;
- /* Rd. */
- skip_whitespace (str);
- if (((rd = reg_required_here (&str, 12)) == FAIL)
- || (skip_past_comma (&str) == FAIL))
- {
- inst.error = BAD_ARGS;
- return;
- }
- else if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
+ default:
+ abort ();
}
-
- bfci_lsb_and_width (str);
}
+/* Encode a <shift> in an ARM-format instruction. The immediate,
+ if any, is handled by md_apply_fix3. */
static void
-do_bfi (char *str)
+encode_arm_shift (int i)
{
- int rd, rm;
-
- /* Rd. */
- skip_whitespace (str);
- if (((rd = reg_required_here (&str, 12)) == FAIL)
- || (skip_past_comma (&str) == FAIL))
- {
- inst.error = BAD_ARGS;
- return;
- }
- else if (rd == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
-
- /* Rm. Accept #0 in this position as an alternative syntax for bfc. */
- skip_whitespace (str);
- if (is_immediate_prefix (*str))
- {
- expressionS expr;
- str++;
- if (my_get_expression (&expr, &str))
- {
- inst.error = _("bad expression");
- return;
- }
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return;
- }
- if (expr.X_add_number != 0)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction |= 0x0000000f; /* Rm = PC -> bfc, not bfi. */
- }
+ if (inst.operands[i].shift_kind == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 5;
else
{
- if ((rm = reg_required_here (&str, 0)) == FAIL)
+ inst.instruction |= inst.operands[i].shift_kind << 5;
+ if (inst.operands[i].immisreg)
{
- inst.error = BAD_ARGS;
- return;
- }
- else if (rm == REG_PC)
- {
- inst.error = BAD_PC;
- return;
+ inst.instruction |= SHIFT_BY_REG;
+ inst.instruction |= inst.operands[i].imm << 8;
}
+ else
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
}
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
-
- bfci_lsb_and_width (str);
-}
-
-static void
-do_bfx (char *str)
-{
- int lsb, width;
-
- /* Rd. */
- skip_whitespace (str);
- if (reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
-
- /* Rm. */
- skip_whitespace (str);
- if (reg_required_here (&str, 0) == FAIL
- || skip_past_comma (&str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
-
- if ((lsb = five_bit_unsigned_immediate (&str)) == -1)
- return;
-
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
- if ((width = five_bit_unsigned_immediate (&str)) == -1)
- return;
-
- end_of_line (str);
-
- if (width == 0 || lsb == 32)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- else if (width + lsb > 32)
- {
- inst.error = _("bit-field extends past end of register");
- return;
- }
-
- inst.instruction |= lsb << 7;
- inst.instruction |= (width - 1) << 16;
}
static void
-do_rbit (char *str)
+encode_arm_shifter_operand (int i)
{
- /* Rd. */
- skip_whitespace (str);
- if (reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL)
+ if (inst.operands[i].isreg)
{
- inst.error = BAD_ARGS;
- return;
+ inst.instruction |= inst.operands[i].reg;
+ encode_arm_shift (i);
}
-
- /* Rm. */
- skip_whitespace (str);
- if (reg_required_here (&str, 0) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ else
+ inst.instruction |= INST_IMMEDIATE;
}
-/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>. */
+/* Subroutine of encode_arm_addr_mode_2 and encode_arm_addr_mode_3. */
static void
-do_mov16 (char *str)
+encode_arm_addr_mode_common (int i, bfd_boolean is_t)
{
- int rd;
- expressionS expr;
+ assert (inst.operands[i].isreg);
+ inst.instruction |= inst.operands[i].reg << 16;
- /* Rd. */
- skip_whitespace (str);
- if (((rd = reg_required_here (&str, 12)) == FAIL)
- || (skip_past_comma (&str) == FAIL))
- {
- inst.error = BAD_ARGS;
- return;
- }
- else if (rd == REG_PC)
+ if (inst.operands[i].preind)
{
- inst.error = BAD_PC;
- return;
- }
+ if (is_t)
+ {
+ inst.error = _("instruction does not accept preindexed addressing");
+ return;
+ }
+ inst.instruction |= PRE_INDEX;
+ if (inst.operands[i].writeback)
+ inst.instruction |= WRITE_BACK;
- /* Imm16. */
- skip_whitespace (str);
- if (!is_immediate_prefix (*str))
- {
- inst.error = _("immediate expression expected");
- return;
}
- str++;
- if (my_get_expression (&expr, &str))
+ else if (inst.operands[i].postind)
{
- inst.error = _("bad expression");
- return;
+ assert (inst.operands[i].writeback);
+ if (is_t)
+ inst.instruction |= WRITE_BACK;
}
- if (expr.X_op != O_constant)
+ else /* unindexed - only for coprocessor */
{
- inst.error = _("constant expression expected");
+ inst.error = _("instruction does not accept unindexed addressing");
return;
}
- if (expr.X_add_number < 0 || expr.X_add_number > 65535)
- {
- inst.error = _("immediate value out of range");
- return;
- }
-
- end_of_line (str);
- /* The value is in two pieces: 0:11, 16:19. */
- inst.instruction |= (expr.X_add_number & 0x00000fff);
- inst.instruction |= (expr.X_add_number & 0x0000f000) << 4;
+ if (((inst.instruction & WRITE_BACK) || !(inst.instruction & PRE_INDEX))
+ && (((inst.instruction & 0x000f0000) >> 16)
+ == ((inst.instruction & 0x0000f000) >> 12)))
+ as_warn ((inst.instruction & LOAD_BIT)
+ ? _("destination register same as write-back base")
+ : _("source register same as write-back base"));
}
-
-
-/* THUMB V5 breakpoint instruction (argument parse)
- BKPT <immed_8>. */
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format mode 2 load or store instruction. If is_t is true,
+ reject forms that cannot be used with a T instruction (i.e. not
+ post-indexed). */
static void
-do_t_bkpt (char * str)
+encode_arm_addr_mode_2 (int i, bfd_boolean is_t)
{
- expressionS expr;
- unsigned long number;
-
- skip_whitespace (str);
+ encode_arm_addr_mode_common (i, is_t);
- /* Allow optional leading '#'. */
- if (is_immediate_prefix (*str))
- str ++;
-
- memset (& expr, '\0', sizeof (expr));
- if (my_get_expression (& expr, & str)
- || (expr.X_op != O_constant
- /* As a convenience we allow 'bkpt' without an operand. */
- && expr.X_op != O_absent))
+ if (inst.operands[i].immisreg)
{
- inst.error = _("bad expression");
- return;
+ inst.instruction |= INST_IMMEDIATE; /* yes, this is backwards */
+ inst.instruction |= inst.operands[i].imm;
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ if (inst.operands[i].shifted)
+ {
+ if (inst.operands[i].shift_kind == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 5;
+ else
+ {
+ inst.instruction |= inst.operands[i].shift_kind << 5;
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
+ }
+ }
}
-
- number = expr.X_add_number;
-
- /* Check it fits an 8 bit unsigned. */
- if (number != (number & 0xff))
+ else /* immediate offset in inst.reloc */
{
- inst.error = _("immediate value out of range");
- return;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 8; /* pipeline offset */
}
-
- inst.instruction |= number;
-
- end_of_line (str);
-}
-
-#ifdef OBJ_ELF
-static bfd_reloc_code_real_type
-arm_parse_reloc (void)
-{
- char id [16];
- char * ip;
- unsigned int i;
- static struct
- {
- char * str;
- int len;
- bfd_reloc_code_real_type reloc;
- }
- reloc_map[] =
- {
-#define MAP(str,reloc) { str, sizeof (str) - 1, reloc }
- MAP ("(got)", BFD_RELOC_ARM_GOT32),
- MAP ("(gotoff)", BFD_RELOC_ARM_GOTOFF),
- /* ScottB: Jan 30, 1998 - Added support for parsing "var(PLT)"
- branch instructions generated by GCC for PLT relocs. */
- MAP ("(plt)", BFD_RELOC_ARM_PLT32),
- MAP ("(target1)", BFD_RELOC_ARM_TARGET1),
- MAP ("(sbrel)", BFD_RELOC_ARM_SBREL32),
- MAP ("(target2)", BFD_RELOC_ARM_TARGET2),
- MAP ("(tlsgd)", BFD_RELOC_ARM_TLS_GD32),
- MAP ("(tlsldm)", BFD_RELOC_ARM_TLS_LDM32),
- MAP ("(tlsldo)", BFD_RELOC_ARM_TLS_LDO32),
- MAP ("(gottpoff)", BFD_RELOC_ARM_TLS_IE32),
- MAP ("(tpoff)", BFD_RELOC_ARM_TLS_LE32),
- { NULL, 0, BFD_RELOC_UNUSED }
-#undef MAP
- };
-
- for (i = 0, ip = input_line_pointer;
- i < sizeof (id) && (ISALNUM (*ip) || ISPUNCT (*ip));
- i++, ip++)
- id[i] = TOLOWER (*ip);
-
- for (i = 0; reloc_map[i].str; i++)
- if (strncmp (id, reloc_map[i].str, reloc_map[i].len) == 0)
- break;
-
- input_line_pointer += reloc_map[i].len;
-
- return reloc_map[i].reloc;
-}
-#endif
-
-/* ARM V5 branch-link-exchange (argument parse) for BLX(1) only.
- Expects inst.instruction is set for BLX(1).
- Note: this is cloned from do_branch, and the reloc changed to be a
- new one that can cope with setting one extra bit (the H bit). */
-
-static void
-do_branch25 (char * str)
-{
- if (my_get_expression (& inst.reloc.exp, & str))
- return;
-
-#ifdef OBJ_ELF
- {
- char * save_in;
-
- /* ScottB: February 5, 1998 */
- /* Check to see of PLT32 reloc required for the instruction. */
-
- /* arm_parse_reloc() works on input_line_pointer.
- We actually want to parse the operands to the branch instruction
- passed in 'str'. Save the input pointer and restore it later. */
- save_in = input_line_pointer;
- input_line_pointer = str;
-
- if (inst.reloc.exp.X_op == O_symbol
- && *str == '('
- && arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
- {
- inst.reloc.type = BFD_RELOC_ARM_PLT32;
- inst.reloc.pc_rel = 0;
- /* Modify str to point to after parsed operands, otherwise
- end_of_line() will complain about the (PLT) left in str. */
- str = input_line_pointer;
- }
- else
- {
- inst.reloc.type = BFD_RELOC_ARM_PCREL_BLX;
- inst.reloc.pc_rel = 1;
- }
-
- input_line_pointer = save_in;
- }
-#else
- inst.reloc.type = BFD_RELOC_ARM_PCREL_BLX;
- inst.reloc.pc_rel = 1;
-#endif /* OBJ_ELF */
-
- end_of_line (str);
}
-/* ARM V5 branch-link-exchange instruction (argument parse)
- BLX <target_addr> ie BLX(1)
- BLX{<condition>} <Rm> ie BLX(2)
- Unfortunately, there are two different opcodes for this mnemonic.
- So, the insns[].value is not used, and the code here zaps values
- into inst.instruction.
- Also, the <target_addr> can be 25 bits, hence has its own reloc. */
-
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format mode 3 load or store instruction. Reject forms that
+ cannot be used with such instructions. If is_t is true, reject
+ forms that cannot be used with a T instruction (i.e. not
+ post-indexed). */
static void
-do_blx (char * str)
+encode_arm_addr_mode_3 (int i, bfd_boolean is_t)
{
- char * mystr = str;
- int rm;
-
- skip_whitespace (mystr);
- rm = reg_required_here (& mystr, 0);
-
- /* The above may set inst.error. Ignore his opinion. */
- inst.error = 0;
-
- if (rm != FAIL)
- {
- /* Arg is a register.
- Use the condition code our caller put in inst.instruction.
- Pass ourselves off as a BX with a funny opcode. */
- inst.instruction |= 0x012fff30;
- do_bx (str);
- }
- else
+ if (inst.operands[i].immisreg && inst.operands[i].shifted)
{
- /* This must be is BLX <target address>, no condition allowed. */
- if (inst.instruction != COND_ALWAYS)
- {
- inst.error = BAD_COND;
- return;
- }
-
- inst.instruction = 0xfafffffe;
-
- /* Process like a B/BL, but with a different reloc.
- Note that B/BL expecte fffffe, not 0, offset in the opcode table. */
- do_branch25 (str);
+ inst.error = _("instruction does not accept scaled register index");
+ return;
}
-}
-/* ARM V5 Thumb BLX (argument parse)
- BLX <target_addr> which is BLX(1)
- BLX <Rm> which is BLX(2)
- Unfortunately, there are two different opcodes for this mnemonic.
- So, the tinsns[].value is not used, and the code here zaps values
- into inst.instruction. */
+ encode_arm_addr_mode_common (i, is_t);
-static void
-do_t_blx (char * str)
-{
- char * mystr = str;
- int rm;
-
- skip_whitespace (mystr);
- inst.instruction = 0x4780;
-
- /* Note that this call is to the ARM register recognizer. BLX(2)
- uses the ARM register space, not the Thumb one, so a call to
- thumb_reg() would be wrong. */
- rm = reg_required_here (& mystr, 3);
- inst.error = 0;
-
- if (rm != FAIL)
+ if (inst.operands[i].immisreg)
{
- /* It's BLX(2). The .instruction was zapped with rm & is final. */
- inst.size = 2;
+ inst.instruction |= inst.operands[i].imm;
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
}
- else
+ else /* immediate offset in inst.reloc */
{
- /* No ARM register. This must be BLX(1). Change the .instruction. */
- inst.instruction = 0xf7ffeffe;
- inst.size = 4;
-
- if (my_get_expression (& inst.reloc.exp, & mystr))
- return;
-
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BLX;
- inst.reloc.pc_rel = 1;
+ inst.instruction |= HWOFFSET_IMM;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 8; /* pipeline offset */
}
-
- end_of_line (mystr);
}
-/* ARM V5 breakpoint instruction (argument parse)
- BKPT <16 bit unsigned immediate>
- Instruction is not conditional.
- The bit pattern given in insns[] has the COND_ALWAYS condition,
- and it is an error if the caller tried to override that. */
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format instruction. Reject all forms which cannot be encoded
+ into a coprocessor load/store instruction. If wb_ok is false,
+ reject use of writeback; if unind_ok is false, reject use of
+ unindexed addressing. If reloc_override is not 0, use it instead
+ of BFD_ARM_CP_OFF_IMM. */
-static void
-do_bkpt (char * str)
+static int
+encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
{
- expressionS expr;
- unsigned long number;
+ inst.instruction |= inst.operands[i].reg << 16;
- skip_whitespace (str);
+ assert (!(inst.operands[i].preind && inst.operands[i].postind));
- /* Allow optional leading '#'. */
- if (is_immediate_prefix (* str))
- str++;
-
- memset (& expr, '\0', sizeof (expr));
-
- if (my_get_expression (& expr, & str)
- || (expr.X_op != O_constant
- /* As a convenience we allow 'bkpt' without an operand. */
- && expr.X_op != O_absent))
+ if (!inst.operands[i].preind && !inst.operands[i].postind) /* unindexed */
{
- inst.error = _("bad expression");
- return;
- }
-
- number = expr.X_add_number;
-
- /* Check it fits a 16 bit unsigned. */
- if (number != (number & 0xffff))
- {
- inst.error = _("immediate value out of range");
- return;
+ assert (!inst.operands[i].writeback);
+ if (!unind_ok)
+ {
+ inst.error = _("instruction does not support unindexed addressing");
+ return FAIL;
+ }
+ inst.instruction |= inst.operands[i].imm;
+ inst.instruction |= INDEX_UP;
+ return SUCCESS;
}
- /* Top 12 of 16 bits to bits 19:8. */
- inst.instruction |= (number & 0xfff0) << 4;
-
- /* Bottom 4 of 16 bits to bits 3:0. */
- inst.instruction |= number & 0xf;
-
- end_of_line (str);
-}
-
-/* THUMB CPS instruction (argument parse). */
-
-static void
-do_t_cps (char * str)
-{
- do_cps_flags (&str, /*thumb_p=*/1);
- end_of_line (str);
-}
-
-/* Parse and validate that a register is of the right form, this saves
- repeated checking of this information in many similar cases.
- Unlike the 32-bit case we do not insert the register into the opcode
- here, since the position is often unknown until the full instruction
- has been parsed. */
-
-static int
-thumb_reg (char ** strp, int hi_lo)
-{
- int reg;
-
- if ((reg = reg_required_here (strp, -1)) == FAIL)
- return FAIL;
+ if (inst.operands[i].preind)
+ inst.instruction |= PRE_INDEX;
- switch (hi_lo)
+ if (inst.operands[i].writeback)
{
- case THUMB_REG_LO:
- if (reg > 7)
+ if (inst.operands[i].reg == REG_PC)
{
- inst.error = _("lo register required");
+ inst.error = _("pc may not be used with write-back");
return FAIL;
}
- break;
-
- case THUMB_REG_HI:
- if (reg < 8)
+ if (!wb_ok)
{
- inst.error = _("hi register required");
+ inst.error = _("instruction does not support writeback");
return FAIL;
}
- break;
-
- default:
- break;
+ inst.instruction |= WRITE_BACK;
}
- return reg;
+ if (reloc_override)
+ inst.reloc.type = reloc_override;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 8;
+ return SUCCESS;
}
-static void
-thumb_mov_compare (char * str, int move)
-{
- int Rd, Rs = FAIL;
+/* inst.reloc.exp describes an "=expr" load pseudo-operation.
+ Determine whether it can be performed with a move instruction; if
+ it can, convert inst.instruction to that move instruction and
+ return 1; if it can't, convert inst.instruction to a literal-pool
+ load and return 0. If this is not a valid thing to do in the
+ current context, set inst.error and return 1.
- skip_whitespace (str);
-
- if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
- || skip_past_comma (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ inst.operands[i] describes the destination register. */
- if (move != THUMB_CPY && is_immediate_prefix (*str))
+static int
+move_or_literal_pool (int i, bfd_boolean thumb_p, bfd_boolean mode_3)
+{
+ if ((inst.instruction & (thumb_p ? THUMB_LOAD_BIT : LOAD_BIT)) == 0)
{
- str++;
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
+ inst.error = _("invalid pseudo operation");
+ return 1;
}
- else if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
-
- if (Rs != FAIL)
+ if (inst.reloc.exp.X_op != O_constant && inst.reloc.exp.X_op != O_symbol)
{
- if (move != THUMB_CPY && Rs < 8 && Rd < 8)
- {
- if (move == THUMB_MOVE)
- /* A move of two lowregs is encoded as ADD Rd, Rs, #0
- since a MOV instruction produces unpredictable results. */
- inst.instruction = T_OPCODE_ADD_I3;
- else
- inst.instruction = T_OPCODE_CMP_LR;
- inst.instruction |= Rd | (Rs << 3);
- }
- else
- {
- if (move == THUMB_MOVE)
- inst.instruction = T_OPCODE_MOV_HR;
- else if (move != THUMB_CPY)
- inst.instruction = T_OPCODE_CMP_HR;
-
- if (Rd > 7)
- inst.instruction |= THUMB_H1;
-
- if (Rs > 7)
- inst.instruction |= THUMB_H2;
-
- inst.instruction |= (Rd & 7) | ((Rs & 7) << 3);
- }
+ inst.error = _("constant expression expected");
+ return 1;
}
- else
+ if (inst.reloc.exp.X_op == O_constant)
{
- if (Rd > 7)
+ if (thumb_p)
{
- inst.error = _("only lo regs allowed with immediate");
- return;
+ if ((inst.reloc.exp.X_add_number & ~0xFF) == 0)
+ {
+ /* This can be done with a mov(1) instruction. */
+ inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
+ inst.instruction |= inst.reloc.exp.X_add_number;
+ return 1;
+ }
}
-
- if (move == THUMB_MOVE)
- inst.instruction = T_OPCODE_MOV_I8;
- else
- inst.instruction = T_OPCODE_CMP_I8;
-
- inst.instruction |= Rd << 8;
-
- if (inst.reloc.exp.X_op != O_constant)
- inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
else
{
- unsigned value = inst.reloc.exp.X_add_number;
-
- if (value > 255)
+ int value = encode_arm_immediate (inst.reloc.exp.X_add_number);
+ if (value != FAIL)
{
- inst.error = _("invalid immediate");
- return;
+ /* This can be done with a mov instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return 1;
}
- inst.instruction |= value;
+ value = encode_arm_immediate (~inst.reloc.exp.X_add_number);
+ if (value != FAIL)
+ {
+ /* This can be done with a mvn instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return 1;
+ }
}
}
- end_of_line (str);
+ if (add_to_lit_pool () == FAIL)
+ {
+ inst.error = _("literal pool insertion failed");
+ return 1;
+ }
+ inst.operands[1].reg = REG_PC;
+ inst.operands[1].isreg = 1;
+ inst.operands[1].preind = 1;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = (thumb_p
+ ? BFD_RELOC_ARM_THUMB_OFFSET
+ : (mode_3
+ ? BFD_RELOC_ARM_HWLITERAL
+ : BFD_RELOC_ARM_LITERAL));
+ return 0;
}
-/* THUMB CPY instruction (argument parse). */
+/* Functions for instruction encoding, sorted by subarchitecture.
+ First some generics; their names are taken from the conventional
+ bit positions for register arguments in ARM format instructions. */
static void
-do_t_cpy (char * str)
+do_noargs (void)
{
- thumb_mov_compare (str, THUMB_CPY);
}
-/* THUMB SETEND instruction (argument parse). */
-
static void
-do_t_setend (char * str)
+do_rd (void)
{
- if (do_endian_specifier (str))
- inst.instruction |= 0x8;
-}
-
-/* Parse INSN_TYPE insn STR having a possible IMMEDIATE_SIZE immediate. */
-
-static unsigned long
-check_iwmmxt_insn (char * str,
- enum iwmmxt_insn_type insn_type,
- int immediate_size)
-{
- int reg = 0;
- const char * inst_error;
- expressionS expr;
- unsigned long number;
-
- inst_error = inst.error;
- if (!inst.error)
- inst.error = BAD_ARGS;
- skip_whitespace (str);
-
- switch (insn_type)
- {
- case check_rd:
- if ((reg = reg_required_here (&str, 12)) == FAIL)
- return FAIL;
- break;
-
- case check_wr:
- if ((wreg_required_here (&str, 0, IWMMXT_REG_WR)) == FAIL)
- return FAIL;
- break;
-
- case check_wrwr:
- if ((wreg_required_here (&str, 12, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL))
- return FAIL;
- break;
-
- case check_wrwrwr:
- if ((wreg_required_here (&str, 12, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 0, IWMMXT_REG_WR) == FAIL))
- return FAIL;
- break;
-
- case check_wrwrwcg:
- if ((wreg_required_here (&str, 12, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 0, IWMMXT_REG_WCG) == FAIL))
- return FAIL;
- break;
-
- case check_tbcst:
- if ((wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL))
- return FAIL;
- break;
-
- case check_tmovmsk:
- if ((reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL))
- return FAIL;
- break;
-
- case check_tmia:
- if ((wreg_required_here (&str, 5, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 0) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL))
- return FAIL;
- break;
-
- case check_tmcrr:
- if ((wreg_required_here (&str, 0, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL))
- return FAIL;
- break;
-
- case check_tmrrc:
- if ((reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 0, IWMMXT_REG_WR) == FAIL))
- return FAIL;
- break;
-
- case check_tmcr:
- if ((wreg_required_here (&str, 16, IWMMXT_REG_WC) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL))
- return FAIL;
- break;
-
- case check_tmrc:
- if ((reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WC) == FAIL))
- return FAIL;
- break;
-
- case check_tinsr:
- if ((wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL))
- return FAIL;
- break;
-
- case check_textrc:
- if ((reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL))
- return FAIL;
- break;
-
- case check_waligni:
- if ((wreg_required_here (&str, 12, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 0, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL))
- return FAIL;
- break;
-
- case check_textrm:
- if ((reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL))
- return FAIL;
- break;
-
- case check_wshufh:
- if ((wreg_required_here (&str, 12, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL
- || wreg_required_here (&str, 16, IWMMXT_REG_WR) == FAIL
- || skip_past_comma (&str) == FAIL))
- return FAIL;
- break;
- }
-
- if (immediate_size == 0)
- {
- end_of_line (str);
- inst.error = inst_error;
- return reg;
- }
- else
- {
- skip_whitespace (str);
-
- /* Allow optional leading '#'. */
- if (is_immediate_prefix (* str))
- str++;
-
- memset (& expr, '\0', sizeof (expr));
-
- if (my_get_expression (& expr, & str) || (expr.X_op != O_constant))
- {
- inst.error = _("bad or missing expression");
- return FAIL;
- }
-
- number = expr.X_add_number;
-
- if (number != (number & immediate_size))
- {
- inst.error = _("immediate value out of range");
- return FAIL;
- }
- end_of_line (str);
- inst.error = inst_error;
- return number;
- }
+ inst.instruction |= inst.operands[0].reg << 12;
}
static void
-do_iwmmxt_byte_addr (char * str)
+do_rd_rm (void)
{
- int op = (inst.instruction & 0x300) >> 8;
- int reg;
-
- inst.instruction &= ~0x300;
- inst.instruction |= (op & 1) << 22 | (op & 2) << 7;
-
- skip_whitespace (str);
-
- if ((reg = wreg_required_here (&str, 12, IWMMXT_REG_WR_OR_WC)) == FAIL
- || skip_past_comma (& str) == FAIL
- || cp_byte_address_required_here (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- }
- else
- end_of_line (str);
-
- if (wc_register (reg))
- {
- as_bad (_("non-word size not supported with control register"));
- inst.instruction |= 0xf0000100;
- inst.instruction &= ~0x00400000;
- }
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
}
static void
-do_iwmmxt_tandc (char * str)
+do_rd_rn (void)
{
- int reg;
-
- reg = check_iwmmxt_insn (str, check_rd, 0);
-
- if (reg != REG_PC && !inst.error)
- inst.error = _("only r15 allowed here");
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
}
static void
-do_iwmmxt_tbcst (char * str)
+do_rn_rd (void)
{
- check_iwmmxt_insn (str, check_tbcst, 0);
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 12;
}
static void
-do_iwmmxt_textrc (char * str)
+do_rd_rm_rn (void)
{
- unsigned long number;
-
- if ((number = check_iwmmxt_insn (str, check_textrc, 7)) == (unsigned long) FAIL)
- return;
-
- inst.instruction |= number & 0x7;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
}
static void
-do_iwmmxt_textrm (char * str)
+do_rd_rn_rm (void)
{
- unsigned long number;
-
- if ((number = check_iwmmxt_insn (str, check_textrm, 7)) == (unsigned long) FAIL)
- return;
-
- inst.instruction |= number & 0x7;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
}
static void
-do_iwmmxt_tinsr (char * str)
+do_rm_rd_rn (void)
{
- unsigned long number;
-
- if ((number = check_iwmmxt_insn (str, check_tinsr, 7)) == (unsigned long) FAIL)
- return;
-
- inst.instruction |= number & 0x7;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
}
static void
-do_iwmmxt_tmcr (char * str)
+do_imm0 (void)
{
- check_iwmmxt_insn (str, check_tmcr, 0);
+ inst.instruction |= inst.operands[0].imm;
}
static void
-do_iwmmxt_tmcrr (char * str)
+do_rd_cpaddr (void)
{
- check_iwmmxt_insn (str, check_tmcrr, 0);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, TRUE, 0);
}
-static void
-do_iwmmxt_tmia (char * str)
-{
- check_iwmmxt_insn (str, check_tmia, 0);
-}
+/* ARM instructions, in alphabetical order by function name (except
+ that wrapper functions appear immediately after the function they
+ wrap). */
+
+/* This is a pseudo-op of the form "adr rd, label" to be converted
+ into a relative address of the form "add rd, pc, #label-.-8". */
static void
-do_iwmmxt_tmovmsk (char * str)
+do_adr (void)
{
- check_iwmmxt_insn (str, check_tmovmsk, 0);
+ inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
+
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+#ifndef TE_WINCE
+ inst.reloc.exp.X_add_number -= 8; /* PC relative adjust. */
+#endif
+ inst.reloc.pc_rel = 1;
}
+/* This is a pseudo-op of the form "adrl rd, label" to be converted
+ into a relative address of the form:
+ add rd, pc, #low(label-.-8)"
+ add rd, rd, #high(label-.-8)" */
+
static void
-do_iwmmxt_tmrc (char * str)
+do_adrl (void)
{
- check_iwmmxt_insn (str, check_tmrc, 0);
+ inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
+
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+#ifndef TE_WINCE
+ inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
+#endif
+ inst.reloc.pc_rel = 1;
+ inst.size = INSN_SIZE * 2;
}
static void
-do_iwmmxt_tmrrc (char * str)
+do_arit (void)
{
- check_iwmmxt_insn (str, check_tmrrc, 0);
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ encode_arm_shifter_operand (2);
}
static void
-do_iwmmxt_torc (char * str)
+do_bfc (void)
{
- check_iwmmxt_insn (str, check_rd, 0);
+ unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 7;
+ inst.instruction |= (msb - 1) << 16;
}
static void
-do_iwmmxt_waligni (char * str)
+do_bfi (void)
{
- unsigned long number;
+ unsigned int msb;
- if ((number = check_iwmmxt_insn (str, check_waligni, 7)) == (unsigned long) FAIL)
- return;
+ /* #0 in second position is alternative syntax for bfc, which is
+ the same instruction but with REG_PC in the Rm field. */
+ if (!inst.operands[1].isreg)
+ inst.operands[1].reg = REG_PC;
- inst.instruction |= ((number & 0x7) << 20);
+ msb = inst.operands[2].imm + inst.operands[3].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 7;
+ inst.instruction |= (msb - 1) << 16;
}
static void
-do_iwmmxt_wmov (char * str)
+do_bfx (void)
{
- if (check_iwmmxt_insn (str, check_wrwr, 0) == (unsigned long) FAIL)
- return;
-
- inst.instruction |= ((inst.instruction >> 16) & 0xf);
+ constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+ _("bit-field extends past end of register"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 7;
+ inst.instruction |= (inst.operands[3].imm - 1) << 16;
}
+/* ARM V5 breakpoint instruction (argument parse)
+ BKPT <16 bit unsigned immediate>
+ Instruction is not conditional.
+ The bit pattern given in insns[] has the COND_ALWAYS condition,
+ and it is an error if the caller tried to override that. */
+
static void
-do_iwmmxt_word_addr (char * str)
+do_bkpt (void)
{
- int op = (inst.instruction & 0x300) >> 8;
- int reg;
-
- inst.instruction &= ~0x300;
- inst.instruction |= (op & 1) << 22 | (op & 2) << 7;
+ /* Top 12 of 16 bits to bits 19:8. */
+ inst.instruction |= (inst.operands[0].imm & 0xfff0) << 4;
- skip_whitespace (str);
+ /* Bottom 4 of 16 bits to bits 3:0. */
+ inst.instruction |= inst.operands[0].imm & 0xf;
+}
- if ((reg = wreg_required_here (&str, 12, IWMMXT_REG_WR_OR_WC)) == FAIL
- || skip_past_comma (& str) == FAIL
- || cp_address_required_here (& str, CP_WB_OK) == FAIL)
+static void
+encode_branch (int default_reloc)
+{
+ if (inst.operands[0].hasreloc)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
+ constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32,
+ _("the only suffix valid here is '(plt)'"));
+ inst.reloc.type = BFD_RELOC_ARM_PLT32;
+ inst.reloc.pc_rel = 0;
}
else
- end_of_line (str);
-
- if (wc_register (reg))
{
- if ((inst.instruction & COND_MASK) != COND_ALWAYS)
- as_bad (_("conditional execution not supported with control register"));
- if (op != 2)
- as_bad (_("non-word size not supported with control register"));
- inst.instruction |= 0xf0000100;
- inst.instruction &= ~0x00400000;
+ inst.reloc.type = default_reloc;
+ inst.reloc.pc_rel = 1;
}
}
static void
-do_iwmmxt_wrwr (char * str)
+do_branch (void)
{
- check_iwmmxt_insn (str, check_wrwr, 0);
+ encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
}
+/* ARM V5 branch-link-exchange instruction (argument parse)
+ BLX <target_addr> ie BLX(1)
+ BLX{<condition>} <Rm> ie BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
+ Also, the <target_addr> can be 25 bits, hence has its own reloc. */
+
static void
-do_iwmmxt_wrwrwcg (char * str)
+do_blx (void)
{
- check_iwmmxt_insn (str, check_wrwrwcg, 0);
+ if (inst.operands[0].isreg)
+ {
+ /* Arg is a register; the opcode provided by insns[] is correct.
+ It is not illegal to do "blx pc", just useless. */
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in blx in ARM mode is not really useful"));
+
+ inst.instruction |= inst.operands[0].reg;
+ }
+ else
+ {
+ /* Arg is an address; this instruction cannot be executed
+ conditionally, and the opcode must be adjusted. */
+ constraint (inst.cond != COND_ALWAYS, BAD_COND);
+ inst.instruction = 0xfafffffe;
+ encode_branch (BFD_RELOC_ARM_PCREL_BLX);
+ }
}
static void
-do_iwmmxt_wrwrwr (char * str)
+do_bx (void)
{
- check_iwmmxt_insn (str, check_wrwrwr, 0);
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
+
+ inst.instruction |= inst.operands[0].reg;
}
+
+/* ARM v5TEJ. Jump to Jazelle code. */
+
static void
-do_iwmmxt_wshufh (char * str)
+do_bxj (void)
{
- unsigned long number;
-
- if ((number = check_iwmmxt_insn (str, check_wshufh, 0xff)) == (unsigned long) FAIL)
- return;
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bxj is not really useful"));
- inst.instruction |= ((number & 0xf0) << 16) | (number & 0xf);
+ inst.instruction |= inst.operands[0].reg;
}
+/* Co-processor data operation:
+ CDP{cond} <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>}
+ CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>} */
static void
-do_iwmmxt_wzero (char * str)
+do_cdp (void)
{
- if (check_iwmmxt_insn (str, check_wr, 0) == (unsigned long) FAIL)
- return;
-
- inst.instruction |= ((inst.instruction & 0xf) << 12) | ((inst.instruction & 0xf) << 16);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 20;
+ inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
+ inst.instruction |= inst.operands[5].imm << 5;
}
-/* Xscale multiply-accumulate (argument parse)
- MIAcc acc0,Rm,Rs
- MIAPHcc acc0,Rm,Rs
- MIAxycc acc0,Rm,Rs. */
-
static void
-do_xsc_mia (char * str)
+do_cmp (void)
{
- int rs;
- int rm;
-
- if (accum0_required_here (& str) == FAIL)
- inst.error = ERR_NO_ACCUM;
-
- else if (skip_past_comma (& str) == FAIL
- || (rm = reg_required_here (& str, 0)) == FAIL)
- inst.error = BAD_ARGS;
-
- else if (skip_past_comma (& str) == FAIL
- || (rs = reg_required_here (& str, 12)) == FAIL)
- inst.error = BAD_ARGS;
-
- /* inst.instruction has now been zapped with both rm and rs. */
- else if (rm == REG_PC || rs == REG_PC)
- inst.error = BAD_PC; /* Undefined result if rm or rs is R15. */
-
- else
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_shifter_operand (1);
}
-/* Xscale move-accumulator-register (argument parse)
+/* Transfer between coprocessor and ARM registers.
+ MRC{cond} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
+ MRC2
+ MCR{cond}
+ MCR2
- MARcc acc0,RdLo,RdHi. */
+ No special properties. */
static void
-do_xsc_mar (char * str)
+do_co_reg (void)
{
- int rdlo, rdhi;
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 21;
+ inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
+ inst.instruction |= inst.operands[5].imm << 5;
+}
- if (accum0_required_here (& str) == FAIL)
- inst.error = ERR_NO_ACCUM;
+/* Transfer between coprocessor register and pair of ARM registers.
+ MCRR{cond} <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+ MCRR2
+ MRRC{cond}
+ MRRC2
- else if (skip_past_comma (& str) == FAIL
- || (rdlo = reg_required_here (& str, 12)) == FAIL)
- inst.error = BAD_ARGS;
+ Two XScale instructions are special cases of these:
- else if (skip_past_comma (& str) == FAIL
- || (rdhi = reg_required_here (& str, 16)) == FAIL)
- inst.error = BAD_ARGS;
+ MAR{cond} acc0, <RdLo>, <RdHi> == MCRR{cond} p0, #0, <RdLo>, <RdHi>, c0
+ MRA{cond} acc0, <RdLo>, <RdHi> == MRRC{cond} p0, #0, <RdLo>, <RdHi>, c0
- /* inst.instruction has now been zapped with both rdlo and rdhi. */
- else if (rdlo == REG_PC || rdhi == REG_PC)
- inst.error = BAD_PC; /* Undefined result if rdlo or rdhi is R15. */
+ Result unpredicatable if Rd or Rn is R15. */
- else
- end_of_line (str);
+static void
+do_co_reg2c (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 4;
+ inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
}
-/* Xscale move-register-accumulator (argument parse)
-
- MRAcc RdLo,RdHi,acc0. */
-
static void
-do_xsc_mra (char * str)
+do_cpsi (void)
{
- int rdlo;
- int rdhi;
-
- skip_whitespace (str);
-
- if ((rdlo = reg_required_here (& str, 12)) == FAIL)
- inst.error = BAD_ARGS;
-
- else if (skip_past_comma (& str) == FAIL
- || (rdhi = reg_required_here (& str, 16)) == FAIL)
- inst.error = BAD_ARGS;
-
- else if (skip_past_comma (& str) == FAIL
- || accum0_required_here (& str) == FAIL)
- inst.error = ERR_NO_ACCUM;
-
- /* inst.instruction has now been zapped with both rdlo and rdhi. */
- else if (rdlo == rdhi)
- inst.error = BAD_ARGS; /* Undefined result if 2 writes to same reg. */
-
- else if (rdlo == REG_PC || rdhi == REG_PC)
- inst.error = BAD_PC; /* Undefined result if rdlo or rdhi is R15. */
- else
- end_of_line (str);
+ inst.instruction |= inst.operands[0].imm << 6;
+ inst.instruction |= inst.operands[1].imm;
}
-static int
-ldst_extend (char ** str)
+static void
+do_it (void)
{
- int add = INDEX_UP;
-
- switch (**str)
- {
- case '#':
- case '$':
- (*str)++;
- if (my_get_expression (& inst.reloc.exp, str))
- return FAIL;
-
- if (inst.reloc.exp.X_op == O_constant)
- {
- int value = inst.reloc.exp.X_add_number;
-
- if (value < -4095 || value > 4095)
- {
- inst.error = _("address offset too large");
- return FAIL;
- }
-
- if (value < 0)
- {
- value = -value;
- add = 0;
- }
-
- inst.instruction |= add | value;
- }
- else
- {
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
- inst.reloc.pc_rel = 0;
- }
- return SUCCESS;
-
- case '-':
- add = 0;
- /* Fall through. */
-
- case '+':
- (*str)++;
- /* Fall through. */
-
- default:
- if (reg_required_here (str, 0) == FAIL)
- return FAIL;
-
- inst.instruction |= add | OFFSET_REG;
- if (skip_past_comma (str) == SUCCESS)
- return decode_shift (str, SHIFT_IMMEDIATE);
-
- return SUCCESS;
- }
+ /* There is no IT instruction in ARM mode. We
+ process it but do not generate code for it. */
+ inst.size = 0;
}
-/* ARMv5TE: Preload-Cache
-
- PLD <addr_mode>
-
- Syntactically, like LDR with B=1, W=0, L=1. */
-
static void
-do_pld (char * str)
+do_ldmstm (void)
{
- int rd;
-
- skip_whitespace (str);
+ int base_reg = inst.operands[0].reg;
+ int range = inst.operands[1].imm;
- if (* str != '[')
- {
- inst.error = _("'[' expected after PLD mnemonic");
- return;
- }
-
- ++str;
- skip_whitespace (str);
-
- if ((rd = reg_required_here (& str, 16)) == FAIL)
- return;
-
- skip_whitespace (str);
+ inst.instruction |= base_reg << 16;
+ inst.instruction |= range;
- if (*str == ']')
- {
- /* [Rn], ... ? */
- ++str;
- skip_whitespace (str);
+ if (inst.operands[1].writeback)
+ inst.instruction |= LDM_TYPE_2_OR_3;
- /* Post-indexed addressing is not allowed with PLD. */
- if (skip_past_comma (&str) == SUCCESS)
- {
- inst.error
- = _("post-indexed expression used in preload instruction");
- return;
- }
- else if (*str == '!') /* [Rn]! */
- {
- inst.error = _("writeback used in preload instruction");
- ++str;
- }
- else /* [Rn] */
- inst.instruction |= INDEX_UP | PRE_INDEX;
- }
- else /* [Rn, ...] */
+ if (inst.operands[0].writeback)
{
- if (skip_past_comma (& str) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return;
- }
-
- if (ldst_extend (&str) == FAIL)
- return;
-
- skip_whitespace (str);
-
- if (* str != ']')
+ inst.instruction |= WRITE_BACK;
+ /* Check for unpredictable uses of writeback. */
+ if (inst.instruction & LOAD_BIT)
{
- inst.error = _("missing ]");
- return;
+ /* Not allowed in LDM type 2. */
+ if ((inst.instruction & LDM_TYPE_2_OR_3)
+ && ((range & (1 << REG_PC)) == 0))
+ as_warn (_("writeback of base register is UNPREDICTABLE"));
+ /* Only allowed if base reg not in list for other types. */
+ else if (range & (1 << base_reg))
+ as_warn (_("writeback of base register when in register list is UNPREDICTABLE"));
}
-
- ++ str;
- skip_whitespace (str);
-
- if (* str == '!') /* [Rn]! */
+ else /* STM. */
{
- inst.error = _("writeback used in preload instruction");
- ++ str;
+ /* Not allowed for type 2. */
+ if (inst.instruction & LDM_TYPE_2_OR_3)
+ as_warn (_("writeback of base register is UNPREDICTABLE"));
+ /* Only allowed if base reg not in list, or first in list. */
+ else if ((range & (1 << base_reg))
+ && (range & ((1 << base_reg) - 1)))
+ as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
}
-
- inst.instruction |= PRE_INDEX;
}
-
- end_of_line (str);
}
/* ARMv5TE load-consecutive (argument parse)
@@ -5802,5824 +4624,5251 @@ do_pld (char * str)
STRccD R, mode. */
static void
-do_ldrd (char * str)
+do_ldrd (void)
{
- int rd;
- int rn;
-
- skip_whitespace (str);
-
- if ((rd = reg_required_here (& str, 12)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+ constraint (inst.operands[0].reg % 2 != 0,
+ _("first destination register must be even"));
+ constraint (inst.operands[1].present
+ && inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("can only load two consecutive registers"));
+ constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
+ constraint (!inst.operands[2].isreg, _("'[' expected"));
- if (skip_past_comma (& str) == FAIL
- || (rn = ld_mode_required_here (& str)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- /* inst.instruction has now been zapped with Rd and the addressing mode. */
- if (rd & 1) /* Unpredictable result if Rd is odd. */
- {
- inst.error = _("destination register must be even");
- return;
- }
-
- if (rd == REG_LR)
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg + 1;
+
+ if (inst.instruction & LOAD_BIT)
{
- inst.error = _("r14 not allowed here");
- return;
- }
+ /* encode_arm_addr_mode_3 will diagnose overlap between the base
+ register and the first register written; we have to diagnose
+ overlap between the base and the second register written here. */
- if (((rd == rn) || (rd + 1 == rn))
- && ((inst.instruction & WRITE_BACK)
- || (!(inst.instruction & PRE_INDEX))))
- as_warn (_("pre/post-indexing used when modified address register is destination"));
-
- /* For an index-register load, the index register must not overlap the
- destination (even if not write-back). */
- if ((inst.instruction & V4_STR_BIT) == 0
- && (inst.instruction & HWOFFSET_IMM) == 0)
- {
- int rm = inst.instruction & 0x0000000f;
+ if (inst.operands[2].reg == inst.operands[1].reg
+ && (inst.operands[2].writeback || inst.operands[2].postind))
+ as_warn (_("base register written back, and overlaps "
+ "second destination register"));
- if (rm == rd || (rm == rd + 1))
- as_warn (_("ldrd destination registers must not overlap index register"));
+ /* For an index-register load, the index register must not overlap the
+ destination (even if not write-back). */
+ else if (inst.operands[2].immisreg
+ && (inst.operands[2].imm == inst.operands[0].reg
+ || inst.operands[2].imm == inst.operands[1].reg))
+ as_warn (_("index register overlaps destination register"));
}
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
}
-/* Returns the index into fp_values of a floating point number,
- or -1 if not in the table. */
-
-static int
-my_get_float_expression (char ** str)
+static void
+do_ldrex (void)
{
- LITTLENUM_TYPE words[MAX_LITTLENUMS];
- char * save_in;
- expressionS exp;
- int i;
- int j;
+ constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+ || inst.operands[1].postind || inst.operands[1].writeback
+ || inst.operands[1].immisreg || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ _("instruction does not accept this addressing mode"));
- memset (words, 0, MAX_LITTLENUMS * sizeof (LITTLENUM_TYPE));
+ constraint (inst.operands[1].reg == REG_PC, BAD_PC);
- /* Look for a raw floating point number. */
- if ((save_in = atof_ieee (*str, 'x', words)) != NULL
- && is_end_of_line[(unsigned char) *save_in])
- {
- for (i = 0; i < NUM_FLOAT_VALS; i++)
- {
- for (j = 0; j < MAX_LITTLENUMS; j++)
- {
- if (words[j] != fp_values[i][j])
- break;
- }
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("offset must be zero in ARM encoding"));
- if (j == MAX_LITTLENUMS)
- {
- *str = save_in;
- return i;
- }
- }
- }
-
- /* Try and parse a more complex expression, this will probably fail
- unless the code uses a floating point prefix (eg "0f"). */
- save_in = input_line_pointer;
- input_line_pointer = *str;
- if (expression (&exp) == absolute_section
- && exp.X_op == O_big
- && exp.X_add_number < 0)
- {
- /* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
- Ditto for 15. */
- if (gen_to_words (words, 5, (long) 15) == 0)
- {
- for (i = 0; i < NUM_FLOAT_VALS; i++)
- {
- for (j = 0; j < MAX_LITTLENUMS; j++)
- {
- if (words[j] != fp_values[i][j])
- break;
- }
-
- if (j == MAX_LITTLENUMS)
- {
- *str = input_line_pointer;
- input_line_pointer = save_in;
- return i;
- }
- }
- }
- }
-
- *str = input_line_pointer;
- input_line_pointer = save_in;
- return -1;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_UNUSED;
}
-/* We handle all bad expressions here, so that we can report the faulty
- instruction in the error message. */
-void
-md_operand (expressionS * expr)
+static void
+do_ldrexd (void)
{
- if (in_my_get_expression)
- {
- expr->X_op = O_illegal;
- if (inst.error == NULL)
- inst.error = _("bad expression");
- }
-}
+ constraint (inst.operands[0].reg % 2 != 0,
+ _("even register required"));
+ constraint (inst.operands[1].present
+ && inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("can only load two consecutive registers"));
+ /* If op 1 were present and equal to PC, this function wouldn't
+ have been called in the first place. */
+ constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
-/* Do those data_ops which can take a negative immediate constant
- by altering the instruction. A bit of a hack really.
- MOV <-> MVN
- AND <-> BIC
- ADC <-> SBC
- by inverting the second operand, and
- ADD <-> SUB
- CMP <-> CMN
- by negating the second operand. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
-static int
-negate_data_op (unsigned long * instruction,
- unsigned long value)
+static void
+do_ldst (void)
{
- int op, new_inst;
- unsigned long negated, inverted;
-
- negated = validate_immediate (-value);
- inverted = validate_immediate (~value);
-
- op = (*instruction >> DATA_OP_SHIFT) & 0xf;
- switch (op)
- {
- /* First negates. */
- case OPCODE_SUB: /* ADD <-> SUB */
- new_inst = OPCODE_ADD;
- value = negated;
- break;
-
- case OPCODE_ADD:
- new_inst = OPCODE_SUB;
- value = negated;
- break;
-
- case OPCODE_CMP: /* CMP <-> CMN */
- new_inst = OPCODE_CMN;
- value = negated;
- break;
-
- case OPCODE_CMN:
- new_inst = OPCODE_CMP;
- value = negated;
- break;
-
- /* Now Inverted ops. */
- case OPCODE_MOV: /* MOV <-> MVN */
- new_inst = OPCODE_MVN;
- value = inverted;
- break;
-
- case OPCODE_MVN:
- new_inst = OPCODE_MOV;
- value = inverted;
- break;
-
- case OPCODE_AND: /* AND <-> BIC */
- new_inst = OPCODE_BIC;
- value = inverted;
- break;
-
- case OPCODE_BIC:
- new_inst = OPCODE_AND;
- value = inverted;
- break;
-
- case OPCODE_ADC: /* ADC <-> SBC */
- new_inst = OPCODE_SBC;
- value = inverted;
- break;
-
- case OPCODE_SBC:
- new_inst = OPCODE_ADC;
- value = inverted;
- break;
-
- /* We cannot do anything. */
- default:
- return FAIL;
- }
-
- if (value == (unsigned) FAIL)
- return FAIL;
-
- *instruction &= OPCODE_MASK;
- *instruction |= new_inst << DATA_OP_SHIFT;
- return value;
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE))
+ return;
+ encode_arm_addr_mode_2 (1, /*is_t=*/FALSE);
}
-static int
-data_op2 (char ** str)
+static void
+do_ldstt (void)
{
- int value;
- expressionS expr;
-
- skip_whitespace (* str);
-
- if (reg_required_here (str, 0) != FAIL)
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
{
- if (skip_past_comma (str) == SUCCESS)
- /* Shift operation on register. */
- return decode_shift (str, NO_SHIFT_RESTRICT);
+ constraint (inst.reloc.exp.X_op != O_constant ||
+ inst.reloc.exp.X_add_number != 0,
+ _("this instruction requires a post-indexed address"));
- return SUCCESS;
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
}
- else
- {
- /* Immediate expression. */
- if (is_immediate_prefix (**str))
- {
- (*str)++;
- inst.error = NULL;
-
- if (my_get_expression (&inst.reloc.exp, str))
- return FAIL;
-
- if (inst.reloc.exp.X_add_symbol)
- {
- inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
- inst.reloc.pc_rel = 0;
- }
- else
- {
- if (skip_past_comma (str) == SUCCESS)
- {
- /* #x, y -- ie explicit rotation by Y. */
- if (my_get_expression (&expr, str))
- return FAIL;
-
- if (expr.X_op != O_constant)
- {
- inst.error = _("constant expression expected");
- return FAIL;
- }
-
- /* Rotate must be a multiple of 2. */
- if (((unsigned) expr.X_add_number) > 30
- || (expr.X_add_number & 1) != 0
- || ((unsigned) inst.reloc.exp.X_add_number) > 255)
- {
- inst.error = _("invalid constant");
- return FAIL;
- }
- inst.instruction |= INST_IMMEDIATE;
- inst.instruction |= inst.reloc.exp.X_add_number;
- inst.instruction |= expr.X_add_number << 7;
- return SUCCESS;
- }
-
- /* Implicit rotation, select a suitable one. */
- value = validate_immediate (inst.reloc.exp.X_add_number);
-
- if (value == FAIL)
- {
- /* Can't be done. Perhaps the code reads something like
- "add Rd, Rn, #-n", where "sub Rd, Rn, #n" would be OK. */
- if ((value = negate_data_op (&inst.instruction,
- inst.reloc.exp.X_add_number))
- == FAIL)
- {
- inst.error = _("invalid constant");
- return FAIL;
- }
- }
-
- inst.instruction |= value;
- }
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_2 (1, /*is_t=*/TRUE);
+}
- inst.instruction |= INST_IMMEDIATE;
- return SUCCESS;
- }
+/* Halfword and signed-byte load/store operations. */
- (*str)++;
- inst.error = _("register or shift expression expected");
- return FAIL;
- }
+static void
+do_ldstv4 (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/TRUE))
+ return;
+ encode_arm_addr_mode_3 (1, /*is_t=*/FALSE);
}
-static int
-fp_op2 (char ** str)
+static void
+do_ldsttv4 (void)
{
- skip_whitespace (* str);
-
- if (fp_reg_required_here (str, 0) != FAIL)
- return SUCCESS;
- else
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
{
- /* Immediate expression. */
- if (*((*str)++) == '#')
- {
- int i;
-
- inst.error = NULL;
-
- skip_whitespace (* str);
-
- /* First try and match exact strings, this is to guarantee
- that some formats will work even for cross assembly. */
-
- for (i = 0; fp_const[i]; i++)
- {
- if (strncmp (*str, fp_const[i], strlen (fp_const[i])) == 0)
- {
- char *start = *str;
-
- *str += strlen (fp_const[i]);
- if (is_end_of_line[(unsigned char) **str])
- {
- inst.instruction |= i + 8;
- return SUCCESS;
- }
- *str = start;
- }
- }
+ constraint (inst.reloc.exp.X_op != O_constant ||
+ inst.reloc.exp.X_add_number != 0,
+ _("this instruction requires a post-indexed address"));
- /* Just because we didn't get a match doesn't mean that the
- constant isn't valid, just that it is in a format that we
- don't automatically recognize. Try parsing it with
- the standard expression routines. */
- if ((i = my_get_float_expression (str)) >= 0)
- {
- inst.instruction |= i + 8;
- return SUCCESS;
- }
-
- inst.error = _("invalid floating point immediate expression");
- return FAIL;
- }
- inst.error =
- _("floating point register or immediate expression expected");
- return FAIL;
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
}
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_3 (1, /*is_t=*/TRUE);
}
+/* Co-processor register load/store.
+ Format: <LDC|STC>{cond}[L] CP#,CRd,<address> */
static void
-do_arit (char * str)
+do_lstc (void)
{
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL
- || skip_past_comma (&str) == FAIL
- || data_op2 (&str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 12;
+ encode_arm_cp_address (2, TRUE, TRUE, 0);
}
static void
-do_adr (char * str)
+do_mlas (void)
{
- /* This is a pseudo-op of the form "adr rd, label" to be converted
- into a relative address of the form "add rd, pc, #label-.-8". */
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || my_get_expression (&inst.reloc.exp, &str))
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* This restriction does not apply to mls (nor to mla in v6, but
+ that's hard to detect at present). */
+ if (inst.operands[0].reg == inst.operands[1].reg
+ && !(inst.instruction & 0x00400000))
+ as_tsktsk (_("rd and rm should be different in mla"));
- /* Frag hacking will turn this into a sub instruction if the offset turns
- out to be negative. */
- inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
-#ifndef TE_WINCE
- inst.reloc.exp.X_add_number -= 8; /* PC relative adjust. */
-#endif
- inst.reloc.pc_rel = 1;
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 12;
- end_of_line (str);
}
static void
-do_adrl (char * str)
+do_mov (void)
{
- /* This is a pseudo-op of the form "adrl rd, label" to be converted
- into a relative address of the form:
- add rd, pc, #low(label-.-8)"
- add rd, rd, #high(label-.-8)" */
-
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || my_get_expression (&inst.reloc.exp, &str))
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
-
- return;
- }
-
- end_of_line (str);
- /* Frag hacking will turn this into a sub instruction if the offset turns
- out to be negative. */
- inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE;
-#ifndef TE_WINCE
- inst.reloc.exp.X_add_number -= 8; /* PC relative adjust */
-#endif
- inst.reloc.pc_rel = 1;
- inst.size = INSN_SIZE * 2;
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_shifter_operand (1);
}
+/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>. */
static void
-do_cmp (char * str)
+do_mov16 (void)
{
- skip_whitespace (str);
-
- if (reg_required_here (&str, 16) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || data_op2 (&str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ /* The value is in two pieces: 0:11, 16:19. */
+ inst.instruction |= (inst.operands[1].imm & 0x00000fff);
+ inst.instruction |= (inst.operands[1].imm & 0x0000f000) << 4;
}
static void
-do_mov (char * str)
+do_mrs (void)
{
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || data_op2 (&str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
+ constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
+ != (PSR_c|PSR_f),
+ _("'CPSR' or 'SPSR' expected"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= (inst.operands[1].imm & SPSR_BIT);
}
+/* Two possible forms:
+ "{C|S}PSR_<field>, Rm",
+ "{C|S}PSR_f, #expression". */
+
static void
-do_ldst (char * str)
+do_msr (void)
{
- int pre_inc = 0;
- int conflict_reg;
- int value;
-
- skip_whitespace (str);
-
- if ((conflict_reg = reg_required_here (&str, 12)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = _("address expected");
- return;
- }
-
- if (*str == '[')
- {
- int reg;
-
- str++;
-
- skip_whitespace (str);
-
- if ((reg = reg_required_here (&str, 16)) == FAIL)
- return;
-
- /* Conflicts can occur on stores as well as loads. */
- conflict_reg = (conflict_reg == reg);
-
- skip_whitespace (str);
-
- if (*str == ']')
- {
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend (&str) == FAIL)
- return;
- if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
- }
- else
- {
- /* [Rn] */
- skip_whitespace (str);
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
- str++;
- inst.instruction |= WRITE_BACK;
- }
-
- inst.instruction |= INDEX_UP;
- pre_inc = 1;
- }
- }
- else
- {
- /* [Rn,...] */
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return;
- }
-
- pre_inc = 1;
- if (ldst_extend (&str) == FAIL)
- return;
-
- skip_whitespace (str);
-
- if (*str++ != ']')
- {
- inst.error = _("missing ]");
- return;
- }
-
- skip_whitespace (str);
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
- str++;
- inst.instruction |= WRITE_BACK;
- }
- }
- }
- else if (*str == '=')
- {
- if ((inst.instruction & LOAD_BIT) == 0)
- {
- inst.error = _("invalid pseudo operation");
- return;
- }
-
- /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op. */
- str++;
-
- skip_whitespace (str);
-
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
-
- if (inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = _("constant expression expected");
- return;
- }
-
- if (inst.reloc.exp.X_op == O_constant)
- {
- value = validate_immediate (inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mov instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= (INST_IMMEDIATE
- | (OPCODE_MOV << DATA_OP_SHIFT));
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
-
- value = validate_immediate (~inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mvn instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= (INST_IMMEDIATE
- | (OPCODE_MVN << DATA_OP_SHIFT));
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
- }
-
- /* Insert into literal pool. */
- if (add_to_lit_pool () == FAIL)
- {
- if (!inst.error)
- inst.error = _("literal pool insertion failed");
- return;
- }
-
- /* Change the instruction exp to point to the pool. */
- inst.reloc.type = BFD_RELOC_ARM_LITERAL;
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
+ inst.instruction |= inst.operands[0].imm;
+ if (inst.operands[1].isreg)
+ inst.instruction |= inst.operands[1].reg;
else
{
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
-
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-#ifndef TE_WINCE
- /* PC rel adjust. */
- inst.reloc.exp.X_add_number -= 8;
-#endif
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
+ inst.instruction |= INST_IMMEDIATE;
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
}
-
- inst.instruction |= (pre_inc ? PRE_INDEX : 0);
- end_of_line (str);
}
static void
-do_ldstt (char * str)
+do_mul (void)
{
- int conflict_reg;
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
- skip_whitespace (str);
-
- if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (& str) == FAIL)
- {
- inst.error = _("address expected");
- return;
- }
-
- if (*str == '[')
- {
- int reg;
-
- str++;
-
- skip_whitespace (str);
-
- if ((reg = reg_required_here (&str, 16)) == FAIL)
- return;
-
- /* ldrt/strt always use post-indexed addressing, so if the base is
- the same as Rd, we warn. */
- if (conflict_reg == reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
-
- skip_whitespace (str);
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rd and rm should be different in mul"));
+}
- if (*str == ']')
- {
- str ++;
+/* Long Multiply Parser
+ UMULL RdLo, RdHi, Rm, Rs
+ SMULL RdLo, RdHi, Rm, Rs
+ UMLAL RdLo, RdHi, Rm, Rs
+ SMLAL RdLo, RdHi, Rm, Rs. */
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend (&str) == FAIL)
- return;
- }
- else
- {
- /* [Rn] */
- skip_whitespace (str);
+static void
+do_mull (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].reg << 8;
- /* Skip a write-back '!'. */
- if (*str == '!')
- str++;
+ /* rdhi, rdlo and rm must all be different. */
+ if (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[1].reg == inst.operands[2].reg)
+ as_tsktsk (_("rdhi, rdlo and rm must all be different"));
+}
- inst.instruction |= INDEX_UP;
- }
- }
- else
- {
- inst.error = _("post-indexed expression expected");
- return;
- }
- }
- else
+static void
+do_nop (void)
+{
+ if (inst.operands[0].present)
{
- inst.error = _("post-indexed expression expected");
- return;
+ /* Architectural NOP hints are CPSR sets with no bits selected. */
+ inst.instruction &= 0xf0000000;
+ inst.instruction |= 0x0320f000 + inst.operands[0].imm;
}
-
- end_of_line (str);
}
-/* Halfword and signed-byte load/store operations. */
+/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
+ PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
+ Condition defaults to COND_ALWAYS.
+ Error if Rd, Rn or Rm are R15. */
static void
-do_ldstv4 (char * str)
+do_pkhbt (void)
{
- int pre_inc = 0;
- int conflict_reg;
- int value;
-
- skip_whitespace (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
+}
- if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+/* ARM V6 PKHTB (Argument Parse). */
- if (skip_past_comma (& str) == FAIL)
+static void
+do_pkhtb (void)
+{
+ if (!inst.operands[3].present)
{
- inst.error = _("address expected");
- return;
+ /* If the shift specifier is omitted, turn the instruction
+ into pkhbt rd, rm, rn. */
+ inst.instruction &= 0xfff00010;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
}
-
- if (*str == '[')
+ else
{
- int reg;
-
- str++;
-
- skip_whitespace (str);
-
- if ((reg = reg_required_here (&str, 16)) == FAIL)
- return;
-
- /* Conflicts can occur on stores as well as loads. */
- conflict_reg = (conflict_reg == reg);
-
- skip_whitespace (str);
-
- if (*str == ']')
- {
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend_v4 (&str) == FAIL)
- return;
- if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
- }
- else
- {
- /* [Rn] */
- inst.instruction |= HWOFFSET_IMM;
-
- skip_whitespace (str);
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
- str++;
- inst.instruction |= WRITE_BACK;
- }
-
- inst.instruction |= INDEX_UP;
- pre_inc = 1;
- }
- }
- else
- {
- /* [Rn,...] */
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return;
- }
-
- pre_inc = 1;
- if (ldst_extend_v4 (&str) == FAIL)
- return;
-
- skip_whitespace (str);
-
- if (*str++ != ']')
- {
- inst.error = _("missing ]");
- return;
- }
-
- skip_whitespace (str);
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
- str++;
- inst.instruction |= WRITE_BACK;
- }
- }
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ encode_arm_shift (3);
}
- else if (*str == '=')
- {
- if ((inst.instruction & LOAD_BIT) == 0)
- {
- inst.error = _("invalid pseudo operation");
- return;
- }
-
- /* XXX Does this work correctly for half-word/byte ops? */
- /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op. */
- str++;
-
- skip_whitespace (str);
-
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
-
- if (inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = _("constant expression expected");
- return;
- }
-
- if (inst.reloc.exp.X_op == O_constant)
- {
- value = validate_immediate (inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mov instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
-
- value = validate_immediate (~ inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mvn instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
- }
+}
- /* Insert into literal pool. */
- if (add_to_lit_pool () == FAIL)
- {
- if (!inst.error)
- inst.error = _("literal pool insertion failed");
- return;
- }
+/* ARMv5TE: Preload-Cache
- /* Change the instruction exp to point to the pool. */
- inst.instruction |= HWOFFSET_IMM;
- inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
- else
- {
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
+ PLD <addr_mode>
- inst.instruction |= HWOFFSET_IMM;
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-#ifndef TE_WINCE
- /* PC rel adjust. */
- inst.reloc.exp.X_add_number -= 8;
-#endif
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
+ Syntactically, like LDR with B=1, W=0, L=1. */
- inst.instruction |= (pre_inc ? PRE_INDEX : 0);
- end_of_line (str);
+static void
+do_pld (void)
+{
+ constraint (!inst.operands[0].isreg,
+ _("'[' expected after PLD mnemonic"));
+ constraint (inst.operands[0].postind,
+ _("post-indexed expression used in preload instruction"));
+ constraint (inst.operands[0].writeback,
+ _("writeback used in preload instruction"));
+ constraint (!inst.operands[0].preind,
+ _("unindexed addressing used in preload instruction"));
+ inst.instruction |= inst.operands[0].reg;
+ encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
}
static void
-do_ldsttv4 (char * str)
+do_push_pop (void)
{
- int conflict_reg;
-
- skip_whitespace (str);
-
- if ((conflict_reg = reg_required_here (& str, 12)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (& str) == FAIL)
- {
- inst.error = _("address expected");
- return;
- }
-
- if (*str == '[')
- {
- int reg;
-
- str++;
-
- skip_whitespace (str);
-
- if ((reg = reg_required_here (&str, 16)) == FAIL)
- return;
-
- /* ldrt/strt always use post-indexed addressing, so if the base is
- the same as Rd, we warn. */
- if (conflict_reg == reg)
- as_warn (_("%s register same as write-back base"),
- ((inst.instruction & LOAD_BIT)
- ? _("destination") : _("source")));
-
- skip_whitespace (str);
-
- if (*str == ']')
- {
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend_v4 (&str) == FAIL)
- return;
- }
- else
- {
- /* [Rn] */
- skip_whitespace (str);
-
- /* Skip a write-back '!'. */
- if (*str == '!')
- str++;
-
- inst.instruction |= (INDEX_UP|HWOFFSET_IMM);
- }
- }
- else
- {
- inst.error = _("post-indexed expression expected");
- return;
- }
- }
- else
- {
- inst.error = _("post-indexed expression expected");
- return;
- }
-
- end_of_line (str);
+ inst.operands[1] = inst.operands[0];
+ memset (&inst.operands[0], 0, sizeof inst.operands[0]);
+ inst.operands[0].isreg = 1;
+ inst.operands[0].writeback = 1;
+ inst.operands[0].reg = REG_SP;
+ do_ldmstm ();
}
+/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
+ word at the specified address and the following word
+ respectively.
+ Unconditionally executed.
+ Error if Rn is R15. */
-static long
-reg_list (char ** strp)
+static void
+do_rfe (void)
{
- char * str = * strp;
- long range = 0;
- int another_range;
-
- /* We come back here if we get ranges concatenated by '+' or '|'. */
- do
- {
- another_range = 0;
-
- if (*str == '{')
- {
- int in_range = 0;
- int cur_reg = -1;
-
- str++;
- do
- {
- int reg;
-
- skip_whitespace (str);
-
- if ((reg = reg_required_here (& str, -1)) == FAIL)
- return FAIL;
-
- if (in_range)
- {
- int i;
-
- if (reg <= cur_reg)
- {
- inst.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--;
- skip_whitespace (str);
-
- if (*str++ != '}')
- {
- inst.error = _("missing `}'");
- return FAIL;
- }
- }
- else
- {
- expressionS expr;
-
- if (my_get_expression (&expr, &str))
- return FAIL;
+ inst.instruction |= inst.operands[0].reg << 16;
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+}
- if (expr.X_op == O_constant)
- {
- if (expr.X_add_number
- != (expr.X_add_number & 0x0000ffff))
- {
- inst.error = _("invalid register mask");
- return FAIL;
- }
+/* ARM V6 ssat (argument parse). */
- if ((range & expr.X_add_number) != 0)
- {
- int regno = range & expr.X_add_number;
+static void
+do_ssat (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= (inst.operands[1].imm - 1) << 16;
+ inst.instruction |= inst.operands[2].reg;
- regno &= -regno;
- regno = (1 << regno) - 1;
- as_tsktsk
- (_("Warning: duplicated register (r%d) in register list"),
- regno);
- }
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
+}
- range |= expr.X_add_number;
- }
- else
- {
- if (inst.reloc.type != 0)
- {
- inst.error = _("expression too complex");
- return FAIL;
- }
+/* ARM V6 usat (argument parse). */
- memcpy (&inst.reloc.exp, &expr, sizeof (expressionS));
- inst.reloc.type = BFD_RELOC_ARM_MULTI;
- inst.reloc.pc_rel = 0;
- }
- }
+static void
+do_usat (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 16;
+ inst.instruction |= inst.operands[2].reg;
- skip_whitespace (str);
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
+}
- if (*str == '|' || *str == '+')
- {
- str++;
- another_range = 1;
- }
- }
- while (another_range);
+/* ARM V6 ssat16 (argument parse). */
- *strp = str;
- return range;
+static void
+do_ssat16 (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= ((inst.operands[1].imm - 1) << 16);
+ inst.instruction |= inst.operands[2].reg;
}
static void
-do_ldmstm (char * str)
+do_usat16 (void)
{
- int base_reg;
- long range;
-
- skip_whitespace (str);
-
- if ((base_reg = reg_required_here (&str, 16)) == FAIL)
- return;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 16;
+ inst.instruction |= inst.operands[2].reg;
+}
- if (base_reg == REG_PC)
- {
- inst.error = _("r15 not allowed as base register");
- return;
- }
+/* ARM V6 SETEND (argument parse). Sets the E bit in the CPSR while
+ preserving the other bits.
- skip_whitespace (str);
+ setend <endian_specifier>, where <endian_specifier> is either
+ BE or LE. */
- if (*str == '!')
- {
- inst.instruction |= WRITE_BACK;
- str++;
- }
+static void
+do_setend (void)
+{
+ if (inst.operands[0].imm)
+ inst.instruction |= 0x200;
+}
- if (skip_past_comma (&str) == FAIL
- || (range = reg_list (&str)) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+do_shift (void)
+{
+ unsigned int Rm = (inst.operands[1].present
+ ? inst.operands[1].reg
+ : inst.operands[0].reg);
- if (*str == '^')
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= Rm;
+ if (inst.operands[2].isreg) /* Rd, {Rm,} Rs */
{
- str++;
- inst.instruction |= LDM_TYPE_2_OR_3;
+ constraint (inst.operands[0].reg != Rm,
+ _("source1 and dest must be same register"));
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= SHIFT_BY_REG;
}
-
- if (inst.instruction & WRITE_BACK)
- {
- /* Check for unpredictable uses of writeback. */
- if (inst.instruction & LOAD_BIT)
- {
- /* Not allowed in LDM type 2. */
- if ((inst.instruction & LDM_TYPE_2_OR_3)
- && ((range & (1 << REG_PC)) == 0))
- as_warn (_("writeback of base register is UNPREDICTABLE"));
- /* Only allowed if base reg not in list for other types. */
- else if (range & (1 << base_reg))
- as_warn (_("writeback of base register when in register list is UNPREDICTABLE"));
- }
- else /* STM. */
- {
- /* Not allowed for type 2. */
- if (inst.instruction & LDM_TYPE_2_OR_3)
- as_warn (_("writeback of base register is UNPREDICTABLE"));
- /* Only allowed if base reg not in list, or first in list. */
- else if ((range & (1 << base_reg))
- && (range & ((1 << base_reg) - 1)))
- as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
- }
- }
-
- inst.instruction |= range;
- end_of_line (str);
+ else
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
}
static void
-do_smi (char * str)
+do_smi (void)
{
- skip_whitespace (str);
-
- /* Allow optional leading '#'. */
- if (is_immediate_prefix (*str))
- str++;
-
- if (my_get_expression (& inst.reloc.exp, & str))
- return;
-
inst.reloc.type = BFD_RELOC_ARM_SMI;
inst.reloc.pc_rel = 0;
- end_of_line (str);
}
static void
-do_swi (char * str)
+do_swi (void)
{
- skip_whitespace (str);
-
- /* Allow optional leading '#'. */
- if (is_immediate_prefix (*str))
- str++;
-
- if (my_get_expression (& inst.reloc.exp, & str))
- return;
-
inst.reloc.type = BFD_RELOC_ARM_SWI;
inst.reloc.pc_rel = 0;
- end_of_line (str);
}
+/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
+ SMLAxy{cond} Rd,Rm,Rs,Rn
+ SMLAWy{cond} Rd,Rm,Rs,Rn
+ Error if any register is R15. */
+
static void
-do_swap (char * str)
+do_smla (void)
{
- int reg;
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 12;
+}
- skip_whitespace (str);
+/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
+ SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
+ Error if any register is R15.
+ Warning if Rdlo == Rdhi. */
- if ((reg = reg_required_here (&str, 12)) == FAIL)
- return;
+static void
+do_smlal (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].reg << 8;
- if (reg == REG_PC)
- {
- inst.error = _("r15 not allowed in swap");
- return;
- }
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+}
- if (skip_past_comma (&str) == FAIL
- || (reg = reg_required_here (&str, 0)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+/* ARM V5E (El Segundo) signed-multiply (argument parse)
+ SMULxy{cond} Rd,Rm,Rs
+ Error if any register is R15. */
- if (reg == REG_PC)
- {
- inst.error = _("r15 not allowed in swap");
- return;
- }
+static void
+do_smul (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+}
- if (skip_past_comma (&str) == FAIL
- || *str++ != '[')
- {
- inst.error = BAD_ARGS;
- return;
- }
+/* ARM V6 srs (argument parse). */
- skip_whitespace (str);
+static void
+do_srs (void)
+{
+ inst.instruction |= inst.operands[0].imm;
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+}
- if ((reg = reg_required_here (&str, 16)) == FAIL)
- return;
+/* ARM V6 strex (argument parse). */
- if (reg == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+static void
+do_strex (void)
+{
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative,
+ _("instruction does not accept this addressing mode"));
- skip_whitespace (str);
+ constraint (inst.operands[2].reg == REG_PC, BAD_PC);
- if (*str++ != ']')
- {
- inst.error = _("missing ]");
- return;
- }
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("offset must be zero in ARM encoding"));
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.reloc.type = BFD_RELOC_UNUSED;
}
static void
-do_branch (char * str)
+do_strexd (void)
{
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
+ constraint (inst.operands[1].reg % 2 != 0,
+ _("even register required"));
+ constraint (inst.operands[2].present
+ && inst.operands[2].reg != inst.operands[1].reg + 1,
+ _("can only store two consecutive registers"));
+ /* If op 2 were present and equal to PC, this function wouldn't
+ have been called in the first place. */
+ constraint (inst.operands[1].reg == REG_LR, _("r14 not allowed here"));
-#ifdef OBJ_ELF
- {
- char * save_in;
-
- /* ScottB: February 5, 1998 - Check to see of PLT32 reloc
- required for the instruction. */
-
- /* arm_parse_reloc () works on input_line_pointer.
- We actually want to parse the operands to the branch instruction
- passed in 'str'. Save the input pointer and restore it later. */
- save_in = input_line_pointer;
- input_line_pointer = str;
- if (inst.reloc.exp.X_op == O_symbol
- && *str == '('
- && arm_parse_reloc () == BFD_RELOC_ARM_PLT32)
- {
- inst.reloc.type = BFD_RELOC_ARM_PLT32;
- inst.reloc.pc_rel = 0;
- /* Modify str to point to after parsed operands, otherwise
- end_of_line() will complain about the (PLT) left in str. */
- str = input_line_pointer;
- }
- else
- {
- inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH;
- inst.reloc.pc_rel = 1;
- }
- input_line_pointer = save_in;
- }
-#else
- inst.reloc.type = BFD_RELOC_ARM_PCREL_BRANCH;
- inst.reloc.pc_rel = 1;
-#endif /* OBJ_ELF */
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[1].reg + 1
+ || inst.operands[0].reg == inst.operands[3].reg,
+ BAD_OVERLAP);
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[3].reg << 16;
}
+/* ARM V6 SXTAH extracts a 16-bit value from a register, sign
+ extends it to 32-bits, and adds the result to a value in another
+ register. You can specify a rotation by 0, 8, 16, or 24 bits
+ before extracting the 16-bit value.
+ SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
+
static void
-do_cdp (char * str)
+do_sxtah (void)
{
- /* Co-processor data operation.
- Format: CDP{cond} CP#,<expr>,CRd,CRn,CRm{,<expr>} */
- skip_whitespace (str);
-
- if (co_proc_number (&str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 10;
+}
- if (skip_past_comma (&str) == FAIL
- || cp_opc_expr (&str, 20,4) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+/* ARM V6 SXTH.
- if (skip_past_comma (&str) == FAIL
- || cp_reg_required_here (&str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
- if (skip_past_comma (&str) == FAIL
- || cp_reg_required_here (&str, 16) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+do_sxth (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 10;
+}
+
+/* VFP instructions. In a logical order: SP variant first, monad
+ before dyad, arithmetic then move then load/store. */
- if (skip_past_comma (&str) == FAIL
- || cp_reg_required_here (&str, 0) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+do_vfp_sp_monadic (void)
+{
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
- if (skip_past_comma (&str) == SUCCESS)
- {
- if (cp_opc_expr (&str, 5, 3) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
- }
+static void
+do_vfp_sp_dyadic (void)
+{
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sn);
+ encode_arm_vfp_sp_reg (inst.operands[2].reg, VFP_REG_Sm);
+}
- end_of_line (str);
+static void
+do_vfp_sp_compare_z (void)
+{
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
}
static void
-do_lstc (char * str)
+do_vfp_dp_sp_cvt (void)
{
- /* Co-processor register load/store.
- Format: <LDC|STC{cond}[L] CP#,CRd,<address> */
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
- skip_whitespace (str);
+static void
+do_vfp_sp_dp_cvt (void)
+{
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ inst.instruction |= inst.operands[1].reg;
+}
- if (co_proc_number (&str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+do_vfp_reg_from_sp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sn);
+}
- if (skip_past_comma (&str) == FAIL
- || cp_reg_required_here (&str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+do_vfp_reg2_from_sp2 (void)
+{
+ constraint (inst.operands[2].imm != 2,
+ _("only two consecutive VFP SP registers allowed here"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ encode_arm_vfp_sp_reg (inst.operands[2].reg, VFP_REG_Sm);
+}
- if (skip_past_comma (&str) == FAIL
- || cp_address_required_here (&str, CP_WB_OK) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+do_vfp_sp_from_reg (void)
+{
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sn);
+ inst.instruction |= inst.operands[1].reg << 12;
+}
- end_of_line (str);
+static void
+do_vfp_sp2_from_reg2 (void)
+{
+ constraint (inst.operands[0].imm != 2,
+ _("only two consecutive VFP SP registers allowed here"));
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sm);
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
}
static void
-do_co_reg (char * str)
+do_vfp_sp_ldst (void)
{
- /* Co-processor register transfer.
- Format: <MCR|MRC>{cond} CP#,<expr1>,Rd,CRn,CRm{,<expr2>} */
+ encode_arm_vfp_sp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
- skip_whitespace (str);
+static void
+do_vfp_dp_ldst (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
- if (co_proc_number (&str) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
- if (skip_past_comma (&str) == FAIL
- || cp_opc_expr (&str, 21, 3) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+vfp_sp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+ else
+ constraint (ldstm_type != VFP_LDSTMIA,
+ _("this addressing mode requires base-register writeback"));
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_vfp_sp_reg (inst.operands[1].reg, VFP_REG_Sd);
+ inst.instruction |= inst.operands[1].imm;
+}
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static void
+vfp_dp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+ int count;
- if (skip_past_comma (&str) == FAIL
- || cp_reg_required_here (&str, 16) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+ else
+ constraint (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX,
+ _("this addressing mode requires base-register writeback"));
- if (skip_past_comma (&str) == FAIL
- || cp_reg_required_here (&str, 0) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 12;
- if (skip_past_comma (&str) == SUCCESS)
- {
- if (cp_opc_expr (&str, 5, 3) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
- }
+ count = inst.operands[1].imm << 1;
+ if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
+ count += 1;
- end_of_line (str);
+ inst.instruction |= count;
}
static void
-do_fpa_ctrl (char * str)
+do_vfp_sp_ldstmia (void)
{
- /* FP control registers.
- Format: <WFS|RFS|WFC|RFC>{cond} Rn */
-
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ vfp_sp_ldstm (VFP_LDSTMIA);
}
static void
-do_fpa_ldst (char * str)
+do_vfp_sp_ldstmdb (void)
{
- skip_whitespace (str);
-
- if (fp_reg_required_here (&str, 12) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || cp_address_required_here (&str, CP_WB_OK) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ vfp_sp_ldstm (VFP_LDSTMDB);
}
static void
-do_fpa_ldmstm (char * str)
+do_vfp_dp_ldstmia (void)
{
- int num_regs;
-
- skip_whitespace (str);
-
- if (fp_reg_required_here (&str, 12) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ vfp_dp_ldstm (VFP_LDSTMIA);
+}
- /* Get Number of registers to transfer. */
- if (skip_past_comma (&str) == FAIL
- || my_get_expression (&inst.reloc.exp, &str))
- {
- if (! inst.error)
- inst.error = _("constant expression expected");
- return;
- }
+static void
+do_vfp_dp_ldstmdb (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMDB);
+}
- if (inst.reloc.exp.X_op != O_constant)
- {
- inst.error = _("constant value required for number of registers");
- return;
- }
+static void
+do_vfp_xp_ldstmia (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMIAX);
+}
- num_regs = inst.reloc.exp.X_add_number;
+static void
+do_vfp_xp_ldstmdb (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMDBX);
+}
+
+/* FPA instructions. Also in a logical order. */
- if (num_regs < 1 || num_regs > 4)
- {
- inst.error = _("number of registers must be in the range [1:4]");
- return;
- }
+static void
+do_fpa_cmp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+}
- switch (num_regs)
+static void
+do_fpa_ldmstm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ switch (inst.operands[1].imm)
{
- case 1:
- inst.instruction |= CP_T_X;
- break;
- case 2:
- inst.instruction |= CP_T_Y;
- break;
- case 3:
- inst.instruction |= CP_T_Y | CP_T_X;
- break;
- case 4:
- break;
- default:
- abort ();
+ case 1: inst.instruction |= CP_T_X; break;
+ case 2: inst.instruction |= CP_T_Y; break;
+ case 3: inst.instruction |= CP_T_Y | CP_T_X; break;
+ case 4: break;
+ default: abort ();
}
- if (inst.instruction & (CP_T_Pre | CP_T_UD)) /* ea/fd format. */
+ if (inst.instruction & (PRE_INDEX | INDEX_UP))
{
- int reg;
- int write_back;
- int offset;
-
/* The instruction specified "ea" or "fd", so we can only accept
[Rn]{!}. The instruction does not really support stacking or
unstacking, so we have to emulate these by setting appropriate
bits and offsets. */
- if (skip_past_comma (&str) == FAIL
- || *str != '[')
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction does not support indexing"));
- str++;
- skip_whitespace (str);
+ if ((inst.instruction & PRE_INDEX) || inst.operands[2].writeback)
+ inst.reloc.exp.X_add_number = 12 * inst.operands[1].imm;
- if ((reg = reg_required_here (&str, 16)) == FAIL)
- return;
-
- skip_whitespace (str);
-
- if (*str != ']')
- {
- inst.error = BAD_ARGS;
- return;
- }
-
- str++;
- if (*str == '!')
- {
- write_back = 1;
- str++;
- if (reg == REG_PC)
- {
- inst.error =
- _("r15 not allowed as base register with write-back");
- return;
- }
- }
- else
- write_back = 0;
+ if (!(inst.instruction & INDEX_UP))
+ inst.reloc.exp.X_add_number = -inst.reloc.exp.X_add_number;
- if (inst.instruction & CP_T_Pre)
- {
- /* Pre-decrement. */
- offset = 3 * num_regs;
- if (write_back)
- inst.instruction |= CP_T_WB;
- }
- else
+ if (!(inst.instruction & PRE_INDEX) && inst.operands[2].writeback)
{
- /* Post-increment. */
- if (write_back)
- {
- inst.instruction |= CP_T_WB;
- offset = 3 * num_regs;
- }
- else
- {
- /* No write-back, so convert this into a standard pre-increment
- instruction -- aesthetically more pleasing. */
- inst.instruction |= CP_T_Pre | CP_T_UD;
- offset = 0;
- }
+ inst.operands[2].preind = 0;
+ inst.operands[2].postind = 1;
}
-
- inst.instruction |= offset;
- }
- else if (skip_past_comma (&str) == FAIL
- || cp_address_required_here (&str, CP_WB_OK) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
}
- end_of_line (str);
+ encode_arm_cp_address (2, TRUE, TRUE, 0);
}
+
+/* iWMMXt instructions: strictly in alphabetical order. */
static void
-do_fpa_dyadic (char * str)
+do_iwmmxt_tandorc (void)
{
- skip_whitespace (str);
-
- if (fp_reg_required_here (&str, 12) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || fp_reg_required_here (&str, 16) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || fp_op2 (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ constraint (inst.operands[0].reg != REG_PC, _("only r15 allowed here"));
}
static void
-do_fpa_monadic (char * str)
+do_iwmmxt_textrc (void)
{
- skip_whitespace (str);
-
- if (fp_reg_required_here (&str, 12) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || fp_op2 (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm;
}
static void
-do_fpa_cmp (char * str)
+do_iwmmxt_textrm (void)
{
- skip_whitespace (str);
-
- if (fp_reg_required_here (&str, 16) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || fp_op2 (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].imm;
}
static void
-do_fpa_from_reg (char * str)
+do_iwmmxt_tinsr (void)
{
- skip_whitespace (str);
-
- if (fp_reg_required_here (&str, 16) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].imm;
}
static void
-do_fpa_to_reg (char * str)
+do_iwmmxt_tmia (void)
{
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL
- || fp_reg_required_here (&str, 0) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 5;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
}
-/* Encode a VFP SP register number. */
-
static void
-vfp_sp_encode_reg (int reg, enum vfp_sp_reg_pos pos)
+do_iwmmxt_waligni (void)
{
- switch (pos)
- {
- case VFP_REG_Sd:
- inst.instruction |= ((reg >> 1) << 12) | ((reg & 1) << 22);
- break;
-
- case VFP_REG_Sn:
- inst.instruction |= ((reg >> 1) << 16) | ((reg & 1) << 7);
- break;
-
- case VFP_REG_Sm:
- inst.instruction |= ((reg >> 1) << 0) | ((reg & 1) << 5);
- break;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 20;
+}
- default:
- abort ();
- }
+static void
+do_iwmmxt_wmov (void)
+{
+ /* WMOV rD, rN is an alias for WOR rD, rN, rN. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
}
-static int
-vfp_sp_reg_required_here (char ** str,
- enum vfp_sp_reg_pos pos)
+static void
+do_iwmmxt_wldstbh (void)
{
- int reg;
- char * start = *str;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.reloc.exp.X_add_number *= 4;
+ encode_arm_cp_address (1, TRUE, FALSE, BFD_RELOC_ARM_CP_OFF_IMM_S2);
+}
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_SN].htab)) != FAIL)
+static void
+do_iwmmxt_wldstw (void)
+{
+ /* RIWR_RIWC clears .isreg for a control register. */
+ if (!inst.operands[0].isreg)
{
- vfp_sp_encode_reg (reg, pos);
- return reg;
+ constraint (inst.cond != COND_ALWAYS, BAD_COND);
+ inst.instruction |= 0xf0000000;
}
- /* In the few cases where we might be able to accept something else
- this error can be overridden. */
- inst.error = _(all_reg_maps[REG_TYPE_SN].expected);
-
- /* Restore the start point. */
- *str = start;
- return FAIL;
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, TRUE, 0);
}
-static int
-vfp_dp_reg_required_here (char ** str,
- enum vfp_dp_reg_pos pos)
+static void
+do_iwmmxt_wldstd (void)
{
- int reg;
- char * start = *str;
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, FALSE, BFD_RELOC_ARM_CP_OFF_IMM_S2);
+}
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_DN].htab)) != FAIL)
- {
- switch (pos)
- {
- case VFP_REG_Dd:
- inst.instruction |= reg << 12;
- break;
+static void
+do_iwmmxt_wshufh (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= ((inst.operands[2].imm & 0xf0) << 16);
+ inst.instruction |= (inst.operands[2].imm & 0x0f);
+}
- case VFP_REG_Dn:
- inst.instruction |= reg << 16;
- break;
+static void
+do_iwmmxt_wzero (void)
+{
+ /* WZERO reg is an alias for WANDN reg, reg, reg. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[0].reg << 16;
+}
+
+/* Cirrus Maverick instructions. Simple 2-, 3-, and 4-register
+ operations first, then control, shift, and load/store. */
- case VFP_REG_Dm:
- inst.instruction |= reg << 0;
- break;
+/* Insns like "foo X,Y,Z". */
- default:
- abort ();
- }
- return reg;
- }
+static void
+do_mav_triple (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
- /* In the few cases where we might be able to accept something else
- this error can be overridden. */
- inst.error = _(all_reg_maps[REG_TYPE_DN].expected);
+/* Insns like "foo W,X,Y,Z".
+ where W=MVAX[0:3] and X,Y,Z=MVFX[0:15]. */
- /* Restore the start point. */
- *str = start;
- return FAIL;
+static void
+do_mav_quad (void)
+{
+ inst.instruction |= inst.operands[0].reg << 5;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.instruction |= inst.operands[3].reg;
}
+/* cfmvsc32<cond> DSPSC,MVDX[15:0]. */
static void
-do_vfp_sp_monadic (char * str)
+do_mav_dspsc (void)
{
- skip_whitespace (str);
-
- if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL
- || vfp_sp_reg_required_here (&str, VFP_REG_Sm) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[1].reg << 12;
}
+/* Maverick shift immediate instructions.
+ cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
+ cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0]. */
+
static void
-do_vfp_dp_monadic (char * str)
+do_mav_shift (void)
{
- skip_whitespace (str);
+ int imm = inst.operands[2].imm;
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
- return;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
- if (skip_past_comma (&str) == FAIL
- || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
+ Bits 5-7 of the insn should have bits 4-6 of the immediate.
+ Bit 4 should be 0. */
+ imm = (imm & 0xf) | ((imm & 0x70) << 1);
- end_of_line (str);
+ inst.instruction |= imm;
}
+
+/* XScale instructions. Also sorted arithmetic before move. */
+
+/* Xscale multiply-accumulate (argument parse)
+ MIAcc acc0,Rm,Rs
+ MIAPHcc acc0,Rm,Rs
+ MIAxycc acc0,Rm,Rs. */
static void
-do_vfp_sp_dyadic (char * str)
+do_xsc_mia (void)
{
- skip_whitespace (str);
-
- if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
- return;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
- if (skip_past_comma (&str) == FAIL
- || vfp_sp_reg_required_here (&str, VFP_REG_Sn) == FAIL
- || skip_past_comma (&str) == FAIL
- || vfp_sp_reg_required_here (&str, VFP_REG_Sm) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+/* Xscale move-accumulator-register (argument parse)
- end_of_line (str);
-}
+ MARcc acc0,RdLo,RdHi. */
static void
-do_vfp_dp_dyadic (char * str)
+do_xsc_mar (void)
{
- skip_whitespace (str);
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
- return;
+/* Xscale move-register-accumulator (argument parse)
- if (skip_past_comma (&str) == FAIL
- || vfp_dp_reg_required_here (&str, VFP_REG_Dn) == FAIL
- || skip_past_comma (&str) == FAIL
- || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ MRAcc RdLo,RdHi,acc0. */
- end_of_line (str);
+static void
+do_xsc_mra (void)
+{
+ constraint (inst.operands[0].reg == inst.operands[1].reg, BAD_OVERLAP);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
}
+
+/* Encoding functions relevant only to Thumb. */
+
+/* inst.operands[i] is a shifted-register operand; encode
+ it into inst.instruction in the format used by Thumb32. */
static void
-do_vfp_reg_from_sp (char * str)
+encode_thumb32_shifted_operand (int i)
{
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL)
- return;
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[i].shift_kind;
- if (skip_past_comma (&str) == FAIL
- || vfp_sp_reg_required_here (&str, VFP_REG_Sn) == FAIL)
+ inst.instruction |= inst.operands[i].reg;
+ if (shift == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 4;
+ else
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
- end_of_line (str);
-}
+ constraint (value > 32
+ || (value == 32 && (shift == SHIFT_LSL
+ || shift == SHIFT_ROR)),
+ _("shift expression is too large"));
-/* 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. Double precision registers are matched if DP is nonzero. */
+ if (value == 0)
+ shift = SHIFT_LSL;
+ else if (value == 32)
+ value = 0;
-static int
-vfp_parse_reg_list (char **str, int *pbase, int dp)
-{
- int base_reg;
- int new_base;
- int regtype;
- int max_regs;
- int count = 0;
- int warned = 0;
- unsigned long mask = 0;
- int i;
+ inst.instruction |= shift << 4;
+ inst.instruction |= (value & 0x1c) << 10;
+ inst.instruction |= (value & 0x03) << 6;
+ }
+}
- if (**str != '{')
- return FAIL;
- (*str)++;
- skip_whitespace (*str);
+/* inst.operands[i] was set up by parse_address. Encode it into a
+ Thumb32 format load or store instruction. Reject forms that cannot
+ be used with such instructions. If is_t is true, reject forms that
+ cannot be used with a T instruction; if is_d is true, reject forms
+ that cannot be used with a D instruction. */
- if (dp)
- {
- regtype = REG_TYPE_DN;
- max_regs = 16;
- }
- else
- {
- regtype = REG_TYPE_SN;
- max_regs = 32;
- }
+static void
+encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
+{
+ bfd_boolean is_pc = (inst.operands[i].reg == REG_PC);
- base_reg = max_regs;
+ constraint (!inst.operands[i].isreg,
+ _("Thumb does not support the ldr =N pseudo-operation"));
- do
+ inst.instruction |= inst.operands[i].reg << 16;
+ if (inst.operands[i].immisreg)
{
- new_base = arm_reg_parse (str, all_reg_maps[regtype].htab);
- if (new_base == FAIL)
+ constraint (is_pc, _("cannot use register index with PC-relative addressing"));
+ constraint (is_t || is_d, _("cannot use register index with this instruction"));
+ constraint (inst.operands[i].negative,
+ _("Thumb does not support negative register indexing"));
+ constraint (inst.operands[i].postind,
+ _("Thumb does not support register post-indexing"));
+ constraint (inst.operands[i].writeback,
+ _("Thumb does not support register indexing with writeback"));
+ constraint (inst.operands[i].shifted && inst.operands[i].shift_kind != SHIFT_LSL,
+ _("Thumb supports only LSL in shifted register indexing"));
+
+ inst.instruction |= inst.operands[1].imm;
+ if (inst.operands[i].shifted)
{
- inst.error = _(all_reg_maps[regtype].expected);
- return FAIL;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ constraint (inst.reloc.exp.X_add_number < 0 || inst.reloc.exp.X_add_number > 3,
+ _("shift out of range"));
+ inst.instruction |= inst.reloc.exp.X_op << 4;
}
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
+ else if (inst.operands[i].preind)
+ {
+ constraint (is_pc && inst.operands[i].writeback,
+ _("cannot use writeback with PC-relative addressing"));
+ constraint (is_t && inst.operands[1].writeback,
+ _("cannot use writeback with this instruction"));
- if (new_base < base_reg)
- base_reg = new_base;
-
- if (mask & (1 << new_base))
+ if (is_d)
{
- inst.error = _("invalid register list");
- return FAIL;
+ inst.instruction |= 0x01000000;
+ if (inst.operands[i].writeback)
+ inst.instruction |= 0x00200000;
}
-
- if ((mask >> new_base) != 0 && ! warned)
+ else
{
- as_tsktsk (_("register list not in ascending order"));
- warned = 1;
+ inst.instruction |= 0x00000c00;
+ if (inst.operands[i].writeback)
+ inst.instruction |= 0x00000100;
}
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+ inst.reloc.pc_rel = is_pc;
+ }
+ else if (inst.operands[i].postind)
+ {
+ assert (inst.operands[i].writeback);
+ constraint (is_pc, _("cannot use post-indexing with PC-relative addressing"));
+ constraint (is_t, _("cannot use post-indexing with this instruction"));
- mask |= 1 << new_base;
- count++;
+ if (is_d)
+ inst.instruction |= 0x00200000;
+ else
+ inst.instruction |= 0x00000900;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
+ }
+ else /* unindexed - only for coprocessor */
+ inst.error = _("instruction does not accept unindexed addressing");
+}
+
+/* Table of Thumb instructions which exist in both 16- and 32-bit
+ encodings (the latter only in post-V6T2 cores). The index is the
+ value used in the insns table below. When there is more than one
+ possible 16-bit encoding for the instruction, this table always
+ holds variant (1). */
+#define T16_32_TAB \
+ X(adc, 4140, eb400000), \
+ X(adcs, 4140, eb500000), \
+ X(add, 1c00, eb000000), \
+ X(adds, 1c00, eb100000), \
+ X(and, 4000, ea000000), \
+ X(ands, 4000, ea100000), \
+ X(asr, 1000, fa40f000), \
+ X(asrs, 1000, fa50f000), \
+ X(bic, 4380, ea200000), \
+ X(bics, 4380, ea300000), \
+ X(cmn, 42c0, eb100f00), \
+ X(cmp, 2800, ebb00f00), \
+ X(cpsie, b660, f3af8400), \
+ X(cpsid, b670, f3af8600), \
+ X(cpy, 4600, ea4f0000), \
+ X(eor, 4040, ea800000), \
+ X(eors, 4040, ea900000), \
+ X(ldmia, c800, e8900000), \
+ X(ldr, 6800, f8500000), \
+ X(ldrb, 7800, f8100000), \
+ X(ldrh, 8800, f8300000), \
+ X(ldrsb, 5600, f9100000), \
+ X(ldrsh, 5e00, f9300000), \
+ X(lsl, 0000, fa00f000), \
+ X(lsls, 0000, fa10f000), \
+ X(lsr, 0800, fa20f000), \
+ X(lsrs, 0800, fa30f000), \
+ X(mov, 2000, ea4f0000), \
+ X(movs, 2000, ea5f0000), \
+ X(mul, 4340, fb00f000), \
+ X(muls, 4340, ffffffff), /* no 32b muls */ \
+ X(mvn, 43c0, ea6f0000), \
+ X(mvns, 43c0, ea7f0000), \
+ X(neg, 4240, f1c00000), /* rsb #0 */ \
+ X(negs, 4240, f1d00000), /* rsbs #0 */ \
+ X(orr, 4300, ea400000), \
+ X(orrs, 4300, ea500000), \
+ X(pop, bc00, e8ad0000), /* ldmia sp!,... */ \
+ X(push, b400, e8bd0000), /* stmia sp!,... */ \
+ X(rev, ba00, fa90f080), \
+ X(rev16, ba40, fa90f090), \
+ X(revsh, bac0, fa90f0b0), \
+ X(ror, 41c0, fa60f000), \
+ X(rors, 41c0, fa70f000), \
+ X(sbc, 4180, eb600000), \
+ X(sbcs, 4180, eb700000), \
+ X(stmia, c000, e8800000), \
+ X(str, 6000, f8400000), \
+ X(strb, 7000, f8000000), \
+ X(strh, 8000, f8200000), \
+ X(sub, 1e00, eba00000), \
+ X(subs, 1e00, ebb00000), \
+ X(sxtb, b240, fa4ff080), \
+ X(sxth, b200, fa0ff080), \
+ X(tst, 4200, ea100f00), \
+ X(uxtb, b2c0, fa5ff080), \
+ X(uxth, b280, fa1ff080), \
+ X(nop, bf00, f3af8000), \
+ X(yield, bf10, f3af8001), \
+ X(wfe, bf20, f3af8002), \
+ X(wfi, bf30, f3af8003), \
+ X(sev, bf40, f3af9004), /* typo, 8004? */
+
+/* To catch errors in encoding functions, the codes are all offset by
+ 0xF800, putting them in one of the 32-bit prefix ranges, ergo undefined
+ as 16-bit instructions. */
+#define X(a,b,c) T_MNEM_##a
+enum t16_32_codes { T16_32_OFFSET = 0xF7FF, T16_32_TAB };
+#undef X
+
+#define X(a,b,c) 0x##b
+static const unsigned short thumb_op16[] = { T16_32_TAB };
+#define THUMB_OP16(n) (thumb_op16[(n) - (T16_32_OFFSET + 1)])
+#undef X
+
+#define X(a,b,c) 0x##c
+static const unsigned int thumb_op32[] = { T16_32_TAB };
+#define THUMB_OP32(n) (thumb_op32[(n) - (T16_32_OFFSET + 1)])
+#define THUMB_SETS_FLAGS(n) (THUMB_OP32 (n) & 0x00100000)
+#undef X
+#undef T16_32_TAB
+
+/* Thumb instruction encoders, in alphabetical order. */
+
+/* Parse an add or subtract instruction. We get here with inst.instruction
+ equalling any of THUMB_OPCODE_add, adds, sub, or subs. */
+
+static void
+do_t_add_sub (void)
+{
+ int Rd, Rs, Rn;
- skip_whitespace (*str);
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
- if (**str == '-') /* We have the start of a range expression */
+ if (unified_syntax)
+ {
+ if (!inst.operands[2].isreg)
{
- int high_range;
-
- (*str)++;
-
- if ((high_range
- = arm_reg_parse (str, all_reg_maps[regtype].htab))
- == FAIL)
- {
- inst.error = _(all_reg_maps[regtype].expected);
- return FAIL;
- }
-
- if (high_range <= new_base)
- {
- inst.error = _("register range not in ascending order");
- return FAIL;
- }
-
- for (new_base++; new_base <= high_range; new_base++)
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ Rn = inst.operands[2].reg;
+ /* See if we can do this with a 16-bit instruction. */
+ if (!inst.operands[2].shifted && inst.size_req != 4)
{
- if (mask & (1 << new_base))
+ if (Rd <= 7 && Rn <= 7 && Rn <= 7
+ && (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs))
{
- inst.error = _("invalid register list");
- return FAIL;
+ inst.instruction = (inst.instruction == T_MNEM_adds
+ ? T_OPCODE_ADD_R3
+ : T_OPCODE_SUB_R3);
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ return;
}
- mask |= 1 << new_base;
- count++;
+ if (inst.instruction == T_MNEM_add)
+ {
+ if (Rd == Rs)
+ {
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ inst.instruction |= Rn << 3;
+ return;
+ }
+ /* ... because addition is commutative! */
+ else if (Rd == Rn)
+ {
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ inst.instruction |= Rs << 3;
+ return;
+ }
+ }
}
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
}
}
- while (skip_past_comma (str) != FAIL);
+ else
+ {
+ constraint (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs,
+ BAD_THUMB32);
- (*str)++;
+ if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
+ {
+ constraint ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
+ || (Rs > 7 && Rs != REG_SP && Rs != REG_PC),
+ BAD_HIREG);
- /* Sanity check -- should have raised a parse error above. */
- if (count == 0 || count > max_regs)
- abort ();
+ inst.instruction = (inst.instruction == T_MNEM_add
+ ? 0x0000 : 0x8000);
+ inst.instruction |= (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ return;
+ }
- *pbase = base_reg;
+ Rn = inst.operands[2].reg;
+ constraint (inst.operands[2].shifted, _("unshifted register required"));
- /* Final test -- the registers must be consecutive. */
- mask >>= base_reg;
- for (i = 0; i < count; i++)
- {
- if ((mask & (1u << i)) == 0)
+ /* We now have Rd, Rs, and Rn set to registers. */
+ if (Rd > 7 || Rs > 7 || Rn > 7)
{
- inst.error = _("non-contiguous register range");
- return FAIL;
+ /* Can't do this for SUB. */
+ constraint (inst.instruction == T_MNEM_sub, BAD_HIREG);
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ if (Rs == Rd)
+ inst.instruction |= Rn << 3;
+ else if (Rn == Rd)
+ inst.instruction |= Rs << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
+ }
+ else
+ {
+ inst.instruction = (inst.instruction == T_MNEM_add
+ ? T_OPCODE_ADD_R3 : T_OPCODE_SUB_R3);
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
}
}
-
- return count;
}
static void
-do_vfp_reg2_from_sp2 (char * str)
+do_t_adr (void)
{
- int reg;
-
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL
- || skip_past_comma (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- /* We require exactly two consecutive SP registers. */
- if (vfp_parse_reg_list (&str, &reg, 0) != 2)
- {
- if (! inst.error)
- inst.error = _("only two consecutive VFP SP registers allowed here");
- }
- vfp_sp_encode_reg (reg, VFP_REG_Sm);
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
+ inst.reloc.pc_rel = 1;
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 4;
}
+/* Arithmetic instructions for which there is just one 16-bit
+ instruction encoding, and it allows only two low registers.
+ For maximal compatibility with ARM syntax, we allow three register
+ operands even when Thumb-32 instructions are not available, as long
+ as the first two are identical. For instance, both "sbc r0,r1" and
+ "sbc r0,r0,r1" are allowed. */
static void
-do_vfp_sp_from_reg (char * str)
+do_t_arit3 (void)
{
- skip_whitespace (str);
+ int Rd, Rs, Rn;
- if (vfp_sp_reg_required_here (&str, VFP_REG_Sn) == FAIL)
- return;
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+ Rn = inst.operands[2].reg;
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL)
+ if (unified_syntax)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
-}
-
-static void
-do_vfp_sp2_from_reg2 (char * str)
-{
- int reg;
-
- skip_whitespace (str);
+ if (!inst.operands[2].isreg)
+ {
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ /* See if we can do this with a 16-bit instruction. */
+ if (THUMB_SETS_FLAGS (inst.instruction)
+ && !inst.operands[2].shifted
+ && inst.size_req != 4
+ && Rd == Rs)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ return;
+ }
- /* We require exactly two consecutive SP registers. */
- if (vfp_parse_reg_list (&str, &reg, 0) != 2)
- {
- if (! inst.error)
- inst.error = _("only two consecutive VFP SP registers allowed here");
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
+ }
}
- vfp_sp_encode_reg (reg, VFP_REG_Sm);
-
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL)
+ else
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* On its face this is a lie - the instruction does set the
+ flags. However, the only supported mnemonic in this mode
+ says it doesn't. */
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
- end_of_line (str);
-}
-
-static void
-do_vfp_reg_from_dp (char * str)
-{
- skip_whitespace (str);
-
- if (reg_required_here (&str, 12) == FAIL)
- return;
+ constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+ _("unshifted register required"));
+ constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
+ constraint (Rd != Rs,
+ _("dest and source1 must be the same register"));
- if (skip_past_comma (&str) == FAIL
- || vfp_dp_reg_required_here (&str, VFP_REG_Dn) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
}
-
- end_of_line (str);
}
+/* Similarly, but for instructions where the arithmetic operation is
+ commutative, so we can allow either of them to be different from
+ the destination operand in a 16-bit instruction. For instance, all
+ three of "adc r0,r1", "adc r0,r0,r1", and "adc r0,r1,r0" are
+ accepted. */
static void
-do_vfp_reg2_from_dp (char * str)
+do_t_arit3c (void)
{
- skip_whitespace (str);
+ int Rd, Rs, Rn;
- if (reg_required_here (&str, 12) == FAIL)
- return;
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+ Rn = inst.operands[2].reg;
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL
- || skip_past_comma (&str) == FAIL
- || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
+ if (unified_syntax)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
-}
-
-static void
-do_vfp_dp_from_reg (char * str)
-{
- skip_whitespace (str);
-
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dn) == FAIL)
- return;
+ if (!inst.operands[2].isreg)
+ {
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ /* See if we can do this with a 16-bit instruction. */
+ if (THUMB_SETS_FLAGS (inst.instruction)
+ && !inst.operands[2].shifted
+ && inst.size_req != 4)
+ {
+ if (Rd == Rs)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ return;
+ }
+ if (Rd == Rn)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rs << 3;
+ return;
+ }
+ }
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
+ }
}
-
- end_of_line (str);
-}
-
-static void
-do_vfp_dp_from_reg2 (char * str)
-{
- skip_whitespace (str);
-
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL
- || skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 16) == FAIL)
+ else
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
-}
+ /* On its face this is a lie - the instruction does set the
+ flags. However, the only supported mnemonic in this mode
+ says it doesn't. */
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
-static const struct vfp_reg *
-vfp_psr_parse (char ** str)
-{
- char *start = *str;
- char c;
- char *p;
- const struct vfp_reg *vreg;
-
- p = start;
-
- /* Find the end of the current token. */
- do
- {
- c = *p++;
- }
- while (ISALPHA (c));
+ constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+ _("unshifted register required"));
+ constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
- /* Mark it. */
- *--p = 0;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
- for (vreg = vfp_regs + 0;
- vreg < vfp_regs + sizeof (vfp_regs) / sizeof (struct vfp_reg);
- vreg++)
- {
- if (streq (start, vreg->name))
- {
- *p = c;
- *str = p;
- return vreg;
- }
+ if (Rd == Rs)
+ inst.instruction |= Rn << 3;
+ else if (Rd == Rn)
+ inst.instruction |= Rs << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
}
-
- *p = c;
- return NULL;
}
-static int
-vfp_psr_required_here (char ** str)
+static void
+do_t_bfc (void)
{
- char *start = *str;
- const struct vfp_reg *vreg;
-
- vreg = vfp_psr_parse (str);
-
- if (vreg)
- {
- inst.instruction |= vreg->regno;
- return SUCCESS;
- }
-
- inst.error = _("VFP system register expected");
-
- *str = start;
- return FAIL;
+ unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= (inst.operands[1].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[1].imm & 0x03) << 6;
+ inst.instruction |= msb - 1;
}
static void
-do_vfp_reg_from_ctrl (char * str)
+do_t_bfi (void)
{
- skip_whitespace (str);
+ unsigned int msb;
- if (reg_required_here (&str, 12) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL
- || vfp_psr_required_here (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* #0 in second position is alternative syntax for bfc, which is
+ the same instruction but with REG_PC in the Rm field. */
+ if (!inst.operands[1].isreg)
+ inst.operands[1].reg = REG_PC;
- end_of_line (str);
+ msb = inst.operands[2].imm + inst.operands[3].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+ inst.instruction |= msb - 1;
}
static void
-do_vfp_ctrl_from_reg (char * str)
+do_t_bfx (void)
{
- skip_whitespace (str);
-
- if (vfp_psr_required_here (&str) == FAIL)
- return;
+ constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+ _("bit-field extends past end of register"));
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+ inst.instruction |= inst.operands[3].imm - 1;
+}
- if (skip_past_comma (&str) == FAIL
- || reg_required_here (&str, 12) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+/* ARM V5 Thumb BLX (argument parse)
+ BLX <target_addr> which is BLX(1)
+ BLX <Rm> which is BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
- end_of_line (str);
-}
+ ??? How to take advantage of the additional two bits of displacement
+ available in Thumb32 mode? Need new relocation? */
static void
-do_vfp_sp_ldst (char * str)
+do_t_blx (void)
{
- skip_whitespace (str);
-
- if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || cp_address_required_here (&str, CP_NO_WB) == FAIL)
+ if (inst.operands[0].isreg)
+ /* We have a register, so this is BLX(2). */
+ inst.instruction |= inst.operands[0].reg << 3;
+ else
{
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
+ /* No register. This must be BLX(1). */
+ inst.instruction = 0xf7ffeffe;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BLX;
+ inst.reloc.pc_rel = 1;
}
-
- end_of_line (str);
}
static void
-do_vfp_dp_ldst (char * str)
+do_t_branch (void)
{
- skip_whitespace (str);
-
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
+ if (unified_syntax && inst.size_req != 2)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
+ if (inst.cond == COND_ALWAYS)
+ {
+ inst.instruction = 0xf7ffbffe;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
+ }
+ else
+ {
+ assert (inst.cond != 0xF);
+ inst.instruction = (inst.cond << 22) | 0xf43faffe;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
+ }
}
-
- if (skip_past_comma (&str) == FAIL
- || cp_address_required_here (&str, CP_NO_WB) == FAIL)
+ else
{
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
+ if (inst.cond == COND_ALWAYS)
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ else
+ {
+ inst.instruction = 0xd0fe | (inst.cond << 8);
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ }
}
- end_of_line (str);
+ inst.reloc.pc_rel = 1;
}
-
static void
-vfp_sp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
+do_t_bkpt (void)
{
- int count;
- int reg;
-
- skip_whitespace (str);
-
- if (reg_required_here (&str, 16) == FAIL)
- return;
-
- skip_whitespace (str);
-
- if (*str == '!')
- {
- inst.instruction |= WRITE_BACK;
- str++;
- }
- else if (ldstm_type != VFP_LDSTMIA)
+ if (inst.operands[0].present)
{
- inst.error = _("this addressing mode requires base-register writeback");
- return;
+ constraint (inst.operands[0].imm > 255,
+ _("immediate value out of range"));
+ inst.instruction |= inst.operands[0].imm;
}
-
- if (skip_past_comma (&str) == FAIL
- || (count = vfp_parse_reg_list (&str, &reg, 0)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
- vfp_sp_encode_reg (reg, VFP_REG_Sd);
-
- inst.instruction |= count;
- end_of_line (str);
}
static void
-vfp_dp_ldstm (char * str, enum vfp_ldstm_type ldstm_type)
+do_t_branch23 (void)
{
- int count;
- int reg;
-
- skip_whitespace (str);
-
- if (reg_required_here (&str, 16) == FAIL)
- return;
-
- skip_whitespace (str);
-
- if (*str == '!')
- {
- inst.instruction |= WRITE_BACK;
- str++;
- }
- else if (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX)
- {
- inst.error = _("this addressing mode requires base-register writeback");
- return;
- }
-
- if (skip_past_comma (&str) == FAIL
- || (count = vfp_parse_reg_list (&str, &reg, 1)) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- count <<= 1;
- if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
- count += 1;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+ inst.reloc.pc_rel = 1;
- inst.instruction |= (reg << 12) | count;
- end_of_line (str);
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if ( inst.reloc.exp.X_op == O_symbol
+ && inst.reloc.exp.X_add_symbol != NULL
+ && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
+ && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
+ inst.reloc.exp.X_add_symbol =
+ find_real_start (inst.reloc.exp.X_add_symbol);
}
static void
-do_vfp_sp_ldstmia (char * str)
+do_t_bx (void)
{
- vfp_sp_ldstm (str, VFP_LDSTMIA);
+ inst.instruction |= inst.operands[0].reg << 3;
+ /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
+ should cause the alignment to be checked once it is known. This is
+ because BX PC only works if the instruction is word aligned. */
}
static void
-do_vfp_sp_ldstmdb (char * str)
+do_t_bxj (void)
{
- vfp_sp_ldstm (str, VFP_LDSTMDB);
-}
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bxj is not really useful"));
-static void
-do_vfp_dp_ldstmia (char * str)
-{
- vfp_dp_ldstm (str, VFP_LDSTMIA);
+ inst.instruction |= inst.operands[0].reg << 16;
}
static void
-do_vfp_dp_ldstmdb (char * str)
+do_t_clz (void)
{
- vfp_dp_ldstm (str, VFP_LDSTMDB);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
}
static void
-do_vfp_xp_ldstmia (char *str)
+do_t_cpsi (void)
{
- vfp_dp_ldstm (str, VFP_LDSTMIAX);
+ if (unified_syntax
+ && (inst.operands[1].present || inst.size_req == 4))
+ {
+ unsigned int imod = (inst.instruction & 0x0030) >> 4;
+ inst.instruction = 0xf3af8000;
+ inst.instruction |= imod << 9;
+ inst.instruction |= inst.operands[0].imm << 5;
+ if (inst.operands[1].present)
+ inst.instruction |= 0x100 | inst.operands[1].imm;
+ }
+ else
+ {
+ constraint (inst.operands[1].present,
+ _("Thumb does not support the 2-argument "
+ "form of this instruction"));
+ inst.instruction |= inst.operands[0].imm;
+ }
}
-static void
-do_vfp_xp_ldstmdb (char * str)
-{
- vfp_dp_ldstm (str, VFP_LDSTMDBX);
-}
+/* THUMB CPY instruction (argument parse). */
static void
-do_vfp_sp_compare_z (char * str)
+do_t_cpy (void)
{
- skip_whitespace (str);
-
- if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
+ if (inst.size_req == 4)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
+ inst.instruction = THUMB_OP32 (T_MNEM_mov);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg;
+ }
+ else
+ {
+ inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+ inst.instruction |= (inst.operands[0].reg & 0x7);
+ inst.instruction |= inst.operands[1].reg << 3;
}
-
- end_of_line (str);
}
static void
-do_vfp_dp_compare_z (char * str)
+do_t_czb (void)
{
- skip_whitespace (str);
-
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ constraint (inst.operands[0].reg > 7, BAD_HIREG);
+ inst.instruction |= inst.operands[0].reg;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH7;
}
static void
-do_vfp_dp_sp_cvt (char * str)
+do_t_hint (void)
{
- skip_whitespace (str);
-
- if (vfp_dp_reg_required_here (&str, VFP_REG_Dd) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL
- || vfp_sp_reg_required_here (&str, VFP_REG_Sm) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- end_of_line (str);
+ if (unified_syntax && inst.size_req == 4)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ else
+ inst.instruction = THUMB_OP16 (inst.instruction);
}
static void
-do_vfp_sp_dp_cvt (char * str)
+do_t_it (void)
{
- skip_whitespace (str);
+ unsigned int cond = inst.operands[0].imm;
+ if ((cond & 0x1) == 0x0)
+ {
+ unsigned int mask = inst.instruction & 0x000f;
+ inst.instruction &= 0xfff0;
- if (vfp_sp_reg_required_here (&str, VFP_REG_Sd) == FAIL)
- return;
+ if ((mask & 0x7) == 0)
+ /* no conversion needed */;
+ else if ((mask & 0x3) == 0)
+ mask = (~(mask & 0x8) & 0x8) | 0x4;
+ else if ((mask & 1) == 0)
+ mask = (~(mask & 0xC) & 0xC) | 0x2;
+ else
+ mask = (~(mask & 0xE) & 0xE) | 0x1;
- if (skip_past_comma (&str) == FAIL
- || vfp_dp_reg_required_here (&str, VFP_REG_Dm) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
+ inst.instruction |= (mask & 0xF);
}
- end_of_line (str);
+ inst.instruction |= cond << 4;
}
-/* Thumb specific routines. */
-
-/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
- was SUB. */
-
static void
-thumb_add_sub (char * str, int subtract)
+do_t_ldmstm (void)
{
- int Rd, Rs, Rn = FAIL;
-
- skip_whitespace (str);
-
- if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
- || skip_past_comma (&str) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* This really doesn't seem worth it. */
+ constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+ _("expression too complex"));
+ constraint (inst.operands[1].writeback,
+ _("Thumb load/store multiple does not support {reglist}^"));
- if (is_immediate_prefix (*str))
+ if (unified_syntax)
{
- Rs = Rd;
- str++;
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
- }
- else
- {
- if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL)
- {
- /* Two operand format, shuffle the registers
- and pretend there are 3. */
- Rn = Rs;
- Rs = Rd;
- }
- else if (is_immediate_prefix (*str))
+ /* See if we can use a 16-bit instruction. */
+ if (inst.instruction < 0xffff /* not ldmdb/stmdb */
+ && inst.size_req != 4
+ && inst.operands[0].reg <= 7
+ && !(inst.operands[1].imm & ~0xff)
+ && (inst.instruction == T_MNEM_stmia
+ ? inst.operands[0].writeback
+ : (inst.operands[0].writeback
+ == !(inst.operands[1].imm & (1 << inst.operands[0].reg)))))
{
- str++;
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
- }
- else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
- }
+ if (inst.instruction == T_MNEM_stmia
+ && (inst.operands[1].imm & (1 << inst.operands[0].reg))
+ && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
+ as_warn (_("value stored for r%d is UNPREDICTABLE"),
+ inst.operands[0].reg);
- /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
- for the latter case, EXPR contains the immediate that was found. */
- if (Rn != FAIL)
- {
- /* All register format. */
- if (Rd > 7 || Rs > 7 || Rn > 7)
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ else
{
- if (Rs != Rd)
+ if (inst.operands[1].imm & (1 << 13))
+ as_warn (_("SP should not be in register list"));
+ if (inst.instruction == T_MNEM_stmia)
{
- inst.error = _("dest and source1 must be the same register");
- return;
+ if (inst.operands[1].imm & (1 << 15))
+ as_warn (_("PC should not be in register list"));
+ if (inst.operands[1].imm & (1 << inst.operands[0].reg))
+ as_warn (_("value stored for r%d is UNPREDICTABLE"),
+ inst.operands[0].reg);
}
-
- /* Can't do this for SUB. */
- if (subtract)
+ else
{
- inst.error = _("subtract valid only on lo regs");
- return;
+ if (inst.operands[1].imm & (1 << 14)
+ && inst.operands[1].imm & (1 << 15))
+ as_warn (_("LR and PC should not both be in register list"));
+ if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
+ && inst.operands[0].writeback)
+ as_warn (_("base register should not be in register list "
+ "when written back"));
}
-
- inst.instruction = (T_OPCODE_ADD_HI
- | (Rd > 7 ? THUMB_H1 : 0)
- | (Rn > 7 ? THUMB_H2 : 0));
- inst.instruction |= (Rd & 7) | ((Rn & 7) << 3);
- }
- else
- {
- inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
- inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].imm;
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
}
}
else
{
- /* Immediate expression, now things start to get nasty. */
-
- /* First deal with HI regs, only very restricted cases allowed:
- Adjusting SP, and using PC or SP to get an address. */
- if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
- || (Rs > 7 && Rs != REG_SP && Rs != REG_PC))
+ constraint (inst.operands[0].reg > 7
+ || (inst.operands[1].imm & ~0xff), BAD_HIREG);
+ if (inst.instruction == T_MNEM_stmia)
{
- inst.error = _("invalid Hi register with immediate");
- return;
- }
-
- if (inst.reloc.exp.X_op != O_constant)
- {
- /* Value isn't known yet, all we can do is store all the fragments
- we know about in the instruction and let the reloc hacking
- work it all out. */
- inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ if (!inst.operands[0].writeback)
+ as_warn (_("this instruction will write back the base register"));
+ if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
+ && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
+ as_warn (_("value stored for r%d is UNPREDICTABLE"),
+ inst.operands[0].reg);
}
else
{
- int offset = inst.reloc.exp.X_add_number;
-
- if (subtract)
- offset = - offset;
-
- if (offset < 0)
- {
- offset = - offset;
- subtract = 1;
-
- /* Quick check, in case offset is MIN_INT. */
- if (offset < 0)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- }
- /* Note - you cannot convert a subtract of 0 into an
- add of 0 because the carry flag is set differently. */
- else if (offset > 0)
- subtract = 0;
-
- if (Rd == REG_SP)
- {
- if (offset & ~0x1fc)
- {
- inst.error = _("invalid immediate value for stack adjust");
- return;
- }
- inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
- inst.instruction |= offset >> 2;
- }
- else if (Rs == REG_PC || Rs == REG_SP)
- {
- if (subtract
- || (offset & ~0x3fc))
- {
- inst.error = _("invalid immediate for address calculation");
- return;
- }
- inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC
- : T_OPCODE_ADD_SP);
- inst.instruction |= (Rd << 8) | (offset >> 2);
- }
- else if (Rs == Rd)
- {
- if (offset & ~0xff)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
- inst.instruction |= (Rd << 8) | offset;
- }
- else
- {
- if (offset & ~0x7)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
- inst.instruction |= Rd | (Rs << 3) | (offset << 6);
- }
+ if (!inst.operands[0].writeback
+ && !(inst.operands[1].imm & (1 << inst.operands[0].reg)))
+ as_warn (_("this instruction will write back the base register"));
+ else if (inst.operands[0].writeback
+ && (inst.operands[1].imm & (1 << inst.operands[0].reg)))
+ as_warn (_("this instruction will not write back the base register"));
}
- }
- end_of_line (str);
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ }
}
static void
-thumb_shift (char * str, int shift)
+do_t_ldrex (void)
{
- int Rd, Rs, Rn = FAIL;
+ constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+ || inst.operands[1].postind || inst.operands[1].writeback
+ || inst.operands[1].immisreg || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ _("instruction does not accept this addressing mode"));
- skip_whitespace (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
+}
- if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL)
+static void
+do_t_ldrexd (void)
+{
+ if (!inst.operands[1].present)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
+ constraint (inst.operands[0].reg == REG_LR,
+ _("r14 not allowed as first register "
+ "when second register is omitted"));
+ inst.operands[1].reg = inst.operands[0].reg + 1;
}
+ constraint (inst.operands[0].reg == inst.operands[1].reg,
+ BAD_OVERLAP);
- if (is_immediate_prefix (*str))
- {
- /* Two operand immediate format, set Rs to Rd. */
- Rs = Rd;
- str ++;
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
- }
- else
- {
- if ((Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
- if (skip_past_comma (&str) == FAIL)
- {
- /* Two operand format, shuffle the registers
- and pretend there are 3. */
- Rn = Rs;
- Rs = Rd;
- }
- else if (is_immediate_prefix (*str))
+static void
+do_t_ldst (void)
+{
+ if (unified_syntax)
+ {
+ /* Generation of 16-bit instructions for anything other than
+ Rd, [Rn, Ri] is deferred to section relaxation time. */
+ if (inst.operands[1].isreg && inst.operands[1].immisreg
+ && !inst.operands[1].shifted && !inst.operands[1].postind
+ && !inst.operands[1].negative && inst.operands[0].reg <= 7
+ && inst.operands[1].reg <= 7 && inst.operands[1].imm <= 7
+ && inst.instruction <= 0xffff)
{
- str++;
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ goto op16;
}
- else if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
+
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
+ return;
}
- /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
- for the latter case, EXPR contains the immediate that was found. */
+ constraint (inst.operands[0].reg > 7, BAD_HIREG);
- if (Rn != FAIL)
+ if (inst.instruction == T_MNEM_ldrsh || inst.instruction == T_MNEM_ldrsb)
{
- if (Rs != Rd)
- {
- inst.error = _("source1 and dest must be same register");
- return;
- }
-
- switch (shift)
- {
- case THUMB_ASR: inst.instruction = T_OPCODE_ASR_R; break;
- case THUMB_LSL: inst.instruction = T_OPCODE_LSL_R; break;
- case THUMB_LSR: inst.instruction = T_OPCODE_LSR_R; break;
- }
-
- inst.instruction |= Rd | (Rn << 3);
+ /* Only [Rn,Rm] is acceptable. */
+ constraint (inst.operands[1].reg > 7 || inst.operands[1].imm > 7, BAD_HIREG);
+ constraint (!inst.operands[1].isreg || !inst.operands[1].immisreg
+ || inst.operands[1].postind || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ _("Thumb does not support this addressing mode"));
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ goto op16;
}
- else
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, /*thumb_p=*/TRUE, /*mode_3=*/FALSE))
+ return;
+
+ constraint (!inst.operands[1].preind
+ || inst.operands[1].shifted
+ || inst.operands[1].writeback,
+ _("Thumb does not support this addressing mode"));
+ if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
{
- switch (shift)
- {
- case THUMB_ASR: inst.instruction = T_OPCODE_ASR_I; break;
- case THUMB_LSL: inst.instruction = T_OPCODE_LSL_I; break;
- case THUMB_LSR: inst.instruction = T_OPCODE_LSR_I; break;
- }
+ constraint (inst.instruction & 0x0600,
+ _("byte or halfword not valid for base register"));
+ constraint (inst.operands[1].reg == REG_PC
+ && !(inst.instruction & THUMB_LOAD_BIT),
+ _("r15 based store not allowed"));
+ constraint (inst.operands[1].immisreg,
+ _("invalid base register for register offset"));
- if (inst.reloc.exp.X_op != O_constant)
- {
- /* Value isn't known yet, create a dummy reloc and let reloc
- hacking fix it up. */
- inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
- }
+ if (inst.operands[1].reg == REG_PC)
+ inst.instruction = T_OPCODE_LDR_PC;
+ else if (inst.instruction & THUMB_LOAD_BIT)
+ inst.instruction = T_OPCODE_LDR_SP;
else
- {
- unsigned shift_value = inst.reloc.exp.X_add_number;
-
- if (shift_value > 32 || (shift_value == 32 && shift == THUMB_LSL))
- {
- inst.error = _("invalid immediate for shift");
- return;
- }
+ inst.instruction = T_OPCODE_STR_SP;
- /* Shifts of zero are handled by converting to LSL. */
- if (shift_value == 0)
- inst.instruction = T_OPCODE_LSL_I;
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ return;
+ }
- /* Shifts of 32 are encoded as a shift of zero. */
- if (shift_value == 32)
- shift_value = 0;
+ constraint (inst.operands[1].reg > 7, BAD_HIREG);
+ if (!inst.operands[1].immisreg)
+ {
+ /* Immediate offset. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ return;
+ }
- inst.instruction |= shift_value << 6;
- }
+ /* Register offset. */
+ constraint (inst.operands[1].imm > 7, BAD_HIREG);
+ constraint (inst.operands[1].negative,
+ _("Thumb does not support this addressing mode"));
- inst.instruction |= Rd | (Rs << 3);
+ op16:
+ switch (inst.instruction)
+ {
+ case T_OPCODE_STR_IW: inst.instruction = T_OPCODE_STR_RW; break;
+ case T_OPCODE_STR_IH: inst.instruction = T_OPCODE_STR_RH; break;
+ case T_OPCODE_STR_IB: inst.instruction = T_OPCODE_STR_RB; break;
+ case T_OPCODE_LDR_IW: inst.instruction = T_OPCODE_LDR_RW; break;
+ case T_OPCODE_LDR_IH: inst.instruction = T_OPCODE_LDR_RH; break;
+ case T_OPCODE_LDR_IB: inst.instruction = T_OPCODE_LDR_RB; break;
+ case 0x5600 /* ldrsb */:
+ case 0x5e00 /* ldrsh */: break;
+ default: abort ();
}
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.instruction |= inst.operands[1].imm << 6;
}
static void
-thumb_load_store (char * str, int load_store, int size)
+do_t_ldstd (void)
{
- int Rd, Rb, Ro = FAIL;
-
- skip_whitespace (str);
-
- if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL)
+ if (!inst.operands[1].present)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
+ inst.operands[1].reg = inst.operands[0].reg + 1;
+ constraint (inst.operands[0].reg == REG_LR,
+ _("r14 not allowed here"));
}
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ encode_thumb32_addr_mode (2, /*is_t=*/FALSE, /*is_d=*/TRUE);
+
+}
- if (*str == '[')
- {
- str++;
- if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
+static void
+do_t_ldstt (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_thumb32_addr_mode (1, /*is_t=*/TRUE, /*is_d=*/FALSE);
+}
- if (skip_past_comma (&str) != FAIL)
- {
- if (is_immediate_prefix (*str))
- {
- str++;
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
- }
- else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
- }
- else
- {
- inst.reloc.exp.X_op = O_constant;
- inst.reloc.exp.X_add_number = 0;
- }
+static void
+do_t_mla (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].reg << 12;
+}
- if (*str != ']')
+static void
+do_t_mlal (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.instruction |= inst.operands[3].reg;
+}
+
+static void
+do_t_mov_cmp (void)
+{
+ if (unified_syntax)
+ {
+ int r0off = (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs) ? 8 : 16;
+ if (!inst.operands[1].isreg)
{
- inst.error = _("expected ']'");
- return;
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= inst.operands[0].reg << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
- str++;
- }
- else if (*str == '=')
- {
- if (load_store != THUMB_LOAD)
+ else if (inst.size_req == 4
+ || inst.operands[1].shifted
+ || (inst.instruction == T_MNEM_movs
+ && (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)))
{
- inst.error = _("invalid pseudo operation");
- return;
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << r0off;
+ encode_thumb32_shifted_operand (1);
}
+ else
+ switch (inst.instruction)
+ {
+ case T_MNEM_mov:
+ inst.instruction = T_OPCODE_MOV_HR;
+ inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+ inst.instruction |= (inst.operands[0].reg & 0x7);
+ inst.instruction |= inst.operands[1].reg << 3;
+ break;
- /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op. */
- str++;
-
- skip_whitespace (str);
-
- if (my_get_expression (& inst.reloc.exp, & str))
- return;
-
- end_of_line (str);
+ case T_MNEM_movs:
+ /* We know we have low registers at this point.
+ Generate ADD Rd, Rs, #0. */
+ inst.instruction = T_OPCODE_ADD_I3;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ break;
- if ( inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = "Constant expression expected";
- return;
- }
+ case T_MNEM_cmp:
+ if (inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7)
+ {
+ inst.instruction = T_OPCODE_CMP_LR;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ else
+ {
+ inst.instruction = T_OPCODE_CMP_HR;
+ inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+ inst.instruction |= (inst.operands[0].reg & 0x7);
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ break;
+ }
+ return;
+ }
- if (inst.reloc.exp.X_op == O_constant
- && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ if (inst.operands[1].isreg)
+ {
+ if (inst.operands[0].reg < 8 && inst.operands[1].reg < 8)
{
- /* This can be done with a mov instruction. */
+ /* A move of two lowregs is encoded as ADD Rd, Rs, #0
+ since a MOV instruction produces unpredictable results. */
+ if (inst.instruction == T_OPCODE_MOV_I8)
+ inst.instruction = T_OPCODE_ADD_I3;
+ else
+ inst.instruction = T_OPCODE_CMP_LR;
- inst.instruction = T_OPCODE_MOV_I8 | (Rd << 8);
- inst.instruction |= inst.reloc.exp.X_add_number;
- return;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
}
-
- /* Insert into literal pool. */
- if (add_to_lit_pool () == FAIL)
+ else
{
- if (!inst.error)
- inst.error = "literal pool insertion failed";
- return;
+ if (inst.instruction == T_OPCODE_MOV_I8)
+ inst.instruction = T_OPCODE_MOV_HR;
+ else
+ inst.instruction = T_OPCODE_CMP_HR;
+ do_t_cpy ();
}
-
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- inst.reloc.pc_rel = 1;
- inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
- /* Adjust ARM pipeline offset to Thumb. */
- inst.reloc.exp.X_add_number += 4;
-
- return;
}
else
{
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
-
- inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
- inst.reloc.pc_rel = 1;
- inst.reloc.exp.X_add_number -= 4; /* Pipeline offset. */
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- end_of_line (str);
- return;
+ constraint (inst.operands[0].reg > 7,
+ _("only lo regs allowed with immediate"));
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
}
+}
+
+static void
+do_t_mov16 (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= (inst.operands[1].imm & 0xf000) << 4;
+ inst.instruction |= (inst.operands[1].imm & 0x0800) << 15;
+ inst.instruction |= (inst.operands[1].imm & 0x0700) << 4;
+ inst.instruction |= (inst.operands[1].imm & 0x00ff);
+}
- if (Rb == REG_PC || Rb == REG_SP)
+static void
+do_t_mvn_tst (void)
+{
+ if (unified_syntax)
{
- if (size != THUMB_WORD)
+ int r0off = (inst.instruction == T_MNEM_mvn
+ || inst.instruction == T_MNEM_mvns) ? 8 : 16;
+ if (!inst.operands[1].isreg)
{
- inst.error = _("byte or halfword not valid for base register");
- return;
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= inst.operands[0].reg << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
- else if (Rb == REG_PC && load_store != THUMB_LOAD)
- {
- inst.error = _("r15 based store not allowed");
- return;
- }
- else if (Ro != FAIL)
- {
- inst.error = _("invalid base register for register offset");
- return;
- }
-
- if (Rb == REG_PC)
- inst.instruction = T_OPCODE_LDR_PC;
- else if (load_store == THUMB_LOAD)
- inst.instruction = T_OPCODE_LDR_SP;
else
- inst.instruction = T_OPCODE_STR_SP;
-
- inst.instruction |= Rd << 8;
- if (inst.reloc.exp.X_op == O_constant)
{
- unsigned offset = inst.reloc.exp.X_add_number;
-
- if (offset & ~0x3fc)
+ /* See if we can do this with a 16-bit instruction. */
+ if (inst.instruction < 0xffff
+ && THUMB_SETS_FLAGS (inst.instruction)
+ && !inst.operands[1].shifted
+ && inst.operands[0].reg <= 7
+ && inst.operands[1].reg <= 7
+ && inst.size_req != 4)
{
- inst.error = _("invalid offset");
- return;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
}
-
- inst.instruction |= offset >> 2;
- }
- else
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- }
- else if (Rb > 7)
- {
- inst.error = _("invalid base register in load/store");
- return;
- }
- else if (Ro == FAIL)
- {
- /* Immediate offset. */
- if (size == THUMB_WORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
- else if (size == THUMB_HALFWORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH);
- else
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB);
-
- inst.instruction |= Rd | (Rb << 3);
-
- if (inst.reloc.exp.X_op == O_constant)
- {
- unsigned offset = inst.reloc.exp.X_add_number;
-
- if (offset & ~(0x1f << size))
+ else
{
- inst.error = _("invalid offset");
- return;
+ constraint (inst.operands[1].shifted
+ && inst.operands[1].immisreg,
+ _("shift must be constant"));
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << r0off;
+ encode_thumb32_shifted_operand (1);
}
- inst.instruction |= (offset >> size) << 6;
}
- else
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
}
else
{
- /* Register offset. */
- if (size == THUMB_WORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
- else if (size == THUMB_HALFWORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH);
- else
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB);
+ constraint (inst.instruction > 0xffff
+ || inst.instruction == T_MNEM_mvns, BAD_THUMB32);
+ constraint (!inst.operands[1].isreg || inst.operands[1].shifted,
+ _("unshifted register required"));
+ constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+ BAD_HIREG);
- inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
}
-
- end_of_line (str);
}
-/* A register must be given at this point.
-
- Shift is the place to put it in inst.instruction.
+static void
+do_t_mrs (void)
+{
+ /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
+ constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
+ != (PSR_c|PSR_f),
+ _("'CPSR' or 'SPSR' expected"));
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= (inst.operands[1].imm & SPSR_BIT) >> 2;
+}
- Restores input start point on err.
- Returns the reg#, or FAIL. */
+static void
+do_t_msr (void)
+{
+ constraint (!inst.operands[1].isreg,
+ _("Thumb encoding does not support an immediate here"));
+ inst.instruction |= (inst.operands[0].imm & SPSR_BIT) >> 2;
+ inst.instruction |= (inst.operands[0].imm & ~SPSR_BIT) >> 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+}
-static int
-mav_reg_required_here (char ** str, int shift, enum arm_reg_type regtype)
+static void
+do_t_mul (void)
{
- int reg;
- char *start = *str;
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[0].reg;
- if ((reg = arm_reg_parse (str, all_reg_maps[regtype].htab)) != FAIL)
+ /* There is no 32-bit MULS and no 16-bit MUL. */
+ if (unified_syntax && inst.instruction == T_MNEM_mul)
{
- if (shift >= 0)
- inst.instruction |= reg << shift;
-
- return reg;
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg << 0;
}
-
- /* Restore the start point. */
- *str = start;
-
- /* Try generic coprocessor name if applicable. */
- if (regtype == REG_TYPE_MVF ||
- regtype == REG_TYPE_MVD ||
- regtype == REG_TYPE_MVFX ||
- regtype == REG_TYPE_MVDX)
+ else
{
- if ((reg = arm_reg_parse (str, all_reg_maps[REG_TYPE_CN].htab)) != FAIL)
- {
- if (shift >= 0)
- inst.instruction |= reg << shift;
+ constraint (!unified_syntax
+ && inst.instruction == T_MNEM_muls, BAD_THUMB32);
+ constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+ BAD_HIREG);
- return reg;
- }
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
- /* Restore the start point. */
- *str = start;
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ inst.instruction |= inst.operands[2].reg << 3;
+ else if (inst.operands[0].reg == inst.operands[2].reg)
+ inst.instruction |= inst.operands[1].reg << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
}
-
- /* In the few cases where we might be able to accept something else
- this error can be overridden. */
- inst.error = _(all_reg_maps[regtype].expected);
-
- return FAIL;
}
-/* Cirrus Maverick Instructions. */
-
-/* Isnsn like "foo X,Y". */
-
static void
-do_mav_binops (char * str,
- int mode,
- enum arm_reg_type reg0,
- enum arm_reg_type reg1)
+do_t_mull (void)
{
- int shift0, shift1;
-
- shift0 = mode & 0xff;
- shift1 = (mode >> 8) & 0xff;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.instruction |= inst.operands[3].reg;
- skip_whitespace (str);
-
- if (mav_reg_required_here (&str, shift0, reg0) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, shift1, reg1) == FAIL)
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- }
- else
- end_of_line (str);
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rdhi and rdlo must be different"));
}
-/* Isnsn like "foo X,Y,Z". */
-
static void
-do_mav_triple (char * str,
- int mode,
- enum arm_reg_type reg0,
- enum arm_reg_type reg1,
- enum arm_reg_type reg2)
+do_t_nop (void)
{
- int shift0, shift1, shift2;
-
- shift0 = mode & 0xff;
- shift1 = (mode >> 8) & 0xff;
- shift2 = (mode >> 16) & 0xff;
-
- skip_whitespace (str);
-
- if (mav_reg_required_here (&str, shift0, reg0) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, shift1, reg1) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, shift2, reg2) == FAIL)
+ if (unified_syntax)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ if (inst.size_req == 4 || inst.operands[0].imm > 15)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm << 4;
+ }
}
else
- end_of_line (str);
-}
-
-/* Wrapper functions. */
-
-static void
-do_mav_binops_1a (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_RN, REG_TYPE_MVF);
+ {
+ constraint (inst.operands[0].present,
+ _("Thumb does not support NOP with hints"));
+ inst.instruction = 0x46c0;
+ }
}
static void
-do_mav_binops_1b (char * str)
+do_t_neg (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_RN, REG_TYPE_MVD);
-}
+ if (unified_syntax)
+ {
+ if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7
+ || !THUMB_SETS_FLAGS (inst.instruction)
+ || inst.size_req == 4)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+ BAD_HIREG);
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
-static void
-do_mav_binops_1c (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_RN, REG_TYPE_MVDX);
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
}
static void
-do_mav_binops_1d (char * str)
+do_t_pkhbt (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVF);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ if (inst.operands[3].present)
+ {
+ unsigned int val = inst.reloc.exp.X_add_number;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ inst.instruction |= (val & 0x1c) << 10;
+ inst.instruction |= (val & 0x03) << 6;
+ }
}
static void
-do_mav_binops_1e (char * str)
+do_t_pkhtb (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVD);
+ if (!inst.operands[3].present)
+ inst.instruction &= ~0x00000020;
+ do_t_pkhbt ();
}
static void
-do_mav_binops_1f (char * str)
+do_t_pld (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVF);
+ encode_thumb32_addr_mode (0, /*is_t=*/FALSE, /*is_d=*/FALSE);
}
static void
-do_mav_binops_1g (char * str)
+do_t_push_pop (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVD);
-}
+ constraint (inst.operands[0].writeback,
+ _("push/pop do not support {reglist}^"));
+ constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+ _("expression too complex"));
-static void
-do_mav_binops_1h (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVFX);
-}
+ if ((inst.operands[0].imm & ~0xff) == 0)
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ else if ((inst.instruction == T_MNEM_push
+ && (inst.operands[0].imm & ~0xff) == 1 << REG_LR)
+ || (inst.instruction == T_MNEM_pop
+ && (inst.operands[0].imm & ~0xff) == 1 << REG_PC))
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= THUMB_PP_PC_LR;
+ inst.operands[0].imm &= 0xff;
+ }
+ else if (unified_syntax)
+ {
+ if (inst.operands[1].imm & (1 << 13))
+ as_warn (_("SP should not be in register list"));
+ if (inst.instruction == T_MNEM_push)
+ {
+ if (inst.operands[1].imm & (1 << 15))
+ as_warn (_("PC should not be in register list"));
+ }
+ else
+ {
+ if (inst.operands[1].imm & (1 << 14)
+ && inst.operands[1].imm & (1 << 15))
+ as_warn (_("LR and PC should not both be in register list"));
+ }
-static void
-do_mav_binops_1i (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVFX);
-}
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ }
+ else
+ {
+ inst.error = _("invalid register list to push/pop instruction");
+ return;
+ }
-static void
-do_mav_binops_1j (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVF, REG_TYPE_MVDX);
+ inst.instruction |= inst.operands[0].imm;
}
static void
-do_mav_binops_1k (char * str)
+do_t_rbit (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVD, REG_TYPE_MVDX);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
}
static void
-do_mav_binops_1l (char * str)
+do_t_rev (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVFX, REG_TYPE_MVF);
+ if (inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7
+ && inst.size_req != 4)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ else if (unified_syntax)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ }
+ else
+ inst.error = BAD_HIREG;
}
static void
-do_mav_binops_1m (char * str)
+do_t_rsb (void)
{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVFX, REG_TYPE_MVD);
-}
+ int Rd, Rs;
-static void
-do_mav_binops_1n (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVFX, REG_TYPE_MVFX);
-}
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
-static void
-do_mav_binops_1o (char * str)
-{
- do_mav_binops (str, MAV_MODE1, REG_TYPE_MVDX, REG_TYPE_MVDX);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ if (!inst.operands[2].isreg)
+ {
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ encode_thumb32_shifted_operand (2);
}
static void
-do_mav_binops_2a (char * str)
+do_t_setend (void)
{
- do_mav_binops (str, MAV_MODE2, REG_TYPE_MVF, REG_TYPE_RN);
+ if (inst.operands[0].imm)
+ inst.instruction |= 0x8;
}
static void
-do_mav_binops_2b (char * str)
+do_t_shift (void)
{
- do_mav_binops (str, MAV_MODE2, REG_TYPE_MVD, REG_TYPE_RN);
-}
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg;
-static void
-do_mav_binops_2c (char * str)
-{
- do_mav_binops (str, MAV_MODE2, REG_TYPE_MVDX, REG_TYPE_RN);
-}
+ if (unified_syntax)
+ {
+ if (inst.operands[0].reg > 7
+ || inst.operands[1].reg > 7
+ || !THUMB_SETS_FLAGS (inst.instruction)
+ || (!inst.operands[2].isreg && inst.instruction == T_MNEM_rors)
+ || (inst.operands[2].isreg && inst.operands[1].reg != inst.operands[0].reg)
+ || inst.size_req == 4)
+ {
+ if (inst.operands[2].isreg)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ }
+ else
+ {
+ inst.operands[1].shifted = 1;
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr:
+ case T_MNEM_asrs: inst.operands[1].shift_kind = SHIFT_ASR; break;
+ case T_MNEM_lsl:
+ case T_MNEM_lsls: inst.operands[1].shift_kind = SHIFT_LSL; break;
+ case T_MNEM_lsr:
+ case T_MNEM_lsrs: inst.operands[1].shift_kind = SHIFT_LSR; break;
+ case T_MNEM_ror:
+ case T_MNEM_rors: inst.operands[1].shift_kind = SHIFT_ROR; break;
+ default: abort ();
+ }
+
+ inst.instruction = THUMB_OP32 (THUMB_SETS_FLAGS (inst.instruction)
+ ? T_MNEM_movs : T_MNEM_mov);
+ inst.instruction |= inst.operands[0].reg << 8;
+ encode_thumb32_shifted_operand (1);
+ /* Prevent the incorrect generation of an ARM_IMMEDIATE fixup. */
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
+ }
+ else
+ {
+ if (inst.operands[2].isreg)
+ {
+ switch (inst.instruction)
+ {
+ case T_MNEM_asrs: inst.instruction = T_OPCODE_ASR_R; break;
+ case T_MNEM_lsls: inst.instruction = T_OPCODE_LSL_R; break;
+ case T_MNEM_lsrs: inst.instruction = T_OPCODE_LSR_R; break;
+ case T_MNEM_rors: inst.instruction = T_OPCODE_ROR_R; break;
+ default: abort ();
+ }
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[2].reg << 3;
+ }
+ else
+ {
+ switch (inst.instruction)
+ {
+ case T_MNEM_asrs: inst.instruction = T_OPCODE_ASR_I; break;
+ case T_MNEM_lsls: inst.instruction = T_OPCODE_LSL_I; break;
+ case T_MNEM_lsrs: inst.instruction = T_OPCODE_LSR_I; break;
+ default: abort ();
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7
+ || inst.operands[1].reg > 7, BAD_HIREG);
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
-static void
-do_mav_binops_3a (char * str)
-{
- do_mav_binops (str, MAV_MODE3, REG_TYPE_MVAX, REG_TYPE_MVFX);
-}
+ if (inst.operands[2].isreg) /* Rd, {Rs,} Rn */
+ {
+ constraint (inst.operands[2].reg > 7, BAD_HIREG);
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("source1 and dest must be same register"));
-static void
-do_mav_binops_3b (char * str)
-{
- do_mav_binops (str, MAV_MODE3, REG_TYPE_MVFX, REG_TYPE_MVAX);
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_R; break;
+ case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_R; break;
+ case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_R; break;
+ case T_MNEM_ror: inst.instruction = T_OPCODE_ROR_R; break;
+ default: abort ();
+ }
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[2].reg << 3;
+ }
+ else
+ {
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_I; break;
+ case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_I; break;
+ case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_I; break;
+ case T_MNEM_ror: inst.error = _("ror #imm not supported"); return;
+ default: abort ();
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ }
}
static void
-do_mav_binops_3c (char * str)
+do_t_simd (void)
{
- do_mav_binops (str, MAV_MODE3, REG_TYPE_MVAX, REG_TYPE_MVDX);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
}
static void
-do_mav_binops_3d (char * str)
+do_t_smi (void)
{
- do_mav_binops (str, MAV_MODE3, REG_TYPE_MVDX, REG_TYPE_MVAX);
+ unsigned int value = inst.reloc.exp.X_add_number;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ inst.instruction |= (value & 0xf000) >> 12;
+ inst.instruction |= (value & 0x0ff0);
+ inst.instruction |= (value & 0x000f) << 16;
}
static void
-do_mav_triple_4a (char * str)
+do_t_ssat (void)
{
- do_mav_triple (str, MAV_MODE4, REG_TYPE_MVFX, REG_TYPE_MVFX, REG_TYPE_RN);
-}
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm - 1;
+ inst.instruction |= inst.operands[2].reg << 16;
-static void
-do_mav_triple_4b (char * str)
-{
- do_mav_triple (str, MAV_MODE4, REG_TYPE_MVDX, REG_TYPE_MVDX, REG_TYPE_RN);
-}
+ if (inst.operands[3].present)
+ {
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
-static void
-do_mav_triple_5a (char * str)
-{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVF, REG_TYPE_MVF);
+ if (inst.reloc.exp.X_add_number != 0)
+ {
+ if (inst.operands[3].shift_kind == SHIFT_ASR)
+ inst.instruction |= 0x00200000; /* sh bit */
+ inst.instruction |= (inst.reloc.exp.X_add_number & 0x1c) << 10;
+ inst.instruction |= (inst.reloc.exp.X_add_number & 0x03) << 6;
+ }
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
}
static void
-do_mav_triple_5b (char * str)
+do_t_ssat16 (void)
{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVD, REG_TYPE_MVD);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm - 1;
+ inst.instruction |= inst.operands[2].reg << 16;
}
static void
-do_mav_triple_5c (char * str)
+do_t_strex (void)
{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVFX, REG_TYPE_MVFX);
-}
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative,
+ _("instruction does not accept this addressing mode"));
-static void
-do_mav_triple_5d (char * str)
-{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_RN, REG_TYPE_MVDX, REG_TYPE_MVDX);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
}
static void
-do_mav_triple_5e (char * str)
+do_t_strexd (void)
{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_MVF, REG_TYPE_MVF, REG_TYPE_MVF);
-}
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[1].reg + 1;
-static void
-do_mav_triple_5f (char * str)
-{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_MVD, REG_TYPE_MVD, REG_TYPE_MVD);
-}
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[0].reg == inst.operands[3].reg
+ || inst.operands[1].reg == inst.operands[2].reg,
+ BAD_OVERLAP);
-static void
-do_mav_triple_5g (char * str)
-{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_MVFX, REG_TYPE_MVFX, REG_TYPE_MVFX);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 16;
}
static void
-do_mav_triple_5h (char * str)
+do_t_sxtah (void)
{
- do_mav_triple (str, MAV_MODE5, REG_TYPE_MVDX, REG_TYPE_MVDX, REG_TYPE_MVDX);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 4;
}
-/* Isnsn like "foo W,X,Y,Z".
- where W=MVAX[0:3] and X,Y,Z=MVFX[0:15]. */
-
static void
-do_mav_quad (char * str,
- int mode,
- enum arm_reg_type reg0,
- enum arm_reg_type reg1,
- enum arm_reg_type reg2,
- enum arm_reg_type reg3)
+do_t_sxth (void)
{
- int shift0, shift1, shift2, shift3;
-
- shift0= mode & 0xff;
- shift1 = (mode >> 8) & 0xff;
- shift2 = (mode >> 16) & 0xff;
- shift3 = (mode >> 24) & 0xff;
-
- skip_whitespace (str);
-
- if (mav_reg_required_here (&str, shift0, reg0) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, shift1, reg1) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, shift2, reg2) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, shift3, reg3) == FAIL)
+ if (inst.instruction <= 0xffff && inst.size_req != 4
+ && inst.operands[0].reg <= 7 && inst.operands[1].reg <= 7
+ && (!inst.operands[2].present || inst.operands[2].imm == 0))
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ else if (unified_syntax)
+ {
+ if (inst.instruction <= 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 4;
}
else
- end_of_line (str);
+ {
+ constraint (inst.operands[2].present && inst.operands[2].imm != 0,
+ _("Thumb encoding does not support rotation"));
+ constraint (1, BAD_HIREG);
+ }
}
static void
-do_mav_quad_6a (char * str)
+do_t_swi (void)
{
- do_mav_quad (str, MAV_MODE6, REG_TYPE_MVAX, REG_TYPE_MVFX, REG_TYPE_MVFX,
- REG_TYPE_MVFX);
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
}
static void
-do_mav_quad_6b (char * str)
+do_t_usat (void)
{
- do_mav_quad (str, MAV_MODE6, REG_TYPE_MVAX, REG_TYPE_MVAX, REG_TYPE_MVFX,
- REG_TYPE_MVFX);
-}
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ inst.instruction |= inst.operands[2].reg << 16;
-/* cfmvsc32<cond> DSPSC,MVDX[15:0]. */
-static void
-do_mav_dspsc_1 (char * str)
-{
- skip_whitespace (str);
-
- /* cfmvsc32. */
- if (mav_reg_required_here (&str, -1, REG_TYPE_DSPSC) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, 12, REG_TYPE_MVDX) == FAIL)
+ if (inst.operands[3].present)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ if (inst.reloc.exp.X_add_number != 0)
+ {
+ if (inst.operands[3].shift_kind == SHIFT_ASR)
+ inst.instruction |= 0x00200000; /* sh bit */
- return;
+ inst.instruction |= (inst.reloc.exp.X_add_number & 0x1c) << 10;
+ inst.instruction |= (inst.reloc.exp.X_add_number & 0x03) << 6;
+ }
+ inst.reloc.type = BFD_RELOC_UNUSED;
}
+}
- end_of_line (str);
+static void
+do_t_usat16 (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ inst.instruction |= inst.operands[2].reg << 16;
}
+
+/* Overall per-instruction processing. */
+
+/* We need to be able to fix up arbitrary expressions in some statements.
+ This is so that we can handle symbols that are an arbitrary distance from
+ the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
+ which returns part of an address in a form which will be valid for
+ a data instruction. We do this by pushing the expression into a symbol
+ in the expr_section, and creating a fix for that. */
-/* cfmv32sc<cond> MVDX[15:0],DSPSC. */
static void
-do_mav_dspsc_2 (char * str)
+fix_new_arm (fragS * frag,
+ int where,
+ short int size,
+ expressionS * exp,
+ int pc_rel,
+ int reloc)
{
- skip_whitespace (str);
+ fixS * new_fix;
- /* cfmv32sc. */
- if (mav_reg_required_here (&str, 12, REG_TYPE_MVDX) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, -1, REG_TYPE_DSPSC) == FAIL)
+ switch (exp->X_op)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ case O_constant:
+ case O_symbol:
+ case O_add:
+ case O_subtract:
+ new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+ break;
- return;
+ default:
+ new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
+ pc_rel, reloc);
+ break;
}
- end_of_line (str);
+ /* Mark whether the fix is to a THUMB instruction, or an ARM
+ instruction. */
+ new_fix->tc_fix_data = (PTR) thumb_mode;
}
-/* Maverick shift immediate instructions.
- cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
- cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0]. */
-
static void
-do_mav_shift (char * str,
- enum arm_reg_type reg0,
- enum arm_reg_type reg1)
+output_inst (const char * str)
{
- int error;
- int imm, neg = 0;
-
- skip_whitespace (str);
-
- error = 0;
+ char * to = NULL;
- if (mav_reg_required_here (&str, 12, reg0) == FAIL
- || skip_past_comma (&str) == FAIL
- || mav_reg_required_here (&str, 16, reg1) == FAIL
- || skip_past_comma (&str) == FAIL)
+ if (inst.error)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ as_bad ("%s -- `%s'", inst.error, str);
return;
}
+ if (inst.size == 0)
+ return;
- /* Calculate the immediate operand.
- The operand is a 7bit signed number. */
- skip_whitespace (str);
-
- if (*str == '#')
- ++str;
+ to = frag_more (inst.size);
- if (!ISDIGIT (*str) && *str != '-')
+ if (thumb_mode && (inst.size > THUMB_SIZE))
{
- inst.error = _("expecting immediate, 7bit operand");
- return;
+ assert (inst.size == (2 * THUMB_SIZE));
+ md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
+ md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
}
-
- if (*str == '-')
+ else if (inst.size > INSN_SIZE)
{
- neg = 1;
- ++str;
+ assert (inst.size == (2 * INSN_SIZE));
+ md_number_to_chars (to, inst.instruction, INSN_SIZE);
+ md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
}
+ else
+ md_number_to_chars (to, inst.instruction, inst.size);
- for (imm = 0; *str && ISDIGIT (*str); ++str)
- imm = imm * 10 + *str - '0';
+ if (inst.reloc.type != BFD_RELOC_UNUSED)
+ fix_new_arm (frag_now, to - frag_now->fr_literal,
+ inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
+ inst.reloc.type);
- if (imm > 64)
- {
- inst.error = _("immediate out of range");
- return;
- }
+#ifdef OBJ_ELF
+ dwarf2_emit_insn (inst.size);
+#endif
+}
- /* Make negative imm's into 7bit signed numbers. */
- if (neg)
- {
- imm = -imm;
- imm &= 0x0000007f;
- }
+/* Tag values used in struct asm_opcode's tag field. */
+enum opcode_tag
+{
+ OT_unconditional, /* Instruction cannot be conditionalized.
+ The ARM condition field is still 0xE. */
+ OT_unconditionalF, /* Instruction cannot be conditionalized
+ and carries 0xF in its ARM condition field. */
+ OT_csuffix, /* Instruction takes a conditional suffix. */
+ OT_cinfix3, /* Instruction takes a conditional infix,
+ beginning at character index 3. (In
+ unified mode, it becomes a suffix.) */
+ OT_csuf_or_in3, /* Instruction takes either a conditional
+ suffix or an infix at character index 3.
+ (In unified mode, a suffix only. */
+ OT_odd_infix_unc, /* This is the unconditional variant of an
+ instruction that takes a conditional infix
+ at an unusual position. In unified mode,
+ this variant will accept a suffix. */
+ OT_odd_infix_0 /* Values greater than or equal to OT_odd_infix_0
+ are the conditional variants of instructions that
+ take conditional infixes in unusual positions.
+ The infix appears at character index
+ (tag - OT_odd_infix_0). These are not accepted
+ in unified mode. */
+};
- /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
- Bits 5-7 of the insn should have bits 4-6 of the immediate.
- Bit 4 should be 0. */
- imm = (imm & 0xf) | ((imm & 0x70) << 1);
+/* Subroutine of md_assemble, responsible for looking up the primary
+ opcode from the mnemonic the user wrote. STR points to the
+ beginning of the mnemonic.
+
+ This is not simply a hash table lookup, because of conditional
+ variants. Most instructions have conditional variants, which are
+ expressed with a _conditional affix_ to the mnemonic. If we were
+ to encode each conditional variant as a literal string in the opcode
+ table, it would have approximately 20,000 entries.
+
+ Most mnemonics take this affix as a suffix, and in unified syntax,
+ 'most' is upgraded to 'all'. However, in the divided syntax, some
+ instructions take the affix as an infix, notably the s-variants of
+ the arithmetic instructions. Of those instructions, all but six
+ have the infix appear after the third character of the mnemonic.
+
+ Accordingly, the algorithm for looking up primary opcodes given
+ an identifier is:
+
+ 1. Look up the identifier in the opcode table.
+ If we find a match, go to step U.
+
+ 2. Look up the last two characters of the identifier in the
+ conditions table. If we find a match, look up the first N-2
+ characters of the identifier in the opcode table. If we
+ find a match, go to step CE.
+
+ 3. Look up the fourth and fifth characters of the identifier in
+ the conditions table. If we find a match, extract those
+ characters from the identifier, and look up the remaining
+ characters in the opcode table. If we find a match, go
+ to step CM.
+
+ 4. Fail.
+
+ U. Examine the tag field of the opcode structure, in case this is
+ one of the six instructions with its conditional infix in an
+ unusual place. If it is, the tag tells us where to find the
+ infix; look it up in the conditions table and set inst.cond
+ accordingly. Otherwise, this is an unconditional instruction.
+ Again set inst.cond accordingly. Return the opcode structure.
+
+ CE. Examine the tag field to make sure this is an instruction that
+ should receive a conditional suffix. If it is not, fail.
+ Otherwise, set inst.cond from the suffix we already looked up,
+ and return the opcode structure.
+
+ CM. Examine the tag field to make sure this is an instruction that
+ should receive a conditional infix after the third character.
+ If it is not, fail. Otherwise, undo the edits to the current
+ line of input and proceed as for case CE. */
+
+static const struct asm_opcode *
+opcode_lookup (char **str)
+{
+ char *end, *base;
+ char *affix;
+ const struct asm_opcode *opcode;
+ const struct asm_cond *cond;
+
+ /* Scan up to the end of the mnemonic, which must end in white space,
+ '.' (in unified mode only), or end of string. */
+ for (base = end = *str; *end != '\0'; end++)
+ if (*end == ' ' || (unified_syntax && *end == '.'))
+ break;
- inst.instruction |= imm;
- end_of_line (str);
-}
+ if (end == base)
+ return 0;
-static void
-do_mav_shift_1 (char * str)
-{
- do_mav_shift (str, REG_TYPE_MVFX, REG_TYPE_MVFX);
-}
+ /* Handle a possible width suffix. */
+ if (end[0] == '.')
+ {
+ if (end[1] == 'w' && (end[2] == ' ' || end[2] == '\0'))
+ inst.size_req = 4;
+ else if (end[1] == 'n' && (end[2] == ' ' || end[2] == '\0'))
+ inst.size_req = 2;
+ else
+ return 0;
-static void
-do_mav_shift_2 (char * str)
-{
- do_mav_shift (str, REG_TYPE_MVDX, REG_TYPE_MVDX);
-}
+ *str = end + 2;
+ }
+ else
+ *str = end;
-static int
-mav_parse_offset (char ** str, int * negative)
-{
- char * p = *str;
- int offset;
+ /* Look for unaffixed or special-case affixed mnemonic. */
+ opcode = hash_find_n (arm_ops_hsh, base, end - base);
+ if (opcode)
+ {
+ /* step U */
+ if (opcode->tag < OT_odd_infix_0)
+ {
+ inst.cond = COND_ALWAYS;
+ return opcode;
+ }
- *negative = 0;
+ if (unified_syntax)
+ as_warn (_("conditional infixes are deprecated in unified syntax"));
+ affix = base + (opcode->tag - OT_odd_infix_0);
+ cond = hash_find_n (arm_cond_hsh, affix, 2);
+ assert (cond);
- skip_whitespace (p);
+ inst.cond = cond->value;
+ return opcode;
+ }
- if (*p == '#')
- ++p;
+ /* Cannot have a conditional suffix on a mnemonic of less than two
+ characters. */
+ if (end - base < 3)
+ return 0;
- if (*p == '-')
- {
- *negative = 1;
- ++p;
- }
+ /* Look for suffixed mnemonic. */
+ affix = end - 2;
+ cond = hash_find_n (arm_cond_hsh, affix, 2);
+ opcode = hash_find_n (arm_ops_hsh, base, affix - base);
+ if (opcode && cond)
+ {
+ /* step CE */
+ switch (opcode->tag)
+ {
+ case OT_cinfix3:
+ case OT_odd_infix_unc:
+ if (!unified_syntax)
+ return 0;
+ /* else fall through */
+
+ case OT_csuffix:
+ case OT_csuf_or_in3:
+ inst.cond = cond->value;
+ return opcode;
+
+ case OT_unconditional:
+ case OT_unconditionalF:
+ /* delayed diagnostic */
+ inst.error = BAD_COND;
+ inst.cond = COND_ALWAYS;
+ return opcode;
- if (!ISDIGIT (*p))
- {
- inst.error = _("offset expected");
- return 0;
+ default:
+ return 0;
+ }
}
- for (offset = 0; *p && ISDIGIT (*p); ++p)
- offset = offset * 10 + *p - '0';
+ /* Cannot have a usual-position infix on a mnemonic of less than
+ six characters (five would be a suffix). */
+ if (end - base < 6)
+ return 0;
- if (offset > 0x3fc)
+ /* Look for infixed mnemonic in the usual position. */
+ affix = base + 3;
+ cond = hash_find_n (arm_cond_hsh, affix, 2);
+ if (cond)
{
- inst.error = _("offset out of range");
- return 0;
+ char save[2];
+ memcpy (save, affix, 2);
+ memmove (affix, affix + 2, (end - affix) - 2);
+ opcode = hash_find_n (arm_ops_hsh, base, (end - base) - 2);
+ memmove (affix + 2, affix, (end - affix) - 2);
+ memcpy (affix, save, 2);
}
- if (offset & 0x3)
+ if (opcode && (opcode->tag == OT_cinfix3 || opcode->tag == OT_csuf_or_in3))
{
- inst.error = _("offset not a multiple of 4");
- return 0;
- }
+ /* step CM */
+ if (unified_syntax)
+ as_warn (_("conditional infixes are deprecated in unified syntax"));
- *str = p;
+ inst.cond = cond->value;
+ return opcode;
+ }
- return *negative ? -offset : offset;
+ return 0;
}
-/* Maverick load/store instructions.
- <insn><cond> CRd,[Rn,<offset>]{!}.
- <insn><cond> CRd,[Rn],<offset>. */
-
-static void
-do_mav_ldst (char * str, enum arm_reg_type reg0)
+void
+md_assemble (char *str)
{
- int offset, negative;
+ char *p = str;
+ const struct asm_opcode * opcode;
- skip_whitespace (str);
+ /* Align the previous label if needed. */
+ if (last_label_seen != NULL)
+ {
+ symbol_set_frag (last_label_seen, frag_now);
+ S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
+ S_SET_SEGMENT (last_label_seen, now_seg);
+ }
- if (mav_reg_required_here (&str, 12, reg0) == FAIL
- || skip_past_comma (&str) == FAIL
- || *str++ != '['
- || reg_required_here (&str, 16) == FAIL)
- goto fail_ldst;
+ memset (&inst, '\0', sizeof (inst));
+ inst.reloc.type = BFD_RELOC_UNUSED;
- if (skip_past_comma (&str) == SUCCESS)
+ opcode = opcode_lookup (&p);
+ if (!opcode)
{
- /* You are here: "<offset>]{!}". */
- inst.instruction |= PRE_INDEX;
-
- offset = mav_parse_offset (&str, &negative);
+ /* It wasn't an instruction, but it might be a register alias of
+ the form alias .req reg. */
+ if (!create_register_alias (str, p))
+ as_bad (_("bad instruction `%s'"), str);
- if (inst.error)
- return;
+ return;
+ }
- if (*str++ != ']')
+ if (thumb_mode)
+ {
+ /* Check that this instruction is supported for this CPU. */
+ if (thumb_mode == 1 && (opcode->tvariant & cpu_variant) == 0)
{
- inst.error = _("missing ]");
+ as_bad (_("selected processor does not support `%s'"), str);
return;
}
+ if (inst.cond != COND_ALWAYS && !unified_syntax
+ && opcode->tencode != do_t_branch)
+ {
+ as_bad (_("Thumb does not support conditional execution"));
+ return;
+ }
+
+ mapping_state (MAP_THUMB);
+ inst.instruction = opcode->tvalue;
+
+ if (!parse_operands (p, opcode->operands))
+ opcode->tencode ();
- if (*str == '!')
+ if (!inst.error)
{
- inst.instruction |= WRITE_BACK;
- ++str;
+ assert (inst.instruction < 0xe800 || inst.instruction > 0xffff);
+ inst.size = (inst.instruction > 0xffff ? 4 : 2);
+ if (inst.size_req && inst.size_req != inst.size)
+ {
+ as_bad (_("cannot honor width suffix -- `%s'"), str);
+ return;
+ }
}
}
else
{
- /* You are here: "], <offset>". */
- if (*str++ != ']')
+ /* Check that this instruction is supported for this CPU. */
+ if ((opcode->avariant & cpu_variant) == 0)
{
- inst.error = _("missing ]");
+ as_bad (_("selected processor does not support `%s'"), str);
+ return;
+ }
+ if (inst.size_req)
+ {
+ as_bad (_("width suffixes are invalid in ARM mode -- `%s'"), str);
return;
}
- if (skip_past_comma (&str) == FAIL
- || (offset = mav_parse_offset (&str, &negative), inst.error))
- goto fail_ldst;
-
- inst.instruction |= CP_T_WB; /* Post indexed, set bit W. */
+ mapping_state (MAP_ARM);
+ inst.instruction = opcode->avalue;
+ if (opcode->tag == OT_unconditionalF)
+ inst.instruction |= 0xF << 28;
+ else
+ inst.instruction |= inst.cond << 28;
+ inst.size = INSN_SIZE;
+ if (!parse_operands (p, opcode->operands))
+ opcode->aencode ();
}
-
- if (negative)
- offset = -offset;
- else
- inst.instruction |= CP_T_UD; /* Positive, so set bit U. */
-
- inst.instruction |= offset >> 2;
- end_of_line (str);
- return;
-
-fail_ldst:
- if (!inst.error)
- inst.error = BAD_ARGS;
+ output_inst (str);
}
-static void
-do_mav_ldst_1 (char * str)
-{
- do_mav_ldst (str, REG_TYPE_MVF);
-}
+/* Various frobbings of labels and their addresses. */
-static void
-do_mav_ldst_2 (char * str)
+void
+arm_start_line_hook (void)
{
- do_mav_ldst (str, REG_TYPE_MVD);
+ last_label_seen = NULL;
}
-static void
-do_mav_ldst_3 (char * str)
+void
+arm_frob_label (symbolS * sym)
{
- do_mav_ldst (str, REG_TYPE_MVFX);
-}
+ last_label_seen = sym;
-static void
-do_mav_ldst_4 (char * str)
-{
- do_mav_ldst (str, REG_TYPE_MVDX);
-}
+ ARM_SET_THUMB (sym, thumb_mode);
-static void
-do_t_nop (char * str)
-{
- /* Do nothing. */
- end_of_line (str);
-}
+#if defined OBJ_COFF || defined OBJ_ELF
+ ARM_SET_INTERWORK (sym, support_interwork);
+#endif
-/* Handle the Format 4 instructions that do not have equivalents in other
- formats. That is, ADC, AND, EOR, SBC, ROR, TST, NEG, CMN, ORR, MUL,
- BIC and MVN. */
+ /* Note - do not allow local symbols (.Lxxx) to be labeled
+ as Thumb functions. This is because these labels, whilst
+ they exist inside Thumb code, are not the entry points for
+ possible ARM->Thumb calls. Also, these labels can be used
+ as part of a computed goto or switch statement. eg gcc
+ can generate code that looks like this:
-static void
-do_t_arit (char * str)
-{
- int Rd, Rs, Rn;
+ ldr r2, [pc, .Laaa]
+ lsl r3, r3, #2
+ ldr r2, [r3, r2]
+ mov pc, r2
- skip_whitespace (str);
+ .Lbbb: .word .Lxxx
+ .Lccc: .word .Lyyy
+ ..etc...
+ .Laaa: .word Lbbb
- if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (Rs = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- {
- inst.error = BAD_ARGS;
- return;
- }
+ The first instruction loads the address of the jump table.
+ The second instruction converts a table index into a byte offset.
+ The third instruction gets the jump address out of the table.
+ The fourth instruction performs the jump.
- if (skip_past_comma (&str) != FAIL)
+ If the address stored at .Laaa is that of a symbol which has the
+ Thumb_Func bit set, then the linker will arrange for this address
+ to have the bottom bit set, which in turn would mean that the
+ address computation performed by the third instruction would end
+ up with the bottom bit set. Since the ARM is capable of unaligned
+ word loads, the instruction would then load the incorrect address
+ out of the jump table, and chaos would ensue. */
+ if (label_is_thumb_function_name
+ && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
+ && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
{
- /* Three operand format not allowed for TST, CMN, NEG and MVN.
- (It isn't allowed for CMP either, but that isn't handled by this
- function.) */
- if (inst.instruction == T_OPCODE_TST
- || inst.instruction == T_OPCODE_CMN
- || inst.instruction == T_OPCODE_NEG
- || inst.instruction == T_OPCODE_MVN)
- {
- inst.error = BAD_ARGS;
- return;
- }
+ /* When the address of a Thumb function is taken the bottom
+ bit of that address should be set. This will allow
+ interworking between Arm and Thumb functions to work
+ correctly. */
- if ((Rn = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
+ THUMB_SET_FUNC (sym, 1);
- if (Rs != Rd)
- {
- inst.error = _("dest and source1 must be the same register");
- return;
- }
- Rs = Rn;
+ label_is_thumb_function_name = FALSE;
}
-
- if (inst.instruction == T_OPCODE_MUL
- && Rs == Rd)
- as_tsktsk (_("Rs and Rd must be different in MUL"));
-
- inst.instruction |= Rd | (Rs << 3);
- end_of_line (str);
}
-static void
-do_t_add (char * str)
+int
+arm_data_in_code (void)
{
- thumb_add_sub (str, 0);
-}
+ if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
+ {
+ *input_line_pointer = '/';
+ input_line_pointer += 5;
+ *input_line_pointer = 0;
+ return 1;
+ }
-static void
-do_t_asr (char * str)
-{
- thumb_shift (str, THUMB_ASR);
+ return 0;
}
-static void
-do_t_branch9 (char * str)
+char *
+arm_canonicalize_symbol_name (char * name)
{
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
- inst.reloc.pc_rel = 1;
- end_of_line (str);
-}
+ int len;
-static void
-do_t_branch12 (char * str)
-{
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
- inst.reloc.pc_rel = 1;
- end_of_line (str);
+ if (thumb_mode && (len = strlen (name)) > 5
+ && streq (name + len - 5, "/data"))
+ *(name + len - 5) = 0;
+
+ return name;
}
+
+/* Table of all register names defined by default. The user can
+ define additional names with .req. Note that all register names
+ should appear in both upper and lowercase variants. Some registers
+ also have mixed-case names. */
-/* Find the real, Thumb encoded start of a Thumb function. */
+#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE }
+#define REGNUM(p,n,t) REGDEF(p##n, n, t)
+#define REGSET(p,t) \
+ REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \
+ REGNUM(p, 4,t), REGNUM(p, 5,t), REGNUM(p, 6,t), REGNUM(p, 7,t), \
+ REGNUM(p, 8,t), REGNUM(p, 9,t), REGNUM(p,10,t), REGNUM(p,11,t), \
+ REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t)
-static symbolS *
-find_real_start (symbolS * symbolP)
+static const struct reg_entry reg_names[] =
{
- 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 ();
+ /* ARM integer registers. */
+ REGSET(r, RN), REGSET(R, RN),
- /* Names that start with '.' are local labels, not function entry points.
- The compiler may generate BL instructions to these labels because it
- needs to perform a branch to a far away location. */
- if (name[0] == '.')
- return symbolP;
+ /* ATPCS synonyms. */
+ REGDEF(a1,0,RN), REGDEF(a2,1,RN), REGDEF(a3, 2,RN), REGDEF(a4, 3,RN),
+ REGDEF(v1,4,RN), REGDEF(v2,5,RN), REGDEF(v3, 6,RN), REGDEF(v4, 7,RN),
+ REGDEF(v5,8,RN), REGDEF(v6,9,RN), REGDEF(v7,10,RN), REGDEF(v8,11,RN),
- real_start = malloc (strlen (name) + strlen (STUB_NAME) + 1);
- sprintf (real_start, "%s%s", STUB_NAME, name);
+ REGDEF(A1,0,RN), REGDEF(A2,1,RN), REGDEF(A3, 2,RN), REGDEF(A4, 3,RN),
+ REGDEF(V1,4,RN), REGDEF(V2,5,RN), REGDEF(V3, 6,RN), REGDEF(V4, 7,RN),
+ REGDEF(V5,8,RN), REGDEF(V6,9,RN), REGDEF(V7,10,RN), REGDEF(V8,11,RN),
- new_target = symbol_find (real_start);
+ /* Well-known aliases. */
+ REGDEF(wr, 7,RN), REGDEF(sb, 9,RN), REGDEF(sl,10,RN), REGDEF(fp,11,RN),
+ REGDEF(ip,12,RN), REGDEF(sp,13,RN), REGDEF(lr,14,RN), REGDEF(pc,15,RN),
+
+ REGDEF(WR, 7,RN), REGDEF(SB, 9,RN), REGDEF(SL,10,RN), REGDEF(FP,11,RN),
+ REGDEF(IP,12,RN), REGDEF(SP,13,RN), REGDEF(LR,14,RN), REGDEF(PC,15,RN),
+
+ /* Coprocessor numbers. */
+ REGSET(p, CP), REGSET(P, CP),
+
+ /* Coprocessor register numbers. The "cr" variants are for backward
+ compatibility. */
+ REGSET(c, CN), REGSET(C, CN),
+ REGSET(cr, CN), REGSET(CR, CN),
+
+ /* FPA registers. */
+ REGNUM(f,0,FN), REGNUM(f,1,FN), REGNUM(f,2,FN), REGNUM(f,3,FN),
+ REGNUM(f,4,FN), REGNUM(f,5,FN), REGNUM(f,6,FN), REGNUM(f,7, FN),
+
+ REGNUM(F,0,FN), REGNUM(F,1,FN), REGNUM(F,2,FN), REGNUM(F,3,FN),
+ REGNUM(F,4,FN), REGNUM(F,5,FN), REGNUM(F,6,FN), REGNUM(F,7, FN),
+
+ /* VFP SP registers. */
+ REGSET(s,VFS),
+ REGNUM(s,16,VFS), REGNUM(s,17,VFS), REGNUM(s,18,VFS), REGNUM(s,19,VFS),
+ REGNUM(s,20,VFS), REGNUM(s,21,VFS), REGNUM(s,22,VFS), REGNUM(s,23,VFS),
+ REGNUM(s,24,VFS), REGNUM(s,25,VFS), REGNUM(s,26,VFS), REGNUM(s,27,VFS),
+ REGNUM(s,28,VFS), REGNUM(s,29,VFS), REGNUM(s,30,VFS), REGNUM(s,31,VFS),
+
+ REGSET(S,VFS),
+ REGNUM(S,16,VFS), REGNUM(S,17,VFS), REGNUM(S,18,VFS), REGNUM(S,19,VFS),
+ REGNUM(S,20,VFS), REGNUM(S,21,VFS), REGNUM(S,22,VFS), REGNUM(S,23,VFS),
+ REGNUM(S,24,VFS), REGNUM(S,25,VFS), REGNUM(S,26,VFS), REGNUM(S,27,VFS),
+ REGNUM(S,28,VFS), REGNUM(S,29,VFS), REGNUM(S,30,VFS), REGNUM(S,31,VFS),
+
+ /* VFP DP Registers. */
+ REGSET(d,VFD), REGSET(D,VFS),
+
+ /* VFP control registers. */
+ REGDEF(fpsid,0,VFC), REGDEF(fpscr,1,VFC), REGDEF(fpexc,8,VFC),
+ REGDEF(FPSID,0,VFC), REGDEF(FPSCR,1,VFC), REGDEF(FPEXC,8,VFC),
+
+ /* Maverick DSP coprocessor registers. */
+ REGSET(mvf,MVF), REGSET(mvd,MVD), REGSET(mvfx,MVFX), REGSET(mvdx,MVDX),
+ REGSET(MVF,MVF), REGSET(MVD,MVD), REGSET(MVFX,MVFX), REGSET(MVDX,MVDX),
+
+ REGNUM(mvax,0,MVAX), REGNUM(mvax,1,MVAX),
+ REGNUM(mvax,2,MVAX), REGNUM(mvax,3,MVAX),
+ REGDEF(dspsc,0,DSPSC),
+
+ REGNUM(MVAX,0,MVAX), REGNUM(MVAX,1,MVAX),
+ REGNUM(MVAX,2,MVAX), REGNUM(MVAX,3,MVAX),
+ REGDEF(DSPSC,0,DSPSC),
+
+ /* iWMMXt data registers - p0, c0-15. */
+ REGSET(wr,MMXWR), REGSET(wR,MMXWR), REGSET(WR, MMXWR),
+
+ /* iWMMXt control registers - p1, c0-3. */
+ REGDEF(wcid, 0,MMXWC), REGDEF(wCID, 0,MMXWC), REGDEF(WCID, 0,MMXWC),
+ REGDEF(wcon, 1,MMXWC), REGDEF(wCon, 1,MMXWC), REGDEF(WCON, 1,MMXWC),
+ REGDEF(wcssf, 2,MMXWC), REGDEF(wCSSF, 2,MMXWC), REGDEF(WCSSF, 2,MMXWC),
+ REGDEF(wcasf, 3,MMXWC), REGDEF(wCASF, 3,MMXWC), REGDEF(WCASF, 3,MMXWC),
+
+ /* iWMMXt scalar (constant/offset) registers - p1, c8-11. */
+ REGDEF(wcgr0, 8,MMXWCG), REGDEF(wCGR0, 8,MMXWCG), REGDEF(WCGR0, 8,MMXWCG),
+ REGDEF(wcgr1, 9,MMXWCG), REGDEF(wCGR1, 9,MMXWCG), REGDEF(WCGR1, 9,MMXWCG),
+ REGDEF(wcgr2,10,MMXWCG), REGDEF(wCGR2,10,MMXWCG), REGDEF(WCGR2,10,MMXWCG),
+ REGDEF(wcgr3,11,MMXWCG), REGDEF(wCGR3,11,MMXWCG), REGDEF(WCGR3,11,MMXWCG),
+
+ /* XScale accumulator registers. */
+ REGNUM(acc,0,XSCALE), REGNUM(ACC,0,XSCALE),
+};
+#undef REGDEF
+#undef REGNUM
+#undef REGSET
- if (new_target == NULL)
- {
- as_warn ("Failed to find real start of function: %s\n", name);
- new_target = symbolP;
- }
+/* Table of all PSR suffixes. Bare "CPSR" and "SPSR" are handled
+ within psr_required_here. */
+static const struct asm_psr psrs[] =
+{
+ /* Backward compatibility notation. Note that "all" is no longer
+ truly all possible PSR bits. */
+ {"all", PSR_c | PSR_f},
+ {"flg", PSR_f},
+ {"ctl", PSR_c},
+
+ /* Individual flags. */
+ {"f", PSR_f},
+ {"c", PSR_c},
+ {"x", PSR_x},
+ {"s", PSR_s},
+ /* Combinations of flags. */
+ {"fs", PSR_f | PSR_s},
+ {"fx", PSR_f | PSR_x},
+ {"fc", PSR_f | PSR_c},
+ {"sf", PSR_s | PSR_f},
+ {"sx", PSR_s | PSR_x},
+ {"sc", PSR_s | PSR_c},
+ {"xf", PSR_x | PSR_f},
+ {"xs", PSR_x | PSR_s},
+ {"xc", PSR_x | PSR_c},
+ {"cf", PSR_c | PSR_f},
+ {"cs", PSR_c | PSR_s},
+ {"cx", PSR_c | PSR_x},
+ {"fsx", PSR_f | PSR_s | PSR_x},
+ {"fsc", PSR_f | PSR_s | PSR_c},
+ {"fxs", PSR_f | PSR_x | PSR_s},
+ {"fxc", PSR_f | PSR_x | PSR_c},
+ {"fcs", PSR_f | PSR_c | PSR_s},
+ {"fcx", PSR_f | PSR_c | PSR_x},
+ {"sfx", PSR_s | PSR_f | PSR_x},
+ {"sfc", PSR_s | PSR_f | PSR_c},
+ {"sxf", PSR_s | PSR_x | PSR_f},
+ {"sxc", PSR_s | PSR_x | PSR_c},
+ {"scf", PSR_s | PSR_c | PSR_f},
+ {"scx", PSR_s | PSR_c | PSR_x},
+ {"xfs", PSR_x | PSR_f | PSR_s},
+ {"xfc", PSR_x | PSR_f | PSR_c},
+ {"xsf", PSR_x | PSR_s | PSR_f},
+ {"xsc", PSR_x | PSR_s | PSR_c},
+ {"xcf", PSR_x | PSR_c | PSR_f},
+ {"xcs", PSR_x | PSR_c | PSR_s},
+ {"cfs", PSR_c | PSR_f | PSR_s},
+ {"cfx", PSR_c | PSR_f | PSR_x},
+ {"csf", PSR_c | PSR_s | PSR_f},
+ {"csx", PSR_c | PSR_s | PSR_x},
+ {"cxf", PSR_c | PSR_x | PSR_f},
+ {"cxs", PSR_c | PSR_x | PSR_s},
+ {"fsxc", PSR_f | PSR_s | PSR_x | PSR_c},
+ {"fscx", PSR_f | PSR_s | PSR_c | PSR_x},
+ {"fxsc", PSR_f | PSR_x | PSR_s | PSR_c},
+ {"fxcs", PSR_f | PSR_x | PSR_c | PSR_s},
+ {"fcsx", PSR_f | PSR_c | PSR_s | PSR_x},
+ {"fcxs", PSR_f | PSR_c | PSR_x | PSR_s},
+ {"sfxc", PSR_s | PSR_f | PSR_x | PSR_c},
+ {"sfcx", PSR_s | PSR_f | PSR_c | PSR_x},
+ {"sxfc", PSR_s | PSR_x | PSR_f | PSR_c},
+ {"sxcf", PSR_s | PSR_x | PSR_c | PSR_f},
+ {"scfx", PSR_s | PSR_c | PSR_f | PSR_x},
+ {"scxf", PSR_s | PSR_c | PSR_x | PSR_f},
+ {"xfsc", PSR_x | PSR_f | PSR_s | PSR_c},
+ {"xfcs", PSR_x | PSR_f | PSR_c | PSR_s},
+ {"xsfc", PSR_x | PSR_s | PSR_f | PSR_c},
+ {"xscf", PSR_x | PSR_s | PSR_c | PSR_f},
+ {"xcfs", PSR_x | PSR_c | PSR_f | PSR_s},
+ {"xcsf", PSR_x | PSR_c | PSR_s | PSR_f},
+ {"cfsx", PSR_c | PSR_f | PSR_s | PSR_x},
+ {"cfxs", PSR_c | PSR_f | PSR_x | PSR_s},
+ {"csfx", PSR_c | PSR_s | PSR_f | PSR_x},
+ {"csxf", PSR_c | PSR_s | PSR_x | PSR_f},
+ {"cxfs", PSR_c | PSR_x | PSR_f | PSR_s},
+ {"cxsf", PSR_c | PSR_x | PSR_s | PSR_f},
+};
- free (real_start);
+/* Table of all shift-in-operand names. */
+static const struct asm_shift_name shift_names [] =
+{
+ { "asl", SHIFT_LSL }, { "ASL", SHIFT_LSL },
+ { "lsl", SHIFT_LSL }, { "LSL", SHIFT_LSL },
+ { "lsr", SHIFT_LSR }, { "LSR", SHIFT_LSR },
+ { "asr", SHIFT_ASR }, { "ASR", SHIFT_ASR },
+ { "ror", SHIFT_ROR }, { "ROR", SHIFT_ROR },
+ { "rrx", SHIFT_RRX }, { "RRX", SHIFT_RRX }
+};
- return new_target;
-}
+/* Table of all explicit relocation names. */
+#ifdef OBJ_ELF
+static struct reloc_entry reloc_names[] =
+{
+ { "got", BFD_RELOC_ARM_GOT32 }, { "GOT", BFD_RELOC_ARM_GOT32 },
+ { "gotoff", BFD_RELOC_ARM_GOTOFF }, { "GOTOFF", BFD_RELOC_ARM_GOTOFF },
+ { "plt", BFD_RELOC_ARM_PLT32 }, { "PLT", BFD_RELOC_ARM_PLT32 },
+ { "target1", BFD_RELOC_ARM_TARGET1 }, { "TARGET1", BFD_RELOC_ARM_TARGET1 },
+ { "target2", BFD_RELOC_ARM_TARGET2 }, { "TARGET2", BFD_RELOC_ARM_TARGET2 },
+ { "sbrel", BFD_RELOC_ARM_SBREL32 }, { "SBREL", BFD_RELOC_ARM_SBREL32 },
+ { "tlsgd", BFD_RELOC_ARM_TLS_GD32}, { "TLSGD", BFD_RELOC_ARM_TLS_GD32},
+ { "tlsldm", BFD_RELOC_ARM_TLS_LDM32}, { "TLSLDM", BFD_RELOC_ARM_TLS_LDM32},
+ { "tlsldo", BFD_RELOC_ARM_TLS_LDO32}, { "TLSLDO", BFD_RELOC_ARM_TLS_LDO32},
+ { "gottpoff",BFD_RELOC_ARM_TLS_IE32}, { "GOTTPOFF",BFD_RELOC_ARM_TLS_IE32},
+ { "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32}
+};
+#endif
-static void
-do_t_branch23 (char * str)
+/* Table of all conditional affixes. 0xF is not defined as a condition code. */
+static const struct asm_cond conds[] =
{
- if (my_get_expression (& inst.reloc.exp, & str))
- return;
-
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
- inst.reloc.pc_rel = 1;
- end_of_line (str);
+ {"eq", 0x0},
+ {"ne", 0x1},
+ {"cs", 0x2}, {"hs", 0x2},
+ {"cc", 0x3}, {"ul", 0x3}, {"lo", 0x3},
+ {"mi", 0x4},
+ {"pl", 0x5},
+ {"vs", 0x6},
+ {"vc", 0x7},
+ {"hi", 0x8},
+ {"ls", 0x9},
+ {"ge", 0xa},
+ {"lt", 0xb},
+ {"gt", 0xc},
+ {"le", 0xd},
+ {"al", 0xe}
+};
- /* If the destination of the branch is a defined symbol which does not have
- the THUMB_FUNC attribute, then we must be calling a function which has
- the (interfacearm) attribute. We look for the Thumb entry point to that
- function and change the branch to refer to that function instead. */
- if ( inst.reloc.exp.X_op == O_symbol
- && inst.reloc.exp.X_add_symbol != NULL
- && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
- && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
- inst.reloc.exp.X_add_symbol =
- find_real_start (inst.reloc.exp.X_add_symbol);
-}
+/* Table of ARM-format instructions. */
+
+/* Macros for gluing together operand strings. N.B. In all cases
+ other than OPS0, the trailing OP_stop comes from default
+ zero-initialization of the unspecified elements of the array. */
+#define OPS0() { OP_stop, }
+#define OPS1(a) { OP_##a, }
+#define OPS2(a,b) { OP_##a,OP_##b, }
+#define OPS3(a,b,c) { OP_##a,OP_##b,OP_##c, }
+#define OPS4(a,b,c,d) { OP_##a,OP_##b,OP_##c,OP_##d, }
+#define OPS5(a,b,c,d,e) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e, }
+#define OPS6(a,b,c,d,e,f) { OP_##a,OP_##b,OP_##c,OP_##d,OP_##e,OP_##f, }
+
+/* These macros abstract out the exact format of the mnemonic table and
+ save some repeated characters. */
+
+/* The normal sort of mnemonic; has a Thumb variant; takes a conditional suffix. */
+#define TxCE(mnem, op, top, nops, ops, ae, te) \
+ { #mnem, OPS##nops ops, OT_csuffix, 0x##op, top, ARM_VARIANT, \
+ do_##te ? THUMB_VARIANT : 0, do_##ae, do_##te }
+
+/* Two variants of the above - TCE for a numeric Thumb opcode, tCE for
+ a T_MNEM_xyz enumerator. */
+#define TCE(mnem, aop, top, nops, ops, ae, te) \
+ TxCE(mnem, aop, 0x##top, nops, ops, ae, te)
+#define tCE(mnem, aop, top, nops, ops, ae, te) \
+ TxCE(mnem, aop, T_MNEM_##top, nops, ops, ae, te)
+
+/* Second most common sort of mnemonic: has a Thumb variant, takes a conditional
+ infix after the third character. */
+#define TxC3(mnem, op, top, nops, ops, ae, te) \
+ { #mnem, OPS##nops ops, OT_cinfix3, 0x##op, top, ARM_VARIANT, \
+ do_##te ? THUMB_VARIANT : 0, do_##ae, do_##te }
+#define TC3(mnem, aop, top, nops, ops, ae, te) \
+ TxC3(mnem, aop, 0x##top, nops, ops, ae, te)
+#define tC3(mnem, aop, top, nops, ops, ae, te) \
+ TxC3(mnem, aop, T_MNEM_##top, nops, ops, ae, te)
+
+/* Mnemonic with a conditional infix in an unusual place. Each and every variant has to
+ appear in the condition table. */
+#define TxCM_(m1, m2, m3, op, top, nops, ops, ae, te) \
+ { #m1 #m2 #m3, OPS##nops ops, sizeof(#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof(#m1) - 1, \
+ 0x##op, top, ARM_VARIANT, do_##te ? THUMB_VARIANT : 0, do_##ae, do_##te }
+
+#define TxCM(m1, m2, op, top, nops, ops, ae, te) \
+ TxCM_(m1, , m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, eq, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, ne, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, cs, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, hs, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, cc, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, ul, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, lo, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, mi, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, pl, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, vs, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, vc, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, hi, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, ls, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, ge, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, lt, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, gt, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, le, m2, op, top, nops, ops, ae, te), \
+ TxCM_(m1, al, m2, op, top, nops, ops, ae, te)
+
+#define TCM(m1,m2, aop, top, nops, ops, ae, te) \
+ TxCM(m1,m2, aop, 0x##top, nops, ops, ae, te)
+#define tCM(m1,m2, aop, top, nops, ops, ae, te) \
+ TxCM(m1,m2, aop, T_MNEM_##top, nops, ops, ae, te)
+
+/* Mnemonic that cannot be conditionalized. The ARM condition-code
+ field is still 0xE. */
+#define TUE(mnem, op, top, nops, ops, ae, te) \
+ { #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
+ do_##te ? THUMB_VARIANT : 0, do_##ae, do_##te }
+
+/* Mnemonic that cannot be conditionalized, and bears 0xF in its ARM
+ condition code field. */
+#define TUF(mnem, op, top, nops, ops, ae, te) \
+ { #mnem, OPS##nops ops, OT_unconditionalF, 0x##op, 0x##top, ARM_VARIANT, \
+ do_##te ? THUMB_VARIANT : 0, do_##ae, do_##te }
+
+/* ARM-only variants of all the above. */
+#define CE(mnem, op, nops, ops, ae) TCE(mnem, op, 0, nops, ops, ae, 0)
+#define C3(mnem, op, nops, ops, ae) TC3(mnem, op, 0, nops, ops, ae, 0)
+#define CM(m1,m2, op, nops, ops, ae) TCM(m1,m2, op, 0, nops, ops, ae, 0)
+#define UE(mnem, op, nops, ops, ae) TUE(mnem, op, 0, nops, ops, ae, 0)
+#define UF(mnem, op, nops, ops, ae) TUF(mnem, op, 0, nops, ops, ae, 0)
+#define do_0 0
+
+/* Thumb-only, unconditional. */
+#define UT(mnem, op, nops, ops, te) TUE(mnem, 0, op, nops, ops, 0, te)
+
+/* ARM-only, takes either a suffix or a position-3 infix
+ (for an FPA corner case). */
+#define C3E(mnem, op, nops, ops, ae) \
+ { #mnem, OPS##nops ops, OT_csuf_or_in3, 0x##op, 0, ARM_VARIANT, 0, do_##ae, 0 }
-static void
-do_t_bx (char * str)
+static const struct asm_opcode insns[] =
{
- int reg;
+#define ARM_VARIANT ARM_EXT_V1 /* Core ARM Instructions. */
+#define THUMB_VARIANT ARM_EXT_V4T
+ tCE(and, 0000000, and, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(ands, 0100000, ands, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(eor, 0200000, eor, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(eors, 0300000, eors, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(sub, 0400000, sub, 3, (RR, oRR, SH), arit, t_add_sub),
+ tC3(subs, 0500000, subs, 3, (RR, oRR, SH), arit, t_add_sub),
+ tCE(add, 0800000, add, 3, (RR, oRR, SH), arit, t_add_sub),
+ tC3(adds, 0900000, adds, 3, (RR, oRR, SH), arit, t_add_sub),
+ tCE(adc, 0a00000, adc, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(adcs, 0b00000, adcs, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(sbc, 0c00000, sbc, 3, (RR, oRR, SH), arit, t_arit3),
+ tC3(sbcs, 0d00000, sbcs, 3, (RR, oRR, SH), arit, t_arit3),
+ tCE(orr, 1800000, orr, 3, (RR, oRR, SH), arit, t_arit3c),
+ tC3(orrs, 1900000, orrs, 3, (RR, oRR, SH), arit, t_arit3c),
+ tCE(bic, 1c00000, bic, 3, (RR, oRR, SH), arit, t_arit3),
+ tC3(bics, 1d00000, bics, 3, (RR, oRR, SH), arit, t_arit3),
+
+ /* The p-variants of tst/cmp/cmn/teq (below) are the pre-V6 mechanism
+ for setting PSR flag bits. They are obsolete in V6 and do not
+ have Thumb equivalents. */
+ tCE(tst, 1100000, tst, 2, (RR, SH), cmp, t_mvn_tst),
+ tC3(tsts, 1100000, tst, 2, (RR, SH), cmp, t_mvn_tst),
+ C3(tstp, 110f000, 2, (RR, SH), cmp),
+ tCE(cmp, 1500000, cmp, 2, (RR, SH), cmp, t_mov_cmp),
+ tC3(cmps, 1500000, cmp, 2, (RR, SH), cmp, t_mov_cmp),
+ C3(cmpp, 150f000, 2, (RR, SH), cmp),
+ tCE(cmn, 1700000, cmn, 2, (RR, SH), cmp, t_mvn_tst),
+ tC3(cmns, 1700000, cmn, 2, (RR, SH), cmp, t_mvn_tst),
+ C3(cmnp, 170f000, 2, (RR, SH), cmp),
+
+ tCE(mov, 1a00000, mov, 2, (RR, SH), mov, t_mov_cmp),
+ tC3(movs, 1b00000, movs, 2, (RR, SH), mov, t_mov_cmp),
+ tCE(mvn, 1e00000, mvn, 2, (RR, SH), mov, t_mvn_tst),
+ tC3(mvns, 1f00000, mvns, 2, (RR, SH), mov, t_mvn_tst),
+
+ tCE(ldr, 4100000, ldr, 2, (RR, ADDR), ldst, t_ldst),
+ tC3(ldrb, 4500000, ldrb, 2, (RR, ADDR), ldst, t_ldst),
+ tCE(str, 4000000, str, 2, (RR, ADDR), ldst, t_ldst),
+ tC3(strb, 4400000, strb, 2, (RR, ADDR), ldst, t_ldst),
+
+ tC3(stmia, 8800000, stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3(stmea, 8800000, stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3(ldmia, 8900000, ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ tC3(ldmfd, 8900000, ldmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ TCE(swi, f000000, df00, 1, (EXPi), swi, t_swi),
+#ifdef TE_WINCE
+ /* XXX This is the wrong place to do this. Think multi-arch. */
+ TCE(b, a000000, e7fe, 1, (EXPr), branch, t_branch),
+ TCE(bl, b000000, f7fffffe, 1, (EXPr), branch, t_branch23),
+#else
+ TCE(b, afffffe, e7fe, 1, (EXPr), branch, t_branch),
+ TCE(bl, bfffffe, f7fffffe, 1, (EXPr), branch, t_branch23),
+#endif
- skip_whitespace (str);
+ /* Pseudo ops. */
+ TCE(adr, 28f0000, 000f, 2, (RR, EXP), adr, t_adr),
+ C3(adrl, 28f0000, 2, (RR, EXP), adrl),
+ tCE(nop, 1a00000, nop, 1, (oI255c), nop, t_nop),
+
+ /* Thumb-compatibility pseudo ops. */
+ tCE(lsl, 1a00000, lsl, 3, (RR, oRR, SH), shift, t_shift),
+ tC3(lsls, 1b00000, lsls, 3, (RR, oRR, SH), shift, t_shift),
+ tCE(lsr, 1a00020, lsr, 3, (RR, oRR, SH), shift, t_shift),
+ tC3(lsrs, 1b00020, lsrs, 3, (RR, oRR, SH), shift, t_shift),
+ tCE(asr, 1a00040, asr, 3, (RR, oRR, SH), shift, t_shift),
+ tC3(asrs, 1b00040, asrs, 3, (RR, oRR, SH), shift, t_shift),
+ tCE(ror, 1a00060, ror, 3, (RR, oRR, SH), shift, t_shift),
+ tC3(rors, 1b00060, rors, 3, (RR, oRR, SH), shift, t_shift),
+ tCE(neg, 2600000, neg, 2, (RR, RR), rd_rn, t_neg),
+ tC3(negs, 2700000, negs, 2, (RR, RR), rd_rn, t_neg),
+ tCE(push, 92d0000, push, 1, (REGLST), push_pop, t_push_pop),
+ tCE(pop, 8bd0000, pop, 1, (REGLST), push_pop, t_push_pop),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6
+ TCE(cpy, 1a00000, 4600, 2, (RR, RR), rd_rm, t_cpy),
+
+ /* V1 instructions with no Thumb analogue prior to V6T2. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(rsb, 0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb),
+ TC3(rsbs, 0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb),
+ TCE(teq, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst),
+ TC3(teqs, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst),
+ C3(teqp, 130f000, 2, (RR, SH), cmp),
+
+ TC3(ldrt, 4300000, f8500e00, 2, (RR, ADDR), ldstt, t_ldstt),
+ TC3(ldrbt, 4700000, f8300e00, 2, (RR, ADDR), ldstt, t_ldstt),
+ TC3(strt, 4200000, f8400e00, 2, (RR, ADDR), ldstt, t_ldstt),
+ TC3(strbt, 4600000, f8200e00, 2, (RR, ADDR), ldstt, t_ldstt),
+
+ TC3(stmdb, 9000000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(stmfd, 9000000, e9100000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ TC3(ldmdb, 9100000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+ TC3(ldmea, 9100000, e9000000, 2, (RRw, REGLST), ldmstm, t_ldmstm),
+
+ /* V1 instructions with no Thumb analogue at all. */
+ CE(rsc, 0e00000, 3, (RR, oRR, SH), arit),
+ C3(rscs, 0f00000, 3, (RR, oRR, SH), arit),
+
+ C3(stmib, 9800000, 2, (RRw, REGLST), ldmstm),
+ C3(stmfa, 9800000, 2, (RRw, REGLST), ldmstm),
+ C3(stmda, 8000000, 2, (RRw, REGLST), ldmstm),
+ C3(stmed, 8000000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmib, 9900000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmed, 9900000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmda, 8100000, 2, (RRw, REGLST), ldmstm),
+ C3(ldmfa, 8100000, 2, (RRw, REGLST), ldmstm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V2 /* ARM 2 - multiplies. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V4T
+ tCE(mul, 0000090, mul, 3, (RRnpc, RRnpc, oRR), mul, t_mul),
+ tC3(muls, 0100090, muls, 3, (RRnpc, RRnpc, oRR), mul, t_mul),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(mla, 0200090, fb000000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
+ C3(mlas, 0300090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas),
+
+ /* Generic coprocessor instructions. */
+ TCE(cdp, e000000, ee000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp, cdp),
+ TCE(ldc, c100000, ec100000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TC3(ldcl, c500000, ec500000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TCE(stc, c000000, ec000000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TC3(stcl, c400000, ec400000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TCE(mcr, e000010, ee000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+ TCE(mrc, e100010, ee100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V2S /* ARM 3 - swp instructions. */
+ CE(swp, 1000090, 3, (RRnpc, RRnpc, RRnpcb), rd_rm_rn),
+ C3(swpb, 1400090, 3, (RRnpc, RRnpc, RRnpcb), rd_rm_rn),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V3 /* ARM 6 Status register instructions. */
+ TCE(mrs, 10f0000, f3ef8000, 2, (RR, PSR), mrs, t_mrs),
+ TCE(msr, 120f000, f3808000, 2, (PSR, RR_EXi), msr, t_msr),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V3M /* ARM 7M long multiplies. */
+ TCE(smull, 0c00090, fb800000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM(smull,s, 0d00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE(umull, 0800090, fba00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM(umull,s, 0900090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE(smlal, 0e00090, fbc00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM(smlal,s, 0f00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+ TCE(umlal, 0a00090, fbe00000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull, t_mull),
+ CM(umlal,s, 0b00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mull),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V4 /* ARM Architecture 4. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V4T
+ tC3(ldrh, 01000b0, ldrh, 2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(strh, 00000b0, strh, 2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(ldrsh, 01000f0, ldrsh, 2, (RR, ADDR), ldstv4, t_ldst),
+ tC3(ldrsb, 01000d0, ldrsb, 2, (RR, ADDR), ldstv4, t_ldst),
+ tCM(ld,sh, 01000f0, ldrsh, 2, (RR, ADDR), ldstv4, t_ldst),
+ tCM(ld,sb, 01000d0, ldrsb, 2, (RR, ADDR), ldstv4, t_ldst),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V4T|ARM_EXT_V5
+ /* ARM Architecture 4T. */
+ /* Note: bx (and blx) are required on V5, even if the processor does
+ not support Thumb. */
+ TCE(bx, 12fff10, 4700, 1, (RR), bx, t_bx),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5 /* ARM Architecture 5T. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V5T
+ /* Note: blx has 2 variants; the .value coded here is for
+ BLX(2). Only this variant has conditional execution. */
+ TCE(blx, 12fff30, 4780, 1, (RR_EXr), blx, t_blx),
+ TUE(bkpt, 1200070, be00, 1, (oIffffb), bkpt, t_bkpt),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(clz, 16f0f10, fab0f080, 2, (RRnpc, RRnpc), rd_rm, t_clz),
+ TUF(ldc2, c100000, fc100000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TUF(ldc2l, c500000, fc500000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TUF(stc2, c000000, fc000000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TUF(stc2l, c400000, fc400000, 3, (RCP, RCN, ADDR), lstc, lstc),
+ TUF(cdp2, e000000, fe000000, 6, (RCP, I15b, RCN, RCN, RCN, oI7b), cdp, cdp),
+ TUF(mcr2, e000010, fe000010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+ TUF(mrc2, e100010, fe100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5ExP /* ARM Architecture 5TExP. */
+ TCE(smlabb, 1000080, fb100000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE(smlatb, 10000a0, fb100020, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE(smlabt, 10000c0, fb100010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE(smlatt, 10000e0, fb100030, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+
+ TCE(smlawb, 1200080, fb300000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+ TCE(smlawt, 12000c0, fb300010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
+
+ TCE(smlalbb, 1400080, fbc00080, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+ TCE(smlaltb, 14000a0, fbc000a0, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+ TCE(smlalbt, 14000c0, fbc00090, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+ TCE(smlaltt, 14000e0, fbc000b0, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smlal, t_mlal),
+
+ TCE(smulbb, 1600080, fb10f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smultb, 16000a0, fb10f020, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smulbt, 16000c0, fb10f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smultt, 16000e0, fb10f030, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+
+ TCE(smulwb, 12000a0, fb30f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smulwt, 12000e0, fb30f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+
+ TCE(qadd, 1000050, fa80f080, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, rd_rm_rn),
+ TCE(qdadd, 1400050, fa80f090, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, rd_rm_rn),
+ TCE(qsub, 1200050, fa80f0a0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, rd_rm_rn),
+ TCE(qdsub, 1600050, fa80f0b0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, rd_rm_rn),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5E /* ARM Architecture 5TE. */
+ TUF(pld, 450f000, f810f000, 1, (ADDR), pld, t_pld),
+ TC3(ldrd, 00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd),
+ TC3(strd, 00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDR), ldrd, t_ldstd),
+
+ TCE(mcrr, c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TCE(mrrc, c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V5J /* ARM Architecture 5TEJ. */
+ TCE(bxj, 12fff20, f3c08f00, 1, (RR), bxj, t_bxj),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6 /* ARM V6. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6
+ TUF(cpsie, 1080000, b660, 2, (CPSF, oI31b), cpsi, t_cpsi),
+ TUF(cpsid, 10c0000, b670, 2, (CPSF, oI31b), cpsi, t_cpsi),
+ tCE(rev, 6bf0f30, rev, 2, (RRnpc, RRnpc), rd_rm, t_rev),
+ tCE(rev16, 6bf0fb0, rev16, 2, (RRnpc, RRnpc), rd_rm, t_rev),
+ tCE(revsh, 6ff0fb0, revsh, 2, (RRnpc, RRnpc), rd_rm, t_rev),
+ tCE(sxth, 6bf0070, sxth, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ tCE(uxth, 6ff0070, uxth, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ tCE(sxtb, 6af0070, sxtb, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ tCE(uxtb, 6ef0070, uxtb, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ TUF(setend, 1010000, b650, 1, (ENDI), setend, t_setend),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TUF(cps, 1020000, f3af8100, 1, (I31b), imm0, imm0),
+ TCE(ldrex, 1900f9f, e8500f00, 2, (RRnpc, ADDR), ldrex, t_ldrex),
+ TUF(mcrr2, c400000, fc400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TUF(mrrc2, c500000, fc500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
+ TCE(pkhbt, 6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll), pkhbt, t_pkhbt),
+ TCE(pkhtb, 6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar), pkhtb, t_pkhtb),
+ TCE(qadd16, 6200f10, fa90f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(qadd8, 6200f90, fa80f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(qaddsubx, 6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(qsub16, 6200f70, fad0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(qsub8, 6200ff0, fac0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(qsubaddx, 6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(sadd16, 6100f10, fa90f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(sadd8, 6100f90, fa80f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(saddsubx, 6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(shadd16, 6300f10, fa90f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(shadd8, 6300f90, fa80f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(shaddsubx, 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(shsub16, 6300f70, fad0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(shsub8, 6300ff0, fac0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(shsubaddx, 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(ssub16, 6100f70, fad0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(ssub8, 6100ff0, fac0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(ssubaddx, 6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uadd16, 6500f10, fa90f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uadd8, 6500f90, fa80f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uaddsubx, 6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uhadd16, 6700f10, fa90f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uhadd8, 6700f90, fa80f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uhaddsubx, 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uhsub16, 6700f70, fad0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uhsub8, 6700ff0, fac0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uhsubaddx, 6700f50, fae0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uqadd16, 6600f10, fa90f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uqadd8, 6600f90, fa80f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uqaddsubx, 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uqsub16, 6600f70, fad0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uqsub8, 6600ff0, fac0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(uqsubaddx, 6600f50, fae0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(usub16, 6500f70, fad0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(usub8, 6500ff0, fac0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(usubaddx, 6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TUF(rfeia, 8900a00, e990c000, 1, (RRw), rfe, rfe),
+ UF(rfeib, 9900a00, 1, (RRw), rfe),
+ UF(rfeda, 8100a00, 1, (RRw), rfe),
+ TUF(rfedb, 9100a00, e810c000, 1, (RRw), rfe, rfe),
+ TUF(rfefd, 8900a00, e990c000, 1, (RRw), rfe, rfe),
+ UF(rfefa, 9900a00, 1, (RRw), rfe),
+ UF(rfeea, 8100a00, 1, (RRw), rfe),
+ TUF(rfeed, 9100a00, e810c000, 1, (RRw), rfe, rfe),
+ TCE(sxtah, 6b00070, fa00f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(sxtab16, 6800070, fa20f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(sxtab, 6a00070, fa40f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(sxtb16, 68f0070, fa2ff080, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ TCE(uxtah, 6f00070, fa10f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(uxtab16, 6c00070, fa30f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(uxtab, 6e00070, fa50f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
+ TCE(uxtb16, 6cf0070, fa3ff080, 3, (RRnpc, RRnpc, oROR), sxth, t_sxth),
+ TCE(sel, 68000b0, faa0f080, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE(smlad, 7000010, fb200000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smladx, 7000030, fb200010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smlald, 7400010, fbc000c0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smlaldx, 7400030, fbc000d0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smlsd, 7000050, fb400000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smlsdx, 7000070, fb400010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smlsld, 7400050, fbd000c0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smlsldx, 7400070, fbd000d0, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal,t_mlal),
+ TCE(smmla, 7500010, fb500000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmlar, 7500030, fb500010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmls, 75000d0, fb600000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmlsr, 75000f0, fb600010, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(smmul, 750f010, fb50f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smmulr, 750f030, fb50f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smuad, 700f010, fb20f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smuadx, 700f030, fb20f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smusd, 700f050, fb40f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(smusdx, 700f070, fb40f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TUF(srsia, 8cd0500, e980c000, 1, (I31w), srs, srs),
+ UF(srsib, 9cd0500, 1, (I31w), srs),
+ UF(srsda, 84d0500, 1, (I31w), srs),
+ TUF(srsdb, 94d0500, e800c000, 1, (I31w), srs, srs),
+ TCE(ssat, 6a00010, f3000000, 4, (RRnpc, I32, RRnpc, oSHllar),ssat, t_ssat),
+ TCE(ssat16, 6a00f30, f3200000, 3, (RRnpc, I16, RRnpc), ssat16, t_ssat16),
+ TCE(strex, 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex),
+ TCE(umaal, 0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal, t_mlal),
+ TCE(usad8, 780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
+ TCE(usada8, 7800010, fb700000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
+ TCE(usat, 6e00010, f3800000, 4, (RRnpc, I31, RRnpc, oSHllar),usat, t_usat),
+ TCE(usat16, 6e00f30, f3a00000, 3, (RRnpc, I15, RRnpc), usat16, t_usat16),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6K
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6K
+ tCE(yield, 320f001, yield, 0, (), noargs, t_hint),
+ tCE(wfe, 320f002, wfe, 0, (), noargs, t_hint),
+ tCE(wfi, 320f003, wfi, 0, (), noargs, t_hint),
+ tCE(sev, 320f004, sev, 0, (), noargs, t_hint),
+
+#undef THUMB_VARIANT
+#define THUMB_VARIANT ARM_EXT_V6T2
+ TCE(ldrexb, 1d00f9f, e8d00f4f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE(ldrexh, 1f00f9f, e8d00f5f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE(ldrexd, 1b00f9f, e8d0007f, 3, (RRnpc, oRRnpc, RRnpcb), ldrexd, t_ldrexd),
+ TCE(strexb, 1c00f90, e8c00f40, 3, (RRnpc, RRnpc, ADDR), strex, rm_rd_rn),
+ TCE(strexh, 1e00f90, e8c00f50, 3, (RRnpc, RRnpc, ADDR), strex, rm_rd_rn),
+ TCE(strexd, 1a00f90, e8c00070, 4, (RRnpc, RRnpc, oRRnpc, RRnpcb), strexd, t_strexd),
+ TUF(clrex, 57ff01f, f3bf8f2f, 0, (), noargs, noargs),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6Z
+ TCE(smi, 1600070, f7f08000, 1, (EXPi), smi, t_smi),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_EXT_V6T2
+ TCE(bfc, 7c0001f, f36f0000, 3, (RRnpc, I31, I32), bfc, t_bfc),
+ TCE(bfi, 7c00010, f3600000, 4, (RRnpc, RRnpc_I0, I31, I32), bfi, t_bfi),
+ TCE(sbfx, 7a00050, f3400000, 4, (RR, RR, I31, I32), bfx, t_bfx),
+ TCE(ubfx, 7e00050, f3c00000, 4, (RR, RR, I31, I32), bfx, t_bfx),
+
+ TCE(mls, 0600090, fb000010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
+ TCE(movw, 3000000, f2400000, 2, (RRnpc, Iffff), mov16, t_mov16),
+ TCE(movt, 3400000, f2c00000, 2, (RRnpc, Iffff), mov16, t_mov16),
+ TCE(rbit, 3ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit),
+
+ TC3(ldrht, 03000b0, f8300e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+ TC3(ldrsht, 03000f0, f9300e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+ TC3(ldrsbt, 03000d0, f9100e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+ TC3(strht, 02000b0, f8200e00, 2, (RR, ADDR), ldsttv4, t_ldstt),
+
+ UT(cbnz, b900, 2, (RR, EXP), t_czb),
+ UT(cbz, b100, 2, (RR, EXP), t_czb),
+ /* ARM does not really have an IT instruction. */
+ TUE(it, 0, bf08, 1, (COND), it, t_it),
+ TUE(itt, 0, bf0c, 1, (COND), it, t_it),
+ TUE(ite, 0, bf04, 1, (COND), it, t_it),
+ TUE(ittt, 0, bf0e, 1, (COND), it, t_it),
+ TUE(itet, 0, bf06, 1, (COND), it, t_it),
+ TUE(itte, 0, bf0a, 1, (COND), it, t_it),
+ TUE(itee, 0, bf02, 1, (COND), it, t_it),
+ TUE(itttt, 0, bf0f, 1, (COND), it, t_it),
+ TUE(itett, 0, bf07, 1, (COND), it, t_it),
+ TUE(ittet, 0, bf0b, 1, (COND), it, t_it),
+ TUE(iteet, 0, bf03, 1, (COND), it, t_it),
+ TUE(ittte, 0, bf0d, 1, (COND), it, t_it),
+ TUE(itete, 0, bf05, 1, (COND), it, t_it),
+ TUE(ittee, 0, bf09, 1, (COND), it, t_it),
+ TUE(iteee, 0, bf01, 1, (COND), it, t_it),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_FPA_EXT_V1 /* Core FPA instruction set (V1). */
+ CE(wfs, e200110, 1, (RR), rd),
+ CE(rfs, e300110, 1, (RR), rd),
+ CE(wfc, e400110, 1, (RR), rd),
+ CE(rfc, e500110, 1, (RR), rd),
+
+ C3(ldfs, c100100, 2, (RF, ADDR), rd_cpaddr),
+ C3(ldfd, c108100, 2, (RF, ADDR), rd_cpaddr),
+ C3(ldfe, c500100, 2, (RF, ADDR), rd_cpaddr),
+ C3(ldfp, c508100, 2, (RF, ADDR), rd_cpaddr),
+
+ C3(stfs, c000100, 2, (RF, ADDR), rd_cpaddr),
+ C3(stfd, c008100, 2, (RF, ADDR), rd_cpaddr),
+ C3(stfe, c400100, 2, (RF, ADDR), rd_cpaddr),
+ C3(stfp, c408100, 2, (RF, ADDR), rd_cpaddr),
+
+ C3(mvfs, e008100, 2, (RF, RF_IF), rd_rm),
+ C3(mvfsp, e008120, 2, (RF, RF_IF), rd_rm),
+ C3(mvfsm, e008140, 2, (RF, RF_IF), rd_rm),
+ C3(mvfsz, e008160, 2, (RF, RF_IF), rd_rm),
+ C3(mvfd, e008180, 2, (RF, RF_IF), rd_rm),
+ C3(mvfdp, e0081a0, 2, (RF, RF_IF), rd_rm),
+ C3(mvfdm, e0081c0, 2, (RF, RF_IF), rd_rm),
+ C3(mvfdz, e0081e0, 2, (RF, RF_IF), rd_rm),
+ C3(mvfe, e088100, 2, (RF, RF_IF), rd_rm),
+ C3(mvfep, e088120, 2, (RF, RF_IF), rd_rm),
+ C3(mvfem, e088140, 2, (RF, RF_IF), rd_rm),
+ C3(mvfez, e088160, 2, (RF, RF_IF), rd_rm),
+
+ C3(mnfs, e108100, 2, (RF, RF_IF), rd_rm),
+ C3(mnfsp, e108120, 2, (RF, RF_IF), rd_rm),
+ C3(mnfsm, e108140, 2, (RF, RF_IF), rd_rm),
+ C3(mnfsz, e108160, 2, (RF, RF_IF), rd_rm),
+ C3(mnfd, e108180, 2, (RF, RF_IF), rd_rm),
+ C3(mnfdp, e1081a0, 2, (RF, RF_IF), rd_rm),
+ C3(mnfdm, e1081c0, 2, (RF, RF_IF), rd_rm),
+ C3(mnfdz, e1081e0, 2, (RF, RF_IF), rd_rm),
+ C3(mnfe, e188100, 2, (RF, RF_IF), rd_rm),
+ C3(mnfep, e188120, 2, (RF, RF_IF), rd_rm),
+ C3(mnfem, e188140, 2, (RF, RF_IF), rd_rm),
+ C3(mnfez, e188160, 2, (RF, RF_IF), rd_rm),
+
+ C3(abss, e208100, 2, (RF, RF_IF), rd_rm),
+ C3(abssp, e208120, 2, (RF, RF_IF), rd_rm),
+ C3(abssm, e208140, 2, (RF, RF_IF), rd_rm),
+ C3(abssz, e208160, 2, (RF, RF_IF), rd_rm),
+ C3(absd, e208180, 2, (RF, RF_IF), rd_rm),
+ C3(absdp, e2081a0, 2, (RF, RF_IF), rd_rm),
+ C3(absdm, e2081c0, 2, (RF, RF_IF), rd_rm),
+ C3(absdz, e2081e0, 2, (RF, RF_IF), rd_rm),
+ C3(abse, e288100, 2, (RF, RF_IF), rd_rm),
+ C3(absep, e288120, 2, (RF, RF_IF), rd_rm),
+ C3(absem, e288140, 2, (RF, RF_IF), rd_rm),
+ C3(absez, e288160, 2, (RF, RF_IF), rd_rm),
+
+ C3(rnds, e308100, 2, (RF, RF_IF), rd_rm),
+ C3(rndsp, e308120, 2, (RF, RF_IF), rd_rm),
+ C3(rndsm, e308140, 2, (RF, RF_IF), rd_rm),
+ C3(rndsz, e308160, 2, (RF, RF_IF), rd_rm),
+ C3(rndd, e308180, 2, (RF, RF_IF), rd_rm),
+ C3(rnddp, e3081a0, 2, (RF, RF_IF), rd_rm),
+ C3(rnddm, e3081c0, 2, (RF, RF_IF), rd_rm),
+ C3(rnddz, e3081e0, 2, (RF, RF_IF), rd_rm),
+ C3(rnde, e388100, 2, (RF, RF_IF), rd_rm),
+ C3(rndep, e388120, 2, (RF, RF_IF), rd_rm),
+ C3(rndem, e388140, 2, (RF, RF_IF), rd_rm),
+ C3(rndez, e388160, 2, (RF, RF_IF), rd_rm),
+
+ C3(sqts, e408100, 2, (RF, RF_IF), rd_rm),
+ C3(sqtsp, e408120, 2, (RF, RF_IF), rd_rm),
+ C3(sqtsm, e408140, 2, (RF, RF_IF), rd_rm),
+ C3(sqtsz, e408160, 2, (RF, RF_IF), rd_rm),
+ C3(sqtd, e408180, 2, (RF, RF_IF), rd_rm),
+ C3(sqtdp, e4081a0, 2, (RF, RF_IF), rd_rm),
+ C3(sqtdm, e4081c0, 2, (RF, RF_IF), rd_rm),
+ C3(sqtdz, e4081e0, 2, (RF, RF_IF), rd_rm),
+ C3(sqte, e488100, 2, (RF, RF_IF), rd_rm),
+ C3(sqtep, e488120, 2, (RF, RF_IF), rd_rm),
+ C3(sqtem, e488140, 2, (RF, RF_IF), rd_rm),
+ C3(sqtez, e488160, 2, (RF, RF_IF), rd_rm),
+
+ C3(logs, e508100, 2, (RF, RF_IF), rd_rm),
+ C3(logsp, e508120, 2, (RF, RF_IF), rd_rm),
+ C3(logsm, e508140, 2, (RF, RF_IF), rd_rm),
+ C3(logsz, e508160, 2, (RF, RF_IF), rd_rm),
+ C3(logd, e508180, 2, (RF, RF_IF), rd_rm),
+ C3(logdp, e5081a0, 2, (RF, RF_IF), rd_rm),
+ C3(logdm, e5081c0, 2, (RF, RF_IF), rd_rm),
+ C3(logdz, e5081e0, 2, (RF, RF_IF), rd_rm),
+ C3(loge, e588100, 2, (RF, RF_IF), rd_rm),
+ C3(logep, e588120, 2, (RF, RF_IF), rd_rm),
+ C3(logem, e588140, 2, (RF, RF_IF), rd_rm),
+ C3(logez, e588160, 2, (RF, RF_IF), rd_rm),
+
+ C3(lgns, e608100, 2, (RF, RF_IF), rd_rm),
+ C3(lgnsp, e608120, 2, (RF, RF_IF), rd_rm),
+ C3(lgnsm, e608140, 2, (RF, RF_IF), rd_rm),
+ C3(lgnsz, e608160, 2, (RF, RF_IF), rd_rm),
+ C3(lgnd, e608180, 2, (RF, RF_IF), rd_rm),
+ C3(lgndp, e6081a0, 2, (RF, RF_IF), rd_rm),
+ C3(lgndm, e6081c0, 2, (RF, RF_IF), rd_rm),
+ C3(lgndz, e6081e0, 2, (RF, RF_IF), rd_rm),
+ C3(lgne, e688100, 2, (RF, RF_IF), rd_rm),
+ C3(lgnep, e688120, 2, (RF, RF_IF), rd_rm),
+ C3(lgnem, e688140, 2, (RF, RF_IF), rd_rm),
+ C3(lgnez, e688160, 2, (RF, RF_IF), rd_rm),
+
+ C3(exps, e708100, 2, (RF, RF_IF), rd_rm),
+ C3(expsp, e708120, 2, (RF, RF_IF), rd_rm),
+ C3(expsm, e708140, 2, (RF, RF_IF), rd_rm),
+ C3(expsz, e708160, 2, (RF, RF_IF), rd_rm),
+ C3(expd, e708180, 2, (RF, RF_IF), rd_rm),
+ C3(expdp, e7081a0, 2, (RF, RF_IF), rd_rm),
+ C3(expdm, e7081c0, 2, (RF, RF_IF), rd_rm),
+ C3(expdz, e7081e0, 2, (RF, RF_IF), rd_rm),
+ C3(expe, e788100, 2, (RF, RF_IF), rd_rm),
+ C3(expep, e788120, 2, (RF, RF_IF), rd_rm),
+ C3(expem, e788140, 2, (RF, RF_IF), rd_rm),
+ C3(expdz, e788160, 2, (RF, RF_IF), rd_rm),
+
+ C3(sins, e808100, 2, (RF, RF_IF), rd_rm),
+ C3(sinsp, e808120, 2, (RF, RF_IF), rd_rm),
+ C3(sinsm, e808140, 2, (RF, RF_IF), rd_rm),
+ C3(sinsz, e808160, 2, (RF, RF_IF), rd_rm),
+ C3(sind, e808180, 2, (RF, RF_IF), rd_rm),
+ C3(sindp, e8081a0, 2, (RF, RF_IF), rd_rm),
+ C3(sindm, e8081c0, 2, (RF, RF_IF), rd_rm),
+ C3(sindz, e8081e0, 2, (RF, RF_IF), rd_rm),
+ C3(sine, e888100, 2, (RF, RF_IF), rd_rm),
+ C3(sinep, e888120, 2, (RF, RF_IF), rd_rm),
+ C3(sinem, e888140, 2, (RF, RF_IF), rd_rm),
+ C3(sinez, e888160, 2, (RF, RF_IF), rd_rm),
+
+ C3(coss, e908100, 2, (RF, RF_IF), rd_rm),
+ C3(cossp, e908120, 2, (RF, RF_IF), rd_rm),
+ C3(cossm, e908140, 2, (RF, RF_IF), rd_rm),
+ C3(cossz, e908160, 2, (RF, RF_IF), rd_rm),
+ C3(cosd, e908180, 2, (RF, RF_IF), rd_rm),
+ C3(cosdp, e9081a0, 2, (RF, RF_IF), rd_rm),
+ C3(cosdm, e9081c0, 2, (RF, RF_IF), rd_rm),
+ C3(cosdz, e9081e0, 2, (RF, RF_IF), rd_rm),
+ C3(cose, e988100, 2, (RF, RF_IF), rd_rm),
+ C3(cosep, e988120, 2, (RF, RF_IF), rd_rm),
+ C3(cosem, e988140, 2, (RF, RF_IF), rd_rm),
+ C3(cosez, e988160, 2, (RF, RF_IF), rd_rm),
+
+ C3(tans, ea08100, 2, (RF, RF_IF), rd_rm),
+ C3(tansp, ea08120, 2, (RF, RF_IF), rd_rm),
+ C3(tansm, ea08140, 2, (RF, RF_IF), rd_rm),
+ C3(tansz, ea08160, 2, (RF, RF_IF), rd_rm),
+ C3(tand, ea08180, 2, (RF, RF_IF), rd_rm),
+ C3(tandp, ea081a0, 2, (RF, RF_IF), rd_rm),
+ C3(tandm, ea081c0, 2, (RF, RF_IF), rd_rm),
+ C3(tandz, ea081e0, 2, (RF, RF_IF), rd_rm),
+ C3(tane, ea88100, 2, (RF, RF_IF), rd_rm),
+ C3(tanep, ea88120, 2, (RF, RF_IF), rd_rm),
+ C3(tanem, ea88140, 2, (RF, RF_IF), rd_rm),
+ C3(tanez, ea88160, 2, (RF, RF_IF), rd_rm),
+
+ C3(asns, eb08100, 2, (RF, RF_IF), rd_rm),
+ C3(asnsp, eb08120, 2, (RF, RF_IF), rd_rm),
+ C3(asnsm, eb08140, 2, (RF, RF_IF), rd_rm),
+ C3(asnsz, eb08160, 2, (RF, RF_IF), rd_rm),
+ C3(asnd, eb08180, 2, (RF, RF_IF), rd_rm),
+ C3(asndp, eb081a0, 2, (RF, RF_IF), rd_rm),
+ C3(asndm, eb081c0, 2, (RF, RF_IF), rd_rm),
+ C3(asndz, eb081e0, 2, (RF, RF_IF), rd_rm),
+ C3(asne, eb88100, 2, (RF, RF_IF), rd_rm),
+ C3(asnep, eb88120, 2, (RF, RF_IF), rd_rm),
+ C3(asnem, eb88140, 2, (RF, RF_IF), rd_rm),
+ C3(asnez, eb88160, 2, (RF, RF_IF), rd_rm),
+
+ C3(acss, ec08100, 2, (RF, RF_IF), rd_rm),
+ C3(acssp, ec08120, 2, (RF, RF_IF), rd_rm),
+ C3(acssm, ec08140, 2, (RF, RF_IF), rd_rm),
+ C3(acssz, ec08160, 2, (RF, RF_IF), rd_rm),
+ C3(acsd, ec08180, 2, (RF, RF_IF), rd_rm),
+ C3(acsdp, ec081a0, 2, (RF, RF_IF), rd_rm),
+ C3(acsdm, ec081c0, 2, (RF, RF_IF), rd_rm),
+ C3(acsdz, ec081e0, 2, (RF, RF_IF), rd_rm),
+ C3(acse, ec88100, 2, (RF, RF_IF), rd_rm),
+ C3(acsep, ec88120, 2, (RF, RF_IF), rd_rm),
+ C3(acsem, ec88140, 2, (RF, RF_IF), rd_rm),
+ C3(acsez, ec88160, 2, (RF, RF_IF), rd_rm),
+
+ C3(atns, ed08100, 2, (RF, RF_IF), rd_rm),
+ C3(atnsp, ed08120, 2, (RF, RF_IF), rd_rm),
+ C3(atnsm, ed08140, 2, (RF, RF_IF), rd_rm),
+ C3(atnsz, ed08160, 2, (RF, RF_IF), rd_rm),
+ C3(atnd, ed08180, 2, (RF, RF_IF), rd_rm),
+ C3(atndp, ed081a0, 2, (RF, RF_IF), rd_rm),
+ C3(atndm, ed081c0, 2, (RF, RF_IF), rd_rm),
+ C3(atndz, ed081e0, 2, (RF, RF_IF), rd_rm),
+ C3(atne, ed88100, 2, (RF, RF_IF), rd_rm),
+ C3(atnep, ed88120, 2, (RF, RF_IF), rd_rm),
+ C3(atnem, ed88140, 2, (RF, RF_IF), rd_rm),
+ C3(atnez, ed88160, 2, (RF, RF_IF), rd_rm),
+
+ C3(urds, ee08100, 2, (RF, RF_IF), rd_rm),
+ C3(urdsp, ee08120, 2, (RF, RF_IF), rd_rm),
+ C3(urdsm, ee08140, 2, (RF, RF_IF), rd_rm),
+ C3(urdsz, ee08160, 2, (RF, RF_IF), rd_rm),
+ C3(urdd, ee08180, 2, (RF, RF_IF), rd_rm),
+ C3(urddp, ee081a0, 2, (RF, RF_IF), rd_rm),
+ C3(urddm, ee081c0, 2, (RF, RF_IF), rd_rm),
+ C3(urddz, ee081e0, 2, (RF, RF_IF), rd_rm),
+ C3(urde, ee88100, 2, (RF, RF_IF), rd_rm),
+ C3(urdep, ee88120, 2, (RF, RF_IF), rd_rm),
+ C3(urdem, ee88140, 2, (RF, RF_IF), rd_rm),
+ C3(urdez, ee88160, 2, (RF, RF_IF), rd_rm),
+
+ C3(nrms, ef08100, 2, (RF, RF_IF), rd_rm),
+ C3(nrmsp, ef08120, 2, (RF, RF_IF), rd_rm),
+ C3(nrmsm, ef08140, 2, (RF, RF_IF), rd_rm),
+ C3(nrmsz, ef08160, 2, (RF, RF_IF), rd_rm),
+ C3(nrmd, ef08180, 2, (RF, RF_IF), rd_rm),
+ C3(nrmdp, ef081a0, 2, (RF, RF_IF), rd_rm),
+ C3(nrmdm, ef081c0, 2, (RF, RF_IF), rd_rm),
+ C3(nrmdz, ef081e0, 2, (RF, RF_IF), rd_rm),
+ C3(nrme, ef88100, 2, (RF, RF_IF), rd_rm),
+ C3(nrmep, ef88120, 2, (RF, RF_IF), rd_rm),
+ C3(nrmem, ef88140, 2, (RF, RF_IF), rd_rm),
+ C3(nrmez, ef88160, 2, (RF, RF_IF), rd_rm),
+
+ C3(adfs, e000100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfsp, e000120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfsm, e000140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfsz, e000160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfd, e000180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfdp, e0001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfdm, e0001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfdz, e0001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfe, e080100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfep, e080120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfem, e080140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(adfez, e080160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(sufs, e200100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufsp, e200120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufsm, e200140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufsz, e200160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufd, e200180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufdp, e2001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufdm, e2001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufdz, e2001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufe, e280100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufep, e280120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufem, e280140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(sufez, e280160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(rsfs, e300100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfsp, e300120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfsm, e300140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfsz, e300160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfd, e300180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfdp, e3001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfdm, e3001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfdz, e3001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfe, e380100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfep, e380120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfem, e380140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rsfez, e380160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(mufs, e100100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufsp, e100120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufsm, e100140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufsz, e100160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufd, e100180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufdp, e1001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufdm, e1001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufdz, e1001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufe, e180100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufep, e180120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufem, e180140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(mufez, e180160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(dvfs, e400100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfsp, e400120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfsm, e400140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfsz, e400160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfd, e400180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfdp, e4001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfdm, e4001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfdz, e4001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfe, e480100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfep, e480120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfem, e480140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(dvfez, e480160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(rdfs, e500100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfsp, e500120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfsm, e500140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfsz, e500160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfd, e500180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfdp, e5001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfdm, e5001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfdz, e5001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfe, e580100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfep, e580120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfem, e580140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rdfez, e580160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(pows, e600100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powsp, e600120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powsm, e600140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powsz, e600160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powd, e600180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powdp, e6001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powdm, e6001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powdz, e6001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powe, e680100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powep, e680120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powem, e680140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(powez, e680160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(rpws, e700100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwsp, e700120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwsm, e700140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwsz, e700160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwd, e700180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwdp, e7001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwdm, e7001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwdz, e7001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwe, e780100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwep, e780120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwem, e780140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rpwez, e780160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(rmfs, e800100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfsp, e800120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfsm, e800140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfsz, e800160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfd, e800180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfdp, e8001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfdm, e8001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfdz, e8001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfe, e880100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfep, e880120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfem, e880140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(rmfez, e880160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(fmls, e900100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmlsp, e900120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmlsm, e900140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmlsz, e900160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmld, e900180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmldp, e9001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmldm, e9001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmldz, e9001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmle, e980100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmlep, e980120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmlem, e980140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fmlez, e980160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(fdvs, ea00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvsp, ea00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvsm, ea00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvsz, ea00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvd, ea00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvdp, ea001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvdm, ea001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvdz, ea001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdve, ea80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvep, ea80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvem, ea80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(fdvez, ea80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(frds, eb00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdsp, eb00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdsm, eb00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdsz, eb00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdd, eb00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frddp, eb001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frddm, eb001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frddz, eb001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frde, eb80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdep, eb80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdem, eb80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(frdez, eb80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ C3(pols, ec00100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(polsp, ec00120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(polsm, ec00140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(polsz, ec00160, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(pold, ec00180, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(poldp, ec001a0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(poldm, ec001c0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(poldz, ec001e0, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(pole, ec80100, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(polep, ec80120, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(polem, ec80140, 3, (RF, RF, RF_IF), rd_rn_rm),
+ C3(polez, ec80160, 3, (RF, RF, RF_IF), rd_rn_rm),
+
+ CE(cmf, e90f110, 2, (RF, RF_IF), fpa_cmp),
+ C3E(cmfe, ed0f110, 2, (RF, RF_IF), fpa_cmp),
+ CE(cnf, eb0f110, 2, (RF, RF_IF), fpa_cmp),
+ C3E(cnfe, ef0f110, 2, (RF, RF_IF), fpa_cmp),
+
+ C3(flts, e000110, 2, (RF, RR), rn_rd),
+ C3(fltsp, e000130, 2, (RF, RR), rn_rd),
+ C3(fltsm, e000150, 2, (RF, RR), rn_rd),
+ C3(fltsz, e000170, 2, (RF, RR), rn_rd),
+ C3(fltd, e000190, 2, (RF, RR), rn_rd),
+ C3(fltdp, e0001b0, 2, (RF, RR), rn_rd),
+ C3(fltdm, e0001d0, 2, (RF, RR), rn_rd),
+ C3(fltdz, e0001f0, 2, (RF, RR), rn_rd),
+ C3(flte, e080110, 2, (RF, RR), rn_rd),
+ C3(fltep, e080130, 2, (RF, RR), rn_rd),
+ C3(fltem, e080150, 2, (RF, RR), rn_rd),
+ C3(fltez, e080170, 2, (RF, RR), rn_rd),
- if ((reg = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
+ /* The implementation of the FIX instruction is broken on some
+ assemblers, in that it accepts a precision specifier as well as a
+ rounding specifier, despite the fact that this is meaningless.
+ To be more compatible, we accept it as well, though of course it
+ does not set any bits. */
+ CE(fix, e100110, 2, (RR, RF), rd_rm),
+ C3(fixp, e100130, 2, (RR, RF), rd_rm),
+ C3(fixm, e100150, 2, (RR, RF), rd_rm),
+ C3(fixz, e100170, 2, (RR, RF), rd_rm),
+ C3(fixsp, e100130, 2, (RR, RF), rd_rm),
+ C3(fixsm, e100150, 2, (RR, RF), rd_rm),
+ C3(fixsz, e100170, 2, (RR, RF), rd_rm),
+ C3(fixdp, e100130, 2, (RR, RF), rd_rm),
+ C3(fixdm, e100150, 2, (RR, RF), rd_rm),
+ C3(fixdz, e100170, 2, (RR, RF), rd_rm),
+ C3(fixep, e100130, 2, (RR, RF), rd_rm),
+ C3(fixem, e100150, 2, (RR, RF), rd_rm),
+ C3(fixez, e100170, 2, (RR, RF), rd_rm),
- /* This sets THUMB_H2 from the top bit of reg. */
- inst.instruction |= reg << 3;
+ /* Instructions that were new with the real FPA, call them V2. */
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_FPA_EXT_V2
+ CE(lfm, c100200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ C3(lfmfd, c900200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ C3(lfmea, d100200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ CE(sfm, c000200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ C3(sfmfd, d000200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+ C3(sfmea, c800200, 3, (RF, I4b, ADDR), fpa_ldmstm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_VFP_EXT_V1xD /* VFP V1xD (single precision). */
+ /* Moves and type conversions. */
+ CE(fcpys, eb00a40, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fmrs, e100a10, 2, (RR, RVS), vfp_reg_from_sp),
+ CE(fmsr, e000a10, 2, (RVS, RR), vfp_sp_from_reg),
+ CE(fmstat, ef1fa10, 0, (), noargs),
+ CE(fsitos, eb80ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fuitos, eb80a40, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(ftosis, ebd0a40, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(ftosizs, ebd0ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(ftouis, ebc0a40, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(ftouizs, ebc0ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fmrx, ef00a10, 2, (RR, RVC), rd_rn),
+ CE(fmxr, ee00a10, 2, (RVC, RR), rn_rd),
+
+ /* Memory operations. */
+ CE(flds, d100a00, 2, (RVS, ADDR), vfp_sp_ldst),
+ CE(fsts, d000a00, 2, (RVS, ADDR), vfp_sp_ldst),
+ CE(fldmias, c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia),
+ CE(fldmfds, c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia),
+ CE(fldmdbs, d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb),
+ CE(fldmeas, d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb),
+ CE(fldmiax, c900b00, 2, (RRw, VRDLST), vfp_xp_ldstmia),
+ CE(fldmfdx, c900b00, 2, (RRw, VRDLST), vfp_xp_ldstmia),
+ CE(fldmdbx, d300b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb),
+ CE(fldmeax, d300b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb),
+ CE(fstmias, c800a00, 2, (RRw, VRSLST), vfp_sp_ldstmia),
+ CE(fstmeas, c800a00, 2, (RRw, VRSLST), vfp_sp_ldstmia),
+ CE(fstmdbs, d200a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb),
+ CE(fstmfds, d200a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb),
+ CE(fstmiax, c800b00, 2, (RRw, VRDLST), vfp_xp_ldstmia),
+ CE(fstmeax, c800b00, 2, (RRw, VRDLST), vfp_xp_ldstmia),
+ CE(fstmdbx, d200b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb),
+ CE(fstmfdx, d200b00, 2, (RRw, VRDLST), vfp_xp_ldstmdb),
- /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
- should cause the alignment to be checked once it is known. This is
- because BX PC only works if the instruction is word aligned. */
+ /* Monadic operations. */
+ CE(fabss, eb00ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fnegs, eb10a40, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fsqrts, eb10ac0, 2, (RVS, RVS), vfp_sp_monadic),
+
+ /* Dyadic operations. */
+ CE(fadds, e300a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fsubs, e300a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fmuls, e200a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fdivs, e800a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fmacs, e000a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fmscs, e100a00, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fnmuls, e200a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fnmacs, e000a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
+ CE(fnmscs, e100a40, 3, (RVS, RVS, RVS), vfp_sp_dyadic),
- end_of_line (str);
-}
+ /* Comparisons. */
+ CE(fcmps, eb40a40, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fcmpzs, eb50a40, 1, (RVS), vfp_sp_compare_z),
+ CE(fcmpes, eb40ac0, 2, (RVS, RVS), vfp_sp_monadic),
+ CE(fcmpezs, eb50ac0, 1, (RVS), vfp_sp_compare_z),
-static void
-do_t_compare (char * str)
-{
- thumb_mov_compare (str, THUMB_COMPARE);
-}
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_VFP_EXT_V1 /* VFP V1 (Double precision). */
+ /* Moves and type conversions. */
+ CE(fcpyd, eb00b40, 2, (RVD, RVD), rd_rm),
+ CE(fcvtds, eb70ac0, 2, (RVD, RVS), vfp_dp_sp_cvt),
+ CE(fcvtsd, eb70bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ CE(fmdhr, e200b10, 2, (RVD, RR), rn_rd),
+ CE(fmdlr, e000b10, 2, (RVD, RR), rn_rd),
+ CE(fmrdh, e300b10, 2, (RR, RVD), rd_rn),
+ CE(fmrdl, e100b10, 2, (RR, RVD), rd_rn),
+ CE(fsitod, eb80bc0, 2, (RVD, RVS), vfp_dp_sp_cvt),
+ CE(fuitod, eb80b40, 2, (RVD, RVS), vfp_dp_sp_cvt),
+ CE(ftosid, ebd0b40, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ CE(ftosizd, ebd0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ CE(ftouid, ebc0b40, 2, (RVS, RVD), vfp_sp_dp_cvt),
+ CE(ftouizd, ebc0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
+
+ /* Memory operations. */
+ CE(fldd, d100b00, 2, (RVD, ADDR), vfp_dp_ldst),
+ CE(fstd, d000b00, 2, (RVD, ADDR), vfp_dp_ldst),
+ CE(fldmiad, c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia),
+ CE(fldmfdd, c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia),
+ CE(fldmdbd, d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb),
+ CE(fldmead, d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb),
+ CE(fstmiad, c800b00, 2, (RRw, VRDLST), vfp_dp_ldstmia),
+ CE(fstmead, c800b00, 2, (RRw, VRDLST), vfp_dp_ldstmia),
+ CE(fstmdbd, d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb),
+ CE(fstmfdd, d200b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb),
-static void
-do_t_ldmstm (char * str)
-{
- int Rb;
- long range;
+ /* Monadic operations. */
+ CE(fabsd, eb00bc0, 2, (RVD, RVD), rd_rm),
+ CE(fnegd, eb10b40, 2, (RVD, RVD), rd_rm),
+ CE(fsqrtd, eb10bc0, 2, (RVD, RVD), rd_rm),
+
+ /* Dyadic operations. */
+ CE(faddd, e300b00, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fsubd, e300b40, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fmuld, e200b00, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fdivd, e800b00, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fmacd, e000b00, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fmscd, e100b00, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fnmuld, e200b40, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fnmacd, e000b40, 3, (RVD, RVD, RVD), rd_rn_rm),
+ CE(fnmscd, e100b40, 3, (RVD, RVD, RVD), rd_rn_rm),
- skip_whitespace (str);
+ /* Comparisons. */
+ CE(fcmpd, eb40b40, 2, (RVD, RVD), rd_rm),
+ CE(fcmpzd, eb50b40, 1, (RVD), rd),
+ CE(fcmped, eb40bc0, 2, (RVD, RVD), rd_rm),
+ CE(fcmpezd, eb50bc0, 1, (RVD), rd),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT FPU_VFP_EXT_V2
+ CE(fmsrr, c400a10, 3, (VRSLST, RR, RR), vfp_sp2_from_reg2),
+ CE(fmrrs, c500a10, 3, (RR, RR, VRSLST), vfp_reg2_from_sp2),
+ CE(fmdrr, c400b10, 3, (RVD, RR, RR), rm_rd_rn),
+ CE(fmrrd, c500b10, 3, (RR, RR, RVD), rd_rn_rm),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_CEXT_XSCALE /* Intel XScale extensions. */
+ CE(mia, e200010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ CE(miaph, e280010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ CE(miabb, e2c0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ CE(miabt, e2d0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ CE(miatb, e2e0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ CE(miatt, e2f0010, 3, (RXA, RRnpc, RRnpc), xsc_mia),
+ CE(mar, c400000, 3, (RXA, RRnpc, RRnpc), xsc_mar),
+ CE(mra, c500000, 3, (RRnpc, RRnpc, RXA), xsc_mra),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_CEXT_IWMMXT /* Intel Wireless MMX technology. */
+ CE(tandcb, e13f130, 1, (RR), iwmmxt_tandorc),
+ CE(tandch, e53f130, 1, (RR), iwmmxt_tandorc),
+ CE(tandcw, e93f130, 1, (RR), iwmmxt_tandorc),
+ CE(tbcstb, e400010, 2, (RIWR, RR), rn_rd),
+ CE(tbcsth, e400050, 2, (RIWR, RR), rn_rd),
+ CE(tbcstw, e400090, 2, (RIWR, RR), rn_rd),
+ CE(textrcb, e130170, 2, (RR, I7), iwmmxt_textrc),
+ CE(textrch, e530170, 2, (RR, I7), iwmmxt_textrc),
+ CE(textrcw, e930170, 2, (RR, I7), iwmmxt_textrc),
+ CE(textrmub, e100070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ CE(textrmuh, e500070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ CE(textrmuw, e900070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ CE(textrmsb, e100078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ CE(textrmsh, e500078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ CE(textrmsw, e900078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ CE(tinsrb, e600010, 3, (RIWR, RR, I7), iwmmxt_tinsr),
+ CE(tinsrh, e600050, 3, (RIWR, RR, I7), iwmmxt_tinsr),
+ CE(tinsrw, e600090, 3, (RIWR, RR, I7), iwmmxt_tinsr),
+ CE(tmcr, e000110, 2, (RIWC, RR), rn_rd),
+ CE(tmcrr, c400000, 3, (RIWR, RR, RR), rm_rd_rn),
+ CE(tmia, e200010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ CE(tmiaph, e280010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ CE(tmiabb, e2c0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ CE(tmiabt, e2d0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ CE(tmiatb, e2e0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ CE(tmiatt, e2f0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
+ CE(tmovmskb, e100030, 2, (RR, RIWR), rd_rn),
+ CE(tmovmskh, e500030, 2, (RR, RIWR), rd_rn),
+ CE(tmovmskw, e900030, 2, (RR, RIWR), rd_rn),
+ CE(tmrc, e100110, 2, (RR, RIWC), rd_rn),
+ CE(tmrrc, c500000, 3, (RR, RR, RIWR), rd_rn_rm),
+ CE(torcb, e13f150, 1, (RR), iwmmxt_tandorc),
+ CE(torch, e53f150, 1, (RR), iwmmxt_tandorc),
+ CE(torcw, e93f150, 1, (RR), iwmmxt_tandorc),
+ CE(waccb, e0001c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wacch, e4001c0, 2, (RIWR, RIWR), rd_rn),
+ CE(waccw, e8001c0, 2, (RIWR, RIWR), rd_rn),
+ CE(waddbss, e300180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddb, e000180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddbus, e100180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddhss, e700180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddh, e400180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddhus, e500180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddwss, eb00180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddw, e800180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waddwus, e900180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(waligni, e000020, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_waligni),
+ CE(walignr0, e800020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(walignr1, e900020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(walignr2, ea00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(walignr3, eb00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wand, e200000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wandn, e300000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wavg2b, e800000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wavg2br, e900000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wavg2h, ec00000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wavg2hr, ed00000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpeqb, e000060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpeqh, e400060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpeqw, e800060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpgtub, e100060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpgtuh, e500060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpgtuw, e900060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpgtsb, e300060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpgtsh, e700060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wcmpgtsw, eb00060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wldrb, c100000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ CE(wldrh, c500000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ CE(wldrw, c100100, 2, (RIWR_RIWC, ADDR), iwmmxt_wldstw),
+ CE(wldrd, c500100, 2, (RIWR, ADDR), iwmmxt_wldstd),
+ CE(wmacs, e600100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmacsz, e700100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmacu, e400100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmacuz, e500100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmadds, ea00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaddu, e800100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaxsb, e200160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaxsh, e600160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaxsw, ea00160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaxub, e000160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaxuh, e400160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmaxuw, e800160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wminsb, e300160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wminsh, e700160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wminsw, eb00160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wminub, e100160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wminuh, e500160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wminuw, e900160, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmov, e000000, 2, (RIWR, RIWR), iwmmxt_wmov),
+ CE(wmulsm, e300100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmulsl, e200100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmulum, e100100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wmulul, e000100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wor, e000000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wpackhss, e700080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wpackhus, e500080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wpackwss, eb00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wpackwus, e900080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wpackdss, ef00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wpackdus, ed00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wrorh, e700040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wrorhg, e700148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wrorw, eb00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wrorwg, eb00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wrord, ef00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wrordg, ef00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsadb, e000120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsadbz, e100120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsadh, e400120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsadhz, e500120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wshufh, e0001e0, 3, (RIWR, RIWR, I255), iwmmxt_wshufh),
+ CE(wsllh, e500040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsllhg, e500148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsllw, e900040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsllwg, e900148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wslld, ed00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wslldg, ed00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsrah, e400040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsrahg, e400148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsraw, e800040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsrawg, e800148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsrad, ec00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsradg, ec00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsrlh, e600040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsrlhg, e600148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsrlw, ea00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsrlwg, ea00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wsrld, ee00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsrldg, ee00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
+ CE(wstrb, c000000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ CE(wstrh, c400000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
+ CE(wstrw, c000100, 2, (RIWR_RIWC, ADDR), iwmmxt_wldstw),
+ CE(wstrd, c400100, 2, (RIWR, ADDR), iwmmxt_wldstd),
+ CE(wsubbss, e3001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubb, e0001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubbus, e1001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubhss, e7001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubh, e4001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubhus, e5001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubwss, eb001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubw, e8001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wsubwus, e9001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wunpckehub,e0000c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckehuh,e4000c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckehuw,e8000c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckehsb,e2000c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckehsh,e6000c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckehsw,ea000c0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckihb, e1000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wunpckihh, e5000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wunpckihw, e9000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wunpckelub,e0000e0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckeluh,e4000e0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckeluw,e8000e0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckelsb,e2000e0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckelsh,e6000e0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckelsw,ea000e0, 2, (RIWR, RIWR), rd_rn),
+ CE(wunpckilb, e1000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wunpckilh, e5000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wunpckilw, e9000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wxor, e100000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ CE(wzero, e300000, 1, (RIWR), iwmmxt_wzero),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT ARM_CEXT_MAVERICK /* Cirrus Maverick instructions. */
+ CE(cfldrs, c100400, 2, (RMF, ADDR), rd_cpaddr),
+ CE(cfldrd, c500400, 2, (RMD, ADDR), rd_cpaddr),
+ CE(cfldr32, c100500, 2, (RMFX, ADDR), rd_cpaddr),
+ CE(cfldr64, c500500, 2, (RMDX, ADDR), rd_cpaddr),
+ CE(cfstrs, c000400, 2, (RMF, ADDR), rd_cpaddr),
+ CE(cfstrd, c400400, 2, (RMD, ADDR), rd_cpaddr),
+ CE(cfstr32, c000500, 2, (RMFX, ADDR), rd_cpaddr),
+ CE(cfstr64, c400500, 2, (RMDX, ADDR), rd_cpaddr),
+ CE(cfmvsr, e000450, 2, (RMF, RR), rn_rd),
+ CE(cfmvrs, e100450, 2, (RR, RMF), rd_rn),
+ CE(cfmvdlr, e000410, 2, (RMD, RR), rn_rd),
+ CE(cfmvrdl, e100410, 2, (RR, RMD), rd_rn),
+ CE(cfmvdhr, e000430, 2, (RMD, RR), rn_rd),
+ CE(cfmvrdh, e100430, 2, (RR, RMD), rd_rn),
+ CE(cfmv64lr, e000510, 2, (RMDX, RR), rn_rd),
+ CE(cfmvr64l, e100510, 2, (RR, RMDX), rd_rn),
+ CE(cfmv64hr, e000530, 2, (RMDX, RR), rn_rd),
+ CE(cfmvr64h, e100530, 2, (RR, RMDX), rd_rn),
+ CE(cfmval32, e200440, 2, (RMAX, RMFX), rd_rn),
+ CE(cfmv32al, e100440, 2, (RMFX, RMAX), rd_rn),
+ CE(cfmvam32, e200460, 2, (RMAX, RMFX), rd_rn),
+ CE(cfmv32am, e100460, 2, (RMFX, RMAX), rd_rn),
+ CE(cfmvah32, e200480, 2, (RMAX, RMFX), rd_rn),
+ CE(cfmv32ah, e100480, 2, (RMFX, RMAX), rd_rn),
+ CE(cfmva32, e2004a0, 2, (RMAX, RMFX), rd_rn),
+ CE(cfmv32a, e1004a0, 2, (RMFX, RMAX), rd_rn),
+ CE(cfmva64, e2004c0, 2, (RMAX, RMDX), rd_rn),
+ CE(cfmv64a, e1004c0, 2, (RMDX, RMAX), rd_rn),
+ CE(cfmvsc32, e2004e0, 2, (RMDS, RMDX), mav_dspsc),
+ CE(cfmv32sc, e1004e0, 2, (RMDX, RMDS), rd),
+ CE(cfcpys, e000400, 2, (RMF, RMF), rd_rn),
+ CE(cfcpyd, e000420, 2, (RMD, RMD), rd_rn),
+ CE(cfcvtsd, e000460, 2, (RMD, RMF), rd_rn),
+ CE(cfcvtds, e000440, 2, (RMF, RMD), rd_rn),
+ CE(cfcvt32s, e000480, 2, (RMF, RMFX), rd_rn),
+ CE(cfcvt32d, e0004a0, 2, (RMD, RMFX), rd_rn),
+ CE(cfcvt64s, e0004c0, 2, (RMF, RMDX), rd_rn),
+ CE(cfcvt64d, e0004e0, 2, (RMD, RMDX), rd_rn),
+ CE(cfcvts32, e100580, 2, (RMFX, RMF), rd_rn),
+ CE(cfcvtd32, e1005a0, 2, (RMFX, RMD), rd_rn),
+ CE(cftruncs32,e1005c0, 2, (RMFX, RMF), rd_rn),
+ CE(cftruncd32,e1005e0, 2, (RMFX, RMD), rd_rn),
+ CE(cfrshl32, e000550, 3, (RMFX, RMFX, RR), mav_triple),
+ CE(cfrshl64, e000570, 3, (RMDX, RMDX, RR), mav_triple),
+ CE(cfsh32, e000500, 3, (RMFX, RMFX, I63s), mav_shift),
+ CE(cfsh64, e200500, 3, (RMDX, RMDX, I63s), mav_shift),
+ CE(cfcmps, e100490, 3, (RR, RMF, RMF), rd_rn_rm),
+ CE(cfcmpd, e1004b0, 3, (RR, RMD, RMD), rd_rn_rm),
+ CE(cfcmp32, e100590, 3, (RR, RMFX, RMFX), rd_rn_rm),
+ CE(cfcmp64, e1005b0, 3, (RR, RMDX, RMDX), rd_rn_rm),
+ CE(cfabss, e300400, 2, (RMF, RMF), rd_rn),
+ CE(cfabsd, e300420, 2, (RMD, RMD), rd_rn),
+ CE(cfnegs, e300440, 2, (RMF, RMF), rd_rn),
+ CE(cfnegd, e300460, 2, (RMD, RMD), rd_rn),
+ CE(cfadds, e300480, 3, (RMF, RMF, RMF), rd_rn_rm),
+ CE(cfaddd, e3004a0, 3, (RMD, RMD, RMD), rd_rn_rm),
+ CE(cfsubs, e3004c0, 3, (RMF, RMF, RMF), rd_rn_rm),
+ CE(cfsubd, e3004e0, 3, (RMD, RMD, RMD), rd_rn_rm),
+ CE(cfmuls, e100400, 3, (RMF, RMF, RMF), rd_rn_rm),
+ CE(cfmuld, e100420, 3, (RMD, RMD, RMD), rd_rn_rm),
+ CE(cfabs32, e300500, 2, (RMFX, RMFX), rd_rn),
+ CE(cfabs64, e300520, 2, (RMDX, RMDX), rd_rn),
+ CE(cfneg32, e300540, 2, (RMFX, RMFX), rd_rn),
+ CE(cfneg64, e300560, 2, (RMDX, RMDX), rd_rn),
+ CE(cfadd32, e300580, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ CE(cfadd64, e3005a0, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
+ CE(cfsub32, e3005c0, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ CE(cfsub64, e3005e0, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
+ CE(cfmul32, e100500, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ CE(cfmul64, e100520, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
+ CE(cfmac32, e100540, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ CE(cfmsc32, e100560, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
+ CE(cfmadd32, e000600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+ CE(cfmsub32, e100600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+ CE(cfmadda32, e200600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
+ CE(cfmsuba32, e300600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
+};
+#undef ARM_VARIANT
+#undef THUMB_VARIANT
+#undef TCE
+#undef TCM
+#undef TUE
+#undef TUF
+#undef TCC
+#undef CE
+#undef CM
+#undef UE
+#undef UF
+#undef UT
+#undef OPS0
+#undef OPS1
+#undef OPS2
+#undef OPS3
+#undef OPS4
+#undef OPS5
+#undef OPS6
+#undef do_0
+
+/* MD interface: bits in the object file. */
- if ((Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
+/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
+ for use in the a.out file, and stores them in the array pointed to by buf.
+ This knows about the endian-ness of the target machine and does
+ THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
+ 2 (short) and 4 (long) Floating numbers are put out as a series of
+ LITTLENUMS (shorts, here at least). */
- if (*str != '!')
- as_warn (_("inserted missing '!': load/store multiple always writes back base register"));
+void
+md_number_to_chars (char * buf, valueT val, int n)
+{
+ if (target_big_endian)
+ number_to_chars_bigendian (buf, val, n);
else
- str++;
+ number_to_chars_littleendian (buf, val, n);
+}
- if (skip_past_comma (&str) == FAIL
- || (range = reg_list (&str)) == FAIL)
- {
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+static valueT
+md_chars_to_number (char * buf, int n)
+{
+ valueT result = 0;
+ unsigned char * where = (unsigned char *) buf;
- if (inst.reloc.type != BFD_RELOC_UNUSED)
+ if (target_big_endian)
{
- /* This really doesn't seem worth it. */
- inst.reloc.type = BFD_RELOC_UNUSED;
- inst.error = _("expression too complex");
- return;
+ while (n--)
+ {
+ result <<= 8;
+ result |= (*where++ & 255);
+ }
}
-
- if (range & ~0xff)
+ else
{
- inst.error = _("only lo-regs valid in load/store multiple");
- return;
+ while (n--)
+ {
+ result <<= 8;
+ result |= (where[n] & 255);
+ }
}
- inst.instruction |= (Rb << 8) | range;
- end_of_line (str);
+ return result;
}
-static void
-do_t_ldr (char * str)
-{
- thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
-}
+/* MD interface: Sections. */
-static void
-do_t_ldrb (char * str)
+int
+md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
+ segT segtype ATTRIBUTE_UNUSED)
{
- thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
+ as_fatal (_("md_estimate_size_before_relax\n"));
+ return 1;
}
-static void
-do_t_ldrh (char * str)
-{
- thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
-}
+/* Round up a section size to the appropriate boundary. */
-static void
-do_t_lds (char * str)
+valueT
+md_section_align (segT segment ATTRIBUTE_UNUSED,
+ valueT size)
{
- int Rd, Rb, Ro;
-
- skip_whitespace (str);
-
- if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL
- || *str++ != '['
- || (Rb = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL
- || (Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || *str++ != ']')
- {
- if (! inst.error)
- inst.error = _("syntax: ldrs[b] Rd, [Rb, Ro]");
- return;
- }
-
- inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
- end_of_line (str);
+#ifdef OBJ_ELF
+ return size;
+#else
+ /* Round all sects to multiple of 4. */
+ return (size + 3) & ~3;
+#endif
}
-static void
-do_t_lsl (char * str)
-{
- thumb_shift (str, THUMB_LSL);
-}
+/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
+ of an rs_align_code fragment. */
-static void
-do_t_lsr (char * str)
+void
+arm_handle_align (fragS * fragP)
{
- thumb_shift (str, THUMB_LSR);
-}
+ static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
+ static char const thumb_noop[2] = { 0xc0, 0x46 };
+ static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
+ static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
-static void
-do_t_mov (char * str)
-{
- thumb_mov_compare (str, THUMB_MOVE);
-}
+ int bytes, fix, noop_size;
+ char * p;
+ const char * noop;
-static void
-do_t_push_pop (char * str)
-{
- long range;
+ if (fragP->fr_type != rs_align_code)
+ return;
- skip_whitespace (str);
+ bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
+ p = fragP->fr_literal + fragP->fr_fix;
+ fix = 0;
- if ((range = reg_list (&str)) == FAIL)
+ if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
+ bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
+
+ if (fragP->tc_frag_data)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
+ if (target_big_endian)
+ noop = thumb_bigend_noop;
+ else
+ noop = thumb_noop;
+ noop_size = sizeof (thumb_noop);
+ }
+ else
+ {
+ if (target_big_endian)
+ noop = arm_bigend_noop;
+ else
+ noop = arm_noop;
+ noop_size = sizeof (arm_noop);
}
- if (inst.reloc.type != BFD_RELOC_UNUSED)
+ if (bytes & (noop_size - 1))
{
- /* This really doesn't seem worth it. */
- inst.reloc.type = BFD_RELOC_UNUSED;
- inst.error = _("expression too complex");
- return;
+ fix = bytes & (noop_size - 1);
+ memset (p, 0, fix);
+ p += fix;
+ bytes -= fix;
}
- if (range & ~0xff)
+ while (bytes >= noop_size)
{
- if ((inst.instruction == T_OPCODE_PUSH
- && (range & ~0xff) == 1 << REG_LR)
- || (inst.instruction == T_OPCODE_POP
- && (range & ~0xff) == 1 << REG_PC))
- {
- inst.instruction |= THUMB_PP_PC_LR;
- range &= 0xff;
- }
- else
- {
- inst.error = _("invalid register list to push/pop instruction");
- return;
- }
+ memcpy (p, noop, noop_size);
+ p += noop_size;
+ bytes -= noop_size;
+ fix += noop_size;
}
- inst.instruction |= range;
- end_of_line (str);
+ fragP->fr_fix += fix;
+ fragP->fr_var = noop_size;
}
-static void
-do_t_str (char * str)
-{
- thumb_load_store (str, THUMB_STORE, THUMB_WORD);
-}
+/* Called from md_do_align. Used to create an alignment
+ frag in a code section. */
-static void
-do_t_strb (char * str)
+void
+arm_frag_align_code (int n, int max)
{
- thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
-}
+ char * p;
-static void
-do_t_strh (char * str)
-{
- thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
-}
+ /* We assume that there will never be a requirement
+ to support alignments greater than 32 bytes. */
+ if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
+ as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
-static void
-do_t_sub (char * str)
-{
- thumb_add_sub (str, 1);
+ p = frag_var (rs_align_code,
+ MAX_MEM_FOR_RS_ALIGN_CODE,
+ 1,
+ (relax_substateT) max,
+ (symbolS *) NULL,
+ (offsetT) n,
+ (char *) NULL);
+ *p = 0;
}
-static void
-do_t_swi (char * str)
-{
- skip_whitespace (str);
-
- if (my_get_expression (&inst.reloc.exp, &str))
- return;
+/* Perform target specific initialisation of a frag. */
- inst.reloc.type = BFD_RELOC_ARM_SWI;
- end_of_line (str);
+void
+arm_init_frag (fragS * fragP)
+{
+ /* Record whether this frag is in an ARM or a THUMB area. */
+ fragP->tc_frag_data = thumb_mode;
}
-static void
-do_t_adr (char * str)
+#ifdef OBJ_ELF
+/* When we change sections we need to issue a new mapping symbol. */
+
+void
+arm_elf_change_section (void)
{
- int reg;
+ flagword flags;
+ segment_info_type *seginfo;
- /* This is a pseudo-op of the form "adr rd, label" to be converted
- into a relative address of the form "add rd, pc, #label-.-4". */
- skip_whitespace (str);
+ /* Link an unlinked unwind index table section to the .text section. */
+ if (elf_section_type (now_seg) == SHT_ARM_EXIDX
+ && elf_linked_to_section (now_seg) == NULL)
+ elf_linked_to_section (now_seg) = text_section;
- /* Store Rd in temporary location inside instruction. */
- if ((reg = reg_required_here (&str, 4)) == FAIL
- || (reg > 7) /* For Thumb reg must be r0..r7. */
- || skip_past_comma (&str) == FAIL
- || my_get_expression (&inst.reloc.exp, &str))
- {
- if (!inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ if (!SEG_NORMAL (now_seg))
+ return;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
- inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
- inst.reloc.pc_rel = 1;
- inst.instruction |= REG_PC; /* Rd is already placed into the instruction. */
+ flags = bfd_get_section_flags (stdoutput, now_seg);
- end_of_line (str);
+ /* We can ignore sections that only contain debug info. */
+ if ((flags & SEC_ALLOC) == 0)
+ return;
+
+ seginfo = seg_info (now_seg);
+ mapstate = seginfo->tc_segment_info_data.mapstate;
+ marked_pr_dependency = seginfo->tc_segment_info_data.marked_pr_dependency;
}
-static void
-insert_reg (const struct reg_entry * r,
- struct hash_control * htab)
+int
+arm_elf_section_type (const char * str, size_t len)
{
- int len = strlen (r->name) + 2;
- char * buf = xmalloc (len);
- char * buf2 = xmalloc (len);
- int i = 0;
-
-#ifdef REGISTER_PREFIX
- buf[i++] = REGISTER_PREFIX;
-#endif
-
- strcpy (buf + i, r->name);
+ if (len == 5 && strncmp (str, "exidx", 5) == 0)
+ return SHT_ARM_EXIDX;
- for (i = 0; buf[i]; i++)
- buf2[i] = TOUPPER (buf[i]);
+ return -1;
+}
+
+/* Code to deal with unwinding tables. */
- buf2[i] = '\0';
+static void add_unwind_adjustsp (offsetT);
- hash_insert (htab, buf, (PTR) r);
- hash_insert (htab, buf2, (PTR) r);
-}
+/* Cenerate and deferred unwind frame offset. */
static void
-build_reg_hsh (struct reg_map * map)
+flush_pending_unwind (void)
{
- const struct reg_entry *r;
-
- if ((map->htab = hash_new ()) == NULL)
- as_fatal (_("virtual memory exhausted"));
+ offsetT offset;
- for (r = map->names; r->name != NULL; r++)
- insert_reg (r, map->htab);
+ offset = unwind.pending_offset;
+ unwind.pending_offset = 0;
+ if (offset != 0)
+ add_unwind_adjustsp (offset);
}
+/* Add an opcode to this list for this function. Two-byte opcodes should
+ be passed as op[0] << 8 | op[1]. The list of opcodes is built in reverse
+ order. */
+
static void
-insert_reg_alias (char * str,
- int regnum,
- struct hash_control *htab)
+add_unwind_opcode (valueT op, int length)
{
- const char * error;
- struct reg_entry * new = xmalloc (sizeof (struct reg_entry));
- const char * name = xmalloc (strlen (str) + 1);
-
- strcpy ((char *) name, str);
+ /* Add any deferred stack adjustment. */
+ if (unwind.pending_offset)
+ flush_pending_unwind ();
- new->name = name;
- new->number = regnum;
- new->builtin = FALSE;
+ unwind.sp_restored = 0;
- error = hash_insert (htab, name, (PTR) new);
- if (error)
+ if (unwind.opcode_count + length > unwind.opcode_alloc)
+ {
+ unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
+ if (unwind.opcodes)
+ unwind.opcodes = xrealloc (unwind.opcodes,
+ unwind.opcode_alloc);
+ else
+ unwind.opcodes = xmalloc (unwind.opcode_alloc);
+ }
+ while (length > 0)
{
- as_bad (_("failed to create an alias for %s, reason: %s"),
- str, error);
- free ((char *) name);
- free (new);
+ length--;
+ unwind.opcodes[unwind.opcode_count] = op & 0xff;
+ op >>= 8;
+ unwind.opcode_count++;
}
}
-/* 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 non-zero. Otherwise return zero. */
+/* Add unwind opcodes to adjust the stack pointer. */
-static int
-create_register_alias (char * newname, char * p)
+static void
+add_unwind_adjustsp (offsetT offset)
{
- char * q;
- char c;
-
- q = p;
- skip_whitespace (q);
-
- c = *p;
- *p = '\0';
+ valueT op;
- if (*q && !strncmp (q, ".req ", 5))
+ if (offset > 0x200)
{
- char *copy_of_str;
- char *r;
-
-#ifndef IGNORE_OPCODE_CASE
- newname = original_case_string;
-#endif
- copy_of_str = newname;
-
- q += 4;
- skip_whitespace (q);
+ /* We need at most 5 bytes to hold a 32-bit value in a uleb128. */
+ char bytes[5];
+ int n;
+ valueT o;
- for (r = q; *r != '\0'; r++)
- if (*r == ' ')
- break;
+ /* Long form: 0xb2, uleb128. */
+ /* This might not fit in a word so add the individual bytes,
+ remembering the list is built in reverse order. */
+ o = (valueT) ((offset - 0x204) >> 2);
+ if (o == 0)
+ add_unwind_opcode (0, 1);
- if (r != q)
+ /* Calculate the uleb128 encoding of the offset. */
+ n = 0;
+ while (o)
{
- enum arm_reg_type new_type, old_type;
- int old_regno;
- char d = *r;
-
- *r = '\0';
- old_type = arm_reg_parse_any (q);
- *r = d;
-
- new_type = arm_reg_parse_any (newname);
-
- if (new_type == REG_TYPE_MAX)
- {
- if (old_type != REG_TYPE_MAX)
- {
- old_regno = arm_reg_parse (&q, all_reg_maps[old_type].htab);
- insert_reg_alias (newname, old_regno,
- all_reg_maps[old_type].htab);
- }
- else
- as_warn (_("register '%s' does not exist\n"), q);
- }
- else if (old_type == REG_TYPE_MAX)
- {
- as_warn (_("ignoring redefinition of register alias '%s' to non-existant register '%s'"),
- copy_of_str, q);
- }
- else
- {
- /* Do not warn about redefinitions to the same alias. */
- if (new_type != old_type
- || (arm_reg_parse (&q, all_reg_maps[old_type].htab)
- != arm_reg_parse (&q, all_reg_maps[new_type].htab)))
- as_warn (_("ignoring redefinition of register alias '%s'"),
- copy_of_str);
-
- }
+ bytes[n] = o & 0x7f;
+ o >>= 7;
+ if (o)
+ bytes[n] |= 0x80;
+ n++;
}
- else
- as_warn (_("ignoring incomplete .req pseuso op"));
-
- *p = c;
- return 1;
+ /* Add the insn. */
+ for (; n; n--)
+ add_unwind_opcode (bytes[n - 1], 1);
+ add_unwind_opcode (0xb2, 1);
+ }
+ else if (offset > 0x100)
+ {
+ /* Two short opcodes. */
+ add_unwind_opcode (0x3f, 1);
+ op = (offset - 0x104) >> 2;
+ add_unwind_opcode (op, 1);
+ }
+ else if (offset > 0)
+ {
+ /* Short opcode. */
+ op = (offset - 4) >> 2;
+ add_unwind_opcode (op, 1);
+ }
+ else if (offset < 0)
+ {
+ offset = -offset;
+ while (offset > 0x100)
+ {
+ add_unwind_opcode (0x7f, 1);
+ offset -= 0x100;
+ }
+ op = ((offset - 4) >> 2) | 0x40;
+ add_unwind_opcode (op, 1);
}
-
- *p = c;
- return 0;
}
+/* Finish the list of unwind opcodes for this function. */
static void
-set_constant_flonums (void)
-{
- int i;
-
- for (i = 0; i < NUM_FLOAT_VALS; i++)
- if (atof_ieee ((char *) fp_const[i], 'x', fp_values[i]) == NULL)
- abort ();
-}
-
-
-static const struct asm_opcode insns[] =
+finish_unwind_opcodes (void)
{
- /* Core ARM Instructions. */
- {"and", 0xe0000000, 3, ARM_EXT_V1, do_arit},
- {"ands", 0xe0100000, 3, ARM_EXT_V1, do_arit},
- {"eor", 0xe0200000, 3, ARM_EXT_V1, do_arit},
- {"eors", 0xe0300000, 3, ARM_EXT_V1, do_arit},
- {"sub", 0xe0400000, 3, ARM_EXT_V1, do_arit},
- {"subs", 0xe0500000, 3, ARM_EXT_V1, do_arit},
- {"rsb", 0xe0600000, 3, ARM_EXT_V1, do_arit},
- {"rsbs", 0xe0700000, 3, ARM_EXT_V1, do_arit},
- {"add", 0xe0800000, 3, ARM_EXT_V1, do_arit},
- {"adds", 0xe0900000, 3, ARM_EXT_V1, do_arit},
- {"adc", 0xe0a00000, 3, ARM_EXT_V1, do_arit},
- {"adcs", 0xe0b00000, 3, ARM_EXT_V1, do_arit},
- {"sbc", 0xe0c00000, 3, ARM_EXT_V1, do_arit},
- {"sbcs", 0xe0d00000, 3, ARM_EXT_V1, do_arit},
- {"rsc", 0xe0e00000, 3, ARM_EXT_V1, do_arit},
- {"rscs", 0xe0f00000, 3, ARM_EXT_V1, do_arit},
- {"orr", 0xe1800000, 3, ARM_EXT_V1, do_arit},
- {"orrs", 0xe1900000, 3, ARM_EXT_V1, do_arit},
- {"bic", 0xe1c00000, 3, ARM_EXT_V1, do_arit},
- {"bics", 0xe1d00000, 3, ARM_EXT_V1, do_arit},
-
- {"tst", 0xe1100000, 3, ARM_EXT_V1, do_cmp},
- {"tsts", 0xe1100000, 3, ARM_EXT_V1, do_cmp},
- {"tstp", 0xe110f000, 3, ARM_EXT_V1, do_cmp},
- {"teq", 0xe1300000, 3, ARM_EXT_V1, do_cmp},
- {"teqs", 0xe1300000, 3, ARM_EXT_V1, do_cmp},
- {"teqp", 0xe130f000, 3, ARM_EXT_V1, do_cmp},
- {"cmp", 0xe1500000, 3, ARM_EXT_V1, do_cmp},
- {"cmps", 0xe1500000, 3, ARM_EXT_V1, do_cmp},
- {"cmpp", 0xe150f000, 3, ARM_EXT_V1, do_cmp},
- {"cmn", 0xe1700000, 3, ARM_EXT_V1, do_cmp},
- {"cmns", 0xe1700000, 3, ARM_EXT_V1, do_cmp},
- {"cmnp", 0xe170f000, 3, ARM_EXT_V1, do_cmp},
-
- {"mov", 0xe1a00000, 3, ARM_EXT_V1, do_mov},
- {"movs", 0xe1b00000, 3, ARM_EXT_V1, do_mov},
- {"mvn", 0xe1e00000, 3, ARM_EXT_V1, do_mov},
- {"mvns", 0xe1f00000, 3, ARM_EXT_V1, do_mov},
-
- {"ldr", 0xe4100000, 3, ARM_EXT_V1, do_ldst},
- {"ldrb", 0xe4500000, 3, ARM_EXT_V1, do_ldst},
- {"ldrt", 0xe4300000, 3, ARM_EXT_V1, do_ldstt},
- {"ldrbt", 0xe4700000, 3, ARM_EXT_V1, do_ldstt},
- {"str", 0xe4000000, 3, ARM_EXT_V1, do_ldst},
- {"strb", 0xe4400000, 3, ARM_EXT_V1, do_ldst},
- {"strt", 0xe4200000, 3, ARM_EXT_V1, do_ldstt},
- {"strbt", 0xe4600000, 3, ARM_EXT_V1, do_ldstt},
-
- {"stmia", 0xe8800000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmib", 0xe9800000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmda", 0xe8000000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmdb", 0xe9000000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmfd", 0xe9000000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmfa", 0xe9800000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmea", 0xe8800000, 3, ARM_EXT_V1, do_ldmstm},
- {"stmed", 0xe8000000, 3, ARM_EXT_V1, do_ldmstm},
-
- {"ldmia", 0xe8900000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmib", 0xe9900000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmda", 0xe8100000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmdb", 0xe9100000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmfd", 0xe8900000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmfa", 0xe8100000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmea", 0xe9100000, 3, ARM_EXT_V1, do_ldmstm},
- {"ldmed", 0xe9900000, 3, ARM_EXT_V1, do_ldmstm},
-
- {"swi", 0xef000000, 3, ARM_EXT_V1, do_swi},
-#ifdef TE_WINCE
- /* XXX This is the wrong place to do this. Think multi-arch. */
- {"bl", 0xeb000000, 2, ARM_EXT_V1, do_branch},
- {"b", 0xea000000, 1, ARM_EXT_V1, do_branch},
-#else
- {"bl", 0xebfffffe, 2, ARM_EXT_V1, do_branch},
- {"b", 0xeafffffe, 1, ARM_EXT_V1, do_branch},
-#endif
-
- /* Pseudo ops. */
- {"adr", 0xe28f0000, 3, ARM_EXT_V1, do_adr},
- {"adrl", 0xe28f0000, 3, ARM_EXT_V1, do_adrl},
- {"nop", 0xe1a00000, 3, ARM_EXT_V1, do_nop},
-
- /* ARM 2 multiplies. */
- {"mul", 0xe0000090, 3, ARM_EXT_V2, do_mul},
- {"muls", 0xe0100090, 3, ARM_EXT_V2, do_mul},
- {"mla", 0xe0200090, 3, ARM_EXT_V2, do_mla},
- {"mlas", 0xe0300090, 3, ARM_EXT_V2, do_mla},
-
- /* Generic coprocessor instructions. */
- {"cdp", 0xee000000, 3, ARM_EXT_V2, do_cdp},
- {"ldc", 0xec100000, 3, ARM_EXT_V2, do_lstc},
- {"ldcl", 0xec500000, 3, ARM_EXT_V2, do_lstc},
- {"stc", 0xec000000, 3, ARM_EXT_V2, do_lstc},
- {"stcl", 0xec400000, 3, ARM_EXT_V2, do_lstc},
- {"mcr", 0xee000010, 3, ARM_EXT_V2, do_co_reg},
- {"mrc", 0xee100010, 3, ARM_EXT_V2, do_co_reg},
-
- /* ARM 3 - swp instructions. */
- {"swp", 0xe1000090, 3, ARM_EXT_V2S, do_swap},
- {"swpb", 0xe1400090, 3, ARM_EXT_V2S, do_swap},
-
- /* ARM 6 Status register instructions. */
- {"mrs", 0xe10f0000, 3, ARM_EXT_V3, do_mrs},
- {"msr", 0xe120f000, 3, ARM_EXT_V3, do_msr},
- /* ScottB: our code uses 0xe128f000 for msr.
- NickC: but this is wrong because the bits 16 through 19 are
- handled by the PSR_xxx defines above. */
-
- /* ARM 7M long multiplies. */
- {"smull", 0xe0c00090, 5, ARM_EXT_V3M, do_mull},
- {"smulls", 0xe0d00090, 5, ARM_EXT_V3M, do_mull},
- {"umull", 0xe0800090, 5, ARM_EXT_V3M, do_mull},
- {"umulls", 0xe0900090, 5, ARM_EXT_V3M, do_mull},
- {"smlal", 0xe0e00090, 5, ARM_EXT_V3M, do_mull},
- {"smlals", 0xe0f00090, 5, ARM_EXT_V3M, do_mull},
- {"umlal", 0xe0a00090, 5, ARM_EXT_V3M, do_mull},
- {"umlals", 0xe0b00090, 5, ARM_EXT_V3M, do_mull},
-
- /* ARM Architecture 4. */
- {"ldrh", 0xe01000b0, 3, ARM_EXT_V4, do_ldstv4},
- {"ldrsh", 0xe01000f0, 3, ARM_EXT_V4, do_ldstv4},
- {"ldrsb", 0xe01000d0, 3, ARM_EXT_V4, do_ldstv4},
- {"strh", 0xe00000b0, 3, ARM_EXT_V4, do_ldstv4},
-
- /* ARM Architecture 4T. */
- /* Note: bx (and blx) are required on V5, even if the processor does
- not support Thumb. */
- {"bx", 0xe12fff10, 2, ARM_EXT_V4T | ARM_EXT_V5, do_bx},
-
- /* ARM Architecture 5T. */
- /* Note: blx has 2 variants, so the .value is set dynamically.
- Only one of the variants has conditional execution. */
- {"blx", 0xe0000000, 3, ARM_EXT_V5, do_blx},
- {"clz", 0xe16f0f10, 3, ARM_EXT_V5, do_clz},
- {"bkpt", 0xe1200070, 0, ARM_EXT_V5, do_bkpt},
- {"ldc2", 0xfc100000, 0, ARM_EXT_V5, do_lstc2},
- {"ldc2l", 0xfc500000, 0, ARM_EXT_V5, do_lstc2},
- {"stc2", 0xfc000000, 0, ARM_EXT_V5, do_lstc2},
- {"stc2l", 0xfc400000, 0, ARM_EXT_V5, do_lstc2},
- {"cdp2", 0xfe000000, 0, ARM_EXT_V5, do_cdp2},
- {"mcr2", 0xfe000010, 0, ARM_EXT_V5, do_co_reg2},
- {"mrc2", 0xfe100010, 0, ARM_EXT_V5, do_co_reg2},
-
- /* ARM Architecture 5TExP. */
- {"smlabb", 0xe1000080, 6, ARM_EXT_V5ExP, do_smla},
- {"smlatb", 0xe10000a0, 6, ARM_EXT_V5ExP, do_smla},
- {"smlabt", 0xe10000c0, 6, ARM_EXT_V5ExP, do_smla},
- {"smlatt", 0xe10000e0, 6, ARM_EXT_V5ExP, do_smla},
-
- {"smlawb", 0xe1200080, 6, ARM_EXT_V5ExP, do_smla},
- {"smlawt", 0xe12000c0, 6, ARM_EXT_V5ExP, do_smla},
-
- {"smlalbb", 0xe1400080, 7, ARM_EXT_V5ExP, do_smlal},
- {"smlaltb", 0xe14000a0, 7, ARM_EXT_V5ExP, do_smlal},
- {"smlalbt", 0xe14000c0, 7, ARM_EXT_V5ExP, do_smlal},
- {"smlaltt", 0xe14000e0, 7, ARM_EXT_V5ExP, do_smlal},
-
- {"smulbb", 0xe1600080, 6, ARM_EXT_V5ExP, do_smul},
- {"smultb", 0xe16000a0, 6, ARM_EXT_V5ExP, do_smul},
- {"smulbt", 0xe16000c0, 6, ARM_EXT_V5ExP, do_smul},
- {"smultt", 0xe16000e0, 6, ARM_EXT_V5ExP, do_smul},
-
- {"smulwb", 0xe12000a0, 6, ARM_EXT_V5ExP, do_smul},
- {"smulwt", 0xe12000e0, 6, ARM_EXT_V5ExP, do_smul},
-
- {"qadd", 0xe1000050, 4, ARM_EXT_V5ExP, do_qadd},
- {"qdadd", 0xe1400050, 5, ARM_EXT_V5ExP, do_qadd},
- {"qsub", 0xe1200050, 4, ARM_EXT_V5ExP, do_qadd},
- {"qdsub", 0xe1600050, 5, ARM_EXT_V5ExP, do_qadd},
-
- /* ARM Architecture 5TE. */
- {"pld", 0xf450f000, 0, ARM_EXT_V5E, do_pld},
- {"ldrd", 0xe00000d0, 3, ARM_EXT_V5E, do_ldrd},
- {"strd", 0xe00000f0, 3, ARM_EXT_V5E, do_ldrd},
-
- {"mcrr", 0xec400000, 4, ARM_EXT_V5E, do_co_reg2c},
- {"mrrc", 0xec500000, 4, ARM_EXT_V5E, do_co_reg2c},
-
- /* ARM Architecture 5TEJ. */
- {"bxj", 0xe12fff20, 3, ARM_EXT_V5J, do_bxj},
-
- /* ARM V6. */
- { "cps", 0xf1020000, 0, ARM_EXT_V6, do_cps},
- { "cpsie", 0xf1080000, 0, ARM_EXT_V6, do_cpsi},
- { "cpsid", 0xf10C0000, 0, ARM_EXT_V6, do_cpsi},
- { "ldrex", 0xe1900f9f, 5, ARM_EXT_V6, do_ldrex},
- { "mcrr2", 0xfc400000, 0, ARM_EXT_V6, do_co_reg2c},
- { "mrrc2", 0xfc500000, 0, ARM_EXT_V6, do_co_reg2c},
- { "pkhbt", 0xe6800010, 5, ARM_EXT_V6, do_pkhbt},
- { "pkhtb", 0xe6800050, 5, ARM_EXT_V6, do_pkhtb},
- { "qadd16", 0xe6200f10, 6, ARM_EXT_V6, do_qadd16},
- { "qadd8", 0xe6200f90, 5, ARM_EXT_V6, do_qadd16},
- { "qaddsubx", 0xe6200f30, 8, ARM_EXT_V6, do_qadd16},
- { "qsub16", 0xe6200f70, 6, ARM_EXT_V6, do_qadd16},
- { "qsub8", 0xe6200ff0, 5, ARM_EXT_V6, do_qadd16},
- { "qsubaddx", 0xe6200f50, 8, ARM_EXT_V6, do_qadd16},
- { "sadd16", 0xe6100f10, 6, ARM_EXT_V6, do_qadd16},
- { "sadd8", 0xe6100f90, 5, ARM_EXT_V6, do_qadd16},
- { "saddsubx", 0xe6100f30, 8, ARM_EXT_V6, do_qadd16},
- { "shadd16", 0xe6300f10, 7, ARM_EXT_V6, do_qadd16},
- { "shadd8", 0xe6300f90, 6, ARM_EXT_V6, do_qadd16},
- { "shaddsubx", 0xe6300f30, 9, ARM_EXT_V6, do_qadd16},
- { "shsub16", 0xe6300f70, 7, ARM_EXT_V6, do_qadd16},
- { "shsub8", 0xe6300ff0, 6, ARM_EXT_V6, do_qadd16},
- { "shsubaddx", 0xe6300f50, 9, ARM_EXT_V6, do_qadd16},
- { "ssub16", 0xe6100f70, 6, ARM_EXT_V6, do_qadd16},
- { "ssub8", 0xe6100ff0, 5, ARM_EXT_V6, do_qadd16},
- { "ssubaddx", 0xe6100f50, 8, ARM_EXT_V6, do_qadd16},
- { "uadd16", 0xe6500f10, 6, ARM_EXT_V6, do_qadd16},
- { "uadd8", 0xe6500f90, 5, ARM_EXT_V6, do_qadd16},
- { "uaddsubx", 0xe6500f30, 8, ARM_EXT_V6, do_qadd16},
- { "uhadd16", 0xe6700f10, 7, ARM_EXT_V6, do_qadd16},
- { "uhadd8", 0xe6700f90, 6, ARM_EXT_V6, do_qadd16},
- { "uhaddsubx", 0xe6700f30, 9, ARM_EXT_V6, do_qadd16},
- { "uhsub16", 0xe6700f70, 7, ARM_EXT_V6, do_qadd16},
- { "uhsub8", 0xe6700ff0, 6, ARM_EXT_V6, do_qadd16},
- { "uhsubaddx", 0xe6700f50, 9, ARM_EXT_V6, do_qadd16},
- { "uqadd16", 0xe6600f10, 7, ARM_EXT_V6, do_qadd16},
- { "uqadd8", 0xe6600f90, 6, ARM_EXT_V6, do_qadd16},
- { "uqaddsubx", 0xe6600f30, 9, ARM_EXT_V6, do_qadd16},
- { "uqsub16", 0xe6600f70, 7, ARM_EXT_V6, do_qadd16},
- { "uqsub8", 0xe6600ff0, 6, ARM_EXT_V6, do_qadd16},
- { "uqsubaddx", 0xe6600f50, 9, ARM_EXT_V6, do_qadd16},
- { "usub16", 0xe6500f70, 6, ARM_EXT_V6, do_qadd16},
- { "usub8", 0xe6500ff0, 5, ARM_EXT_V6, do_qadd16},
- { "usubaddx", 0xe6500f50, 8, ARM_EXT_V6, do_qadd16},
- { "rev", 0xe6bf0f30, 3, ARM_EXT_V6, do_rev},
- { "rev16", 0xe6bf0fb0, 5, ARM_EXT_V6, do_rev},
- { "revsh", 0xe6ff0fb0, 5, ARM_EXT_V6, do_rev},
- { "rfeia", 0xf8900a00, 0, ARM_EXT_V6, do_rfe},
- { "rfeib", 0xf9900a00, 0, ARM_EXT_V6, do_rfe},
- { "rfeda", 0xf8100a00, 0, ARM_EXT_V6, do_rfe},
- { "rfedb", 0xf9100a00, 0, ARM_EXT_V6, do_rfe},
- { "rfefd", 0xf8900a00, 0, ARM_EXT_V6, do_rfe},
- { "rfefa", 0xf9900a00, 0, ARM_EXT_V6, do_rfe},
- { "rfeea", 0xf8100a00, 0, ARM_EXT_V6, do_rfe},
- { "rfeed", 0xf9100a00, 0, ARM_EXT_V6, do_rfe},
- { "sxtah", 0xe6b00070, 5, ARM_EXT_V6, do_sxtah},
- { "sxtab16", 0xe6800070, 7, ARM_EXT_V6, do_sxtah},
- { "sxtab", 0xe6a00070, 5, ARM_EXT_V6, do_sxtah},
- { "sxth", 0xe6bf0070, 4, ARM_EXT_V6, do_sxth},
- { "sxtb16", 0xe68f0070, 6, ARM_EXT_V6, do_sxth},
- { "sxtb", 0xe6af0070, 4, ARM_EXT_V6, do_sxth},
- { "uxtah", 0xe6f00070, 5, ARM_EXT_V6, do_sxtah},
- { "uxtab16", 0xe6c00070, 7, ARM_EXT_V6, do_sxtah},
- { "uxtab", 0xe6e00070, 5, ARM_EXT_V6, do_sxtah},
- { "uxth", 0xe6ff0070, 4, ARM_EXT_V6, do_sxth},
- { "uxtb16", 0xe6cf0070, 6, ARM_EXT_V6, do_sxth},
- { "uxtb", 0xe6ef0070, 4, ARM_EXT_V6, do_sxth},
- { "sel", 0xe68000b0, 3, ARM_EXT_V6, do_qadd16},
- { "setend", 0xf1010000, 0, ARM_EXT_V6, do_setend},
- { "smlad", 0xe7000010, 5, ARM_EXT_V6, do_smlad},
- { "smladx", 0xe7000030, 6, ARM_EXT_V6, do_smlad},
- { "smlald", 0xe7400010, 6, ARM_EXT_V6, do_smlald},
- { "smlaldx", 0xe7400030, 7, ARM_EXT_V6, do_smlald},
- { "smlsd", 0xe7000050, 5, ARM_EXT_V6, do_smlad},
- { "smlsdx", 0xe7000070, 6, ARM_EXT_V6, do_smlad},
- { "smlsld", 0xe7400050, 6, ARM_EXT_V6, do_smlald},
- { "smlsldx", 0xe7400070, 7, ARM_EXT_V6, do_smlald},
- { "smmla", 0xe7500010, 5, ARM_EXT_V6, do_smlad},
- { "smmlar", 0xe7500030, 6, ARM_EXT_V6, do_smlad},
- { "smmls", 0xe75000d0, 5, ARM_EXT_V6, do_smlad},
- { "smmlsr", 0xe75000f0, 6, ARM_EXT_V6, do_smlad},
- { "smmul", 0xe750f010, 5, ARM_EXT_V6, do_smmul},
- { "smmulr", 0xe750f030, 6, ARM_EXT_V6, do_smmul},
- { "smuad", 0xe700f010, 5, ARM_EXT_V6, do_smmul},
- { "smuadx", 0xe700f030, 6, ARM_EXT_V6, do_smmul},
- { "smusd", 0xe700f050, 5, ARM_EXT_V6, do_smmul},
- { "smusdx", 0xe700f070, 6, ARM_EXT_V6, do_smmul},
- { "srsia", 0xf8cd0500, 0, ARM_EXT_V6, do_srs},
- { "srsib", 0xf9cd0500, 0, ARM_EXT_V6, do_srs},
- { "srsda", 0xf84d0500, 0, ARM_EXT_V6, do_srs},
- { "srsdb", 0xf94d0500, 0, ARM_EXT_V6, do_srs},
- { "ssat", 0xe6a00010, 4, ARM_EXT_V6, do_ssat},
- { "ssat16", 0xe6a00f30, 6, ARM_EXT_V6, do_ssat16},
- { "strex", 0xe1800f90, 5, ARM_EXT_V6, do_strex},
- { "umaal", 0xe0400090, 5, ARM_EXT_V6, do_umaal},
- { "usad8", 0xe780f010, 5, ARM_EXT_V6, do_smmul},
- { "usada8", 0xe7800010, 6, ARM_EXT_V6, do_smlad},
- { "usat", 0xe6e00010, 4, ARM_EXT_V6, do_usat},
- { "usat16", 0xe6e00f30, 6, ARM_EXT_V6, do_usat16},
-
- /* ARM V6K. */
- { "clrex", 0xf57ff01f, 0, ARM_EXT_V6K, do_empty},
- { "ldrexb", 0xe1d00f9f, 6, ARM_EXT_V6K, do_ldrex},
- { "ldrexd", 0xe1b00f9f, 6, ARM_EXT_V6K, do_ldrex},
- { "ldrexh", 0xe1f00f9f, 6, ARM_EXT_V6K, do_ldrex},
- { "sev", 0xe320f004, 3, ARM_EXT_V6K, do_empty},
- { "strexb", 0xe1c00f90, 6, ARM_EXT_V6K, do_strex},
- { "strexd", 0xe1a00f90, 6, ARM_EXT_V6K, do_strex},
- { "strexh", 0xe1e00f90, 6, ARM_EXT_V6K, do_strex},
- { "wfe", 0xe320f002, 3, ARM_EXT_V6K, do_empty},
- { "wfi", 0xe320f003, 3, ARM_EXT_V6K, do_empty},
- { "yield", 0xe320f001, 5, ARM_EXT_V6K, do_empty},
-
- /* ARM V6Z. */
- { "smi", 0xe1600070, 3, ARM_EXT_V6Z, do_smi},
-
- /* ARM V6T2. */
- { "bfc", 0xe7c0001f, 3, ARM_EXT_V6T2, do_bfc},
- { "bfi", 0xe7c00010, 3, ARM_EXT_V6T2, do_bfi},
- { "mls", 0xe0600090, 3, ARM_EXT_V6T2, do_mls},
- { "movw", 0xe3000000, 4, ARM_EXT_V6T2, do_mov16},
- { "movt", 0xe3400000, 4, ARM_EXT_V6T2, do_mov16},
- { "rbit", 0xe3ff0f30, 4, ARM_EXT_V6T2, do_rbit},
- { "sbfx", 0xe7a00050, 4, ARM_EXT_V6T2, do_bfx},
- { "ubfx", 0xe7e00050, 4, ARM_EXT_V6T2, do_bfx},
-
- { "ldrht", 0xe03000b0, 3, ARM_EXT_V6T2, do_ldsttv4},
- { "ldrsht", 0xe03000f0, 3, ARM_EXT_V6T2, do_ldsttv4},
- { "ldrsbt", 0xe03000d0, 3, ARM_EXT_V6T2, do_ldsttv4},
- { "strht", 0xe02000b0, 3, ARM_EXT_V6T2, do_ldsttv4},
-
- /* Core FPA instruction set (V1). */
- {"wfs", 0xee200110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
- {"rfs", 0xee300110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
- {"wfc", 0xee400110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
- {"rfc", 0xee500110, 3, FPU_FPA_EXT_V1, do_fpa_ctrl},
-
- {"ldfs", 0xec100100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
- {"ldfd", 0xec108100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
- {"ldfe", 0xec500100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
- {"ldfp", 0xec508100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
-
- {"stfs", 0xec000100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
- {"stfd", 0xec008100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
- {"stfe", 0xec400100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
- {"stfp", 0xec408100, 3, FPU_FPA_EXT_V1, do_fpa_ldst},
-
- {"mvfs", 0xee008100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfsp", 0xee008120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfsm", 0xee008140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfsz", 0xee008160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfd", 0xee008180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfdp", 0xee0081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfdm", 0xee0081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfdz", 0xee0081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfe", 0xee088100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfep", 0xee088120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfem", 0xee088140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mvfez", 0xee088160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"mnfs", 0xee108100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfsp", 0xee108120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfsm", 0xee108140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfsz", 0xee108160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfd", 0xee108180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfdp", 0xee1081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfdm", 0xee1081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfdz", 0xee1081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfe", 0xee188100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfep", 0xee188120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfem", 0xee188140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"mnfez", 0xee188160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"abss", 0xee208100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"abssp", 0xee208120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"abssm", 0xee208140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"abssz", 0xee208160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absd", 0xee208180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absdp", 0xee2081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absdm", 0xee2081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absdz", 0xee2081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"abse", 0xee288100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absep", 0xee288120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absem", 0xee288140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"absez", 0xee288160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"rnds", 0xee308100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndsp", 0xee308120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndsm", 0xee308140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndsz", 0xee308160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndd", 0xee308180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rnddp", 0xee3081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rnddm", 0xee3081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rnddz", 0xee3081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rnde", 0xee388100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndep", 0xee388120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndem", 0xee388140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"rndez", 0xee388160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"sqts", 0xee408100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtsp", 0xee408120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtsm", 0xee408140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtsz", 0xee408160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtd", 0xee408180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtdp", 0xee4081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtdm", 0xee4081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtdz", 0xee4081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqte", 0xee488100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtep", 0xee488120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtem", 0xee488140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sqtez", 0xee488160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"logs", 0xee508100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logsp", 0xee508120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logsm", 0xee508140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logsz", 0xee508160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logd", 0xee508180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logdp", 0xee5081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logdm", 0xee5081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logdz", 0xee5081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"loge", 0xee588100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logep", 0xee588120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logem", 0xee588140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"logez", 0xee588160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"lgns", 0xee608100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnsp", 0xee608120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnsm", 0xee608140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnsz", 0xee608160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnd", 0xee608180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgndp", 0xee6081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgndm", 0xee6081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgndz", 0xee6081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgne", 0xee688100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnep", 0xee688120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnem", 0xee688140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"lgnez", 0xee688160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"exps", 0xee708100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expsp", 0xee708120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expsm", 0xee708140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expsz", 0xee708160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expd", 0xee708180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expdp", 0xee7081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expdm", 0xee7081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expdz", 0xee7081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expe", 0xee788100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expep", 0xee788120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expem", 0xee788140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"expdz", 0xee788160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"sins", 0xee808100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sinsp", 0xee808120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sinsm", 0xee808140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sinsz", 0xee808160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sind", 0xee808180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sindp", 0xee8081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sindm", 0xee8081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sindz", 0xee8081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sine", 0xee888100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sinep", 0xee888120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sinem", 0xee888140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"sinez", 0xee888160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"coss", 0xee908100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cossp", 0xee908120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cossm", 0xee908140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cossz", 0xee908160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosd", 0xee908180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosdp", 0xee9081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosdm", 0xee9081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosdz", 0xee9081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cose", 0xee988100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosep", 0xee988120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosem", 0xee988140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"cosez", 0xee988160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"tans", 0xeea08100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tansp", 0xeea08120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tansm", 0xeea08140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tansz", 0xeea08160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tand", 0xeea08180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tandp", 0xeea081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tandm", 0xeea081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tandz", 0xeea081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tane", 0xeea88100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tanep", 0xeea88120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tanem", 0xeea88140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"tanez", 0xeea88160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"asns", 0xeeb08100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnsp", 0xeeb08120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnsm", 0xeeb08140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnsz", 0xeeb08160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnd", 0xeeb08180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asndp", 0xeeb081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asndm", 0xeeb081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asndz", 0xeeb081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asne", 0xeeb88100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnep", 0xeeb88120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnem", 0xeeb88140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"asnez", 0xeeb88160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"acss", 0xeec08100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acssp", 0xeec08120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acssm", 0xeec08140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acssz", 0xeec08160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsd", 0xeec08180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsdp", 0xeec081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsdm", 0xeec081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsdz", 0xeec081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acse", 0xeec88100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsep", 0xeec88120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsem", 0xeec88140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"acsez", 0xeec88160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"atns", 0xeed08100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnsp", 0xeed08120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnsm", 0xeed08140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnsz", 0xeed08160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnd", 0xeed08180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atndp", 0xeed081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atndm", 0xeed081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atndz", 0xeed081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atne", 0xeed88100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnep", 0xeed88120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnem", 0xeed88140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"atnez", 0xeed88160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"urds", 0xeee08100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdsp", 0xeee08120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdsm", 0xeee08140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdsz", 0xeee08160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdd", 0xeee08180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urddp", 0xeee081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urddm", 0xeee081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urddz", 0xeee081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urde", 0xeee88100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdep", 0xeee88120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdem", 0xeee88140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"urdez", 0xeee88160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"nrms", 0xeef08100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmsp", 0xeef08120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmsm", 0xeef08140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmsz", 0xeef08160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmd", 0xeef08180, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmdp", 0xeef081a0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmdm", 0xeef081c0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmdz", 0xeef081e0, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrme", 0xeef88100, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmep", 0xeef88120, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmem", 0xeef88140, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
- {"nrmez", 0xeef88160, 3, FPU_FPA_EXT_V1, do_fpa_monadic},
-
- {"adfs", 0xee000100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfsp", 0xee000120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfsm", 0xee000140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfsz", 0xee000160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfd", 0xee000180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfdp", 0xee0001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfdm", 0xee0001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfdz", 0xee0001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfe", 0xee080100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfep", 0xee080120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfem", 0xee080140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"adfez", 0xee080160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"sufs", 0xee200100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufsp", 0xee200120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufsm", 0xee200140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufsz", 0xee200160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufd", 0xee200180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufdp", 0xee2001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufdm", 0xee2001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufdz", 0xee2001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufe", 0xee280100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufep", 0xee280120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufem", 0xee280140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"sufez", 0xee280160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"rsfs", 0xee300100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfsp", 0xee300120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfsm", 0xee300140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfsz", 0xee300160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfd", 0xee300180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfdp", 0xee3001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfdm", 0xee3001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfdz", 0xee3001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfe", 0xee380100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfep", 0xee380120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfem", 0xee380140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rsfez", 0xee380160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"mufs", 0xee100100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufsp", 0xee100120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufsm", 0xee100140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufsz", 0xee100160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufd", 0xee100180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufdp", 0xee1001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufdm", 0xee1001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufdz", 0xee1001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufe", 0xee180100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufep", 0xee180120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufem", 0xee180140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"mufez", 0xee180160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"dvfs", 0xee400100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfsp", 0xee400120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfsm", 0xee400140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfsz", 0xee400160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfd", 0xee400180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfdp", 0xee4001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfdm", 0xee4001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfdz", 0xee4001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfe", 0xee480100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfep", 0xee480120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfem", 0xee480140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"dvfez", 0xee480160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"rdfs", 0xee500100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfsp", 0xee500120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfsm", 0xee500140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfsz", 0xee500160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfd", 0xee500180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfdp", 0xee5001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfdm", 0xee5001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfdz", 0xee5001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfe", 0xee580100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfep", 0xee580120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfem", 0xee580140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rdfez", 0xee580160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"pows", 0xee600100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powsp", 0xee600120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powsm", 0xee600140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powsz", 0xee600160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powd", 0xee600180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powdp", 0xee6001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powdm", 0xee6001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powdz", 0xee6001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powe", 0xee680100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powep", 0xee680120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powem", 0xee680140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"powez", 0xee680160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"rpws", 0xee700100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwsp", 0xee700120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwsm", 0xee700140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwsz", 0xee700160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwd", 0xee700180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwdp", 0xee7001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwdm", 0xee7001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwdz", 0xee7001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwe", 0xee780100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwep", 0xee780120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwem", 0xee780140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rpwez", 0xee780160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"rmfs", 0xee800100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfsp", 0xee800120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfsm", 0xee800140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfsz", 0xee800160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfd", 0xee800180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfdp", 0xee8001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfdm", 0xee8001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfdz", 0xee8001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfe", 0xee880100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfep", 0xee880120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfem", 0xee880140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"rmfez", 0xee880160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"fmls", 0xee900100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmlsp", 0xee900120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmlsm", 0xee900140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmlsz", 0xee900160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmld", 0xee900180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmldp", 0xee9001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmldm", 0xee9001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmldz", 0xee9001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmle", 0xee980100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmlep", 0xee980120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmlem", 0xee980140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fmlez", 0xee980160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"fdvs", 0xeea00100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvsp", 0xeea00120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvsm", 0xeea00140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvsz", 0xeea00160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvd", 0xeea00180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvdp", 0xeea001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvdm", 0xeea001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvdz", 0xeea001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdve", 0xeea80100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvep", 0xeea80120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvem", 0xeea80140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"fdvez", 0xeea80160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"frds", 0xeeb00100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdsp", 0xeeb00120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdsm", 0xeeb00140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdsz", 0xeeb00160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdd", 0xeeb00180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frddp", 0xeeb001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frddm", 0xeeb001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frddz", 0xeeb001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frde", 0xeeb80100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdep", 0xeeb80120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdem", 0xeeb80140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"frdez", 0xeeb80160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"pols", 0xeec00100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"polsp", 0xeec00120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"polsm", 0xeec00140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"polsz", 0xeec00160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"pold", 0xeec00180, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"poldp", 0xeec001a0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"poldm", 0xeec001c0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"poldz", 0xeec001e0, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"pole", 0xeec80100, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"polep", 0xeec80120, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"polem", 0xeec80140, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
- {"polez", 0xeec80160, 3, FPU_FPA_EXT_V1, do_fpa_dyadic},
-
- {"cmf", 0xee90f110, 3, FPU_FPA_EXT_V1, do_fpa_cmp},
- {"cmfe", 0xeed0f110, 3, FPU_FPA_EXT_V1, do_fpa_cmp},
- {"cnf", 0xeeb0f110, 3, FPU_FPA_EXT_V1, do_fpa_cmp},
- {"cnfe", 0xeef0f110, 3, FPU_FPA_EXT_V1, do_fpa_cmp},
- /* The FPA10 data sheet suggests that the 'E' of cmfe/cnfe should
- not be an optional suffix, but part of the instruction. To be
- compatible, we accept either. */
- {"cmfe", 0xeed0f110, 4, FPU_FPA_EXT_V1, do_fpa_cmp},
- {"cnfe", 0xeef0f110, 4, FPU_FPA_EXT_V1, do_fpa_cmp},
-
- {"flts", 0xee000110, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltsp", 0xee000130, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltsm", 0xee000150, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltsz", 0xee000170, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltd", 0xee000190, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltdp", 0xee0001b0, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltdm", 0xee0001d0, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltdz", 0xee0001f0, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"flte", 0xee080110, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltep", 0xee080130, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltem", 0xee080150, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
- {"fltez", 0xee080170, 3, FPU_FPA_EXT_V1, do_fpa_from_reg},
-
- /* The implementation of the FIX instruction is broken on some
- assemblers, in that it accepts a precision specifier as well as a
- rounding specifier, despite the fact that this is meaningless.
- To be more compatible, we accept it as well, though of course it
- does not set any bits. */
- {"fix", 0xee100110, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixp", 0xee100130, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixm", 0xee100150, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixz", 0xee100170, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixsp", 0xee100130, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixsm", 0xee100150, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixsz", 0xee100170, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixdp", 0xee100130, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixdm", 0xee100150, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixdz", 0xee100170, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixep", 0xee100130, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixem", 0xee100150, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
- {"fixez", 0xee100170, 3, FPU_FPA_EXT_V1, do_fpa_to_reg},
-
- /* Instructions that were new with the real FPA, call them V2. */
- {"lfm", 0xec100200, 3, FPU_FPA_EXT_V2, do_fpa_ldmstm},
- {"lfmfd", 0xec900200, 3, FPU_FPA_EXT_V2, do_fpa_ldmstm},
- {"lfmea", 0xed100200, 3, FPU_FPA_EXT_V2, do_fpa_ldmstm},
- {"sfm", 0xec000200, 3, FPU_FPA_EXT_V2, do_fpa_ldmstm},
- {"sfmfd", 0xed000200, 3, FPU_FPA_EXT_V2, do_fpa_ldmstm},
- {"sfmea", 0xec800200, 3, FPU_FPA_EXT_V2, do_fpa_ldmstm},
-
- /* VFP V1xD (single precision). */
- /* Moves and type conversions. */
- {"fcpys", 0xeeb00a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fmrs", 0xee100a10, 4, FPU_VFP_EXT_V1xD, do_vfp_reg_from_sp},
- {"fmsr", 0xee000a10, 4, FPU_VFP_EXT_V1xD, do_vfp_sp_from_reg},
- {"fmstat", 0xeef1fa10, 6, FPU_VFP_EXT_V1xD, do_empty},
- {"fsitos", 0xeeb80ac0, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fuitos", 0xeeb80a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"ftosis", 0xeebd0a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"ftosizs", 0xeebd0ac0, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"ftouis", 0xeebc0a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"ftouizs", 0xeebc0ac0, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fmrx", 0xeef00a10, 4, FPU_VFP_EXT_V1xD, do_vfp_reg_from_ctrl},
- {"fmxr", 0xeee00a10, 4, FPU_VFP_EXT_V1xD, do_vfp_ctrl_from_reg},
-
- /* Memory operations. */
- {"flds", 0xed100a00, 4, FPU_VFP_EXT_V1xD, do_vfp_sp_ldst},
- {"fsts", 0xed000a00, 4, FPU_VFP_EXT_V1xD, do_vfp_sp_ldst},
- {"fldmias", 0xec900a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
- {"fldmfds", 0xec900a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
- {"fldmdbs", 0xed300a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
- {"fldmeas", 0xed300a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
- {"fldmiax", 0xec900b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
- {"fldmfdx", 0xec900b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
- {"fldmdbx", 0xed300b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
- {"fldmeax", 0xed300b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
- {"fstmias", 0xec800a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
- {"fstmeas", 0xec800a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmia},
- {"fstmdbs", 0xed200a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
- {"fstmfds", 0xed200a00, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_ldstmdb},
- {"fstmiax", 0xec800b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
- {"fstmeax", 0xec800b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmia},
- {"fstmdbx", 0xed200b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
- {"fstmfdx", 0xed200b00, 7, FPU_VFP_EXT_V1xD, do_vfp_xp_ldstmdb},
-
- /* Monadic operations. */
- {"fabss", 0xeeb00ac0, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fnegs", 0xeeb10a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fsqrts", 0xeeb10ac0, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
-
- /* Dyadic operations. */
- {"fadds", 0xee300a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fsubs", 0xee300a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fmuls", 0xee200a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fdivs", 0xee800a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fmacs", 0xee000a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fmscs", 0xee100a00, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fnmuls", 0xee200a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fnmacs", 0xee000a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
- {"fnmscs", 0xee100a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_dyadic},
-
- /* Comparisons. */
- {"fcmps", 0xeeb40a40, 5, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fcmpzs", 0xeeb50a40, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_compare_z},
- {"fcmpes", 0xeeb40ac0, 6, FPU_VFP_EXT_V1xD, do_vfp_sp_monadic},
- {"fcmpezs", 0xeeb50ac0, 7, FPU_VFP_EXT_V1xD, do_vfp_sp_compare_z},
+ valueT op;
- /* VFP V1 (Double precision). */
- /* Moves and type conversions. */
- {"fcpyd", 0xeeb00b40, 5, FPU_VFP_EXT_V1, do_vfp_dp_monadic},
- {"fcvtds", 0xeeb70ac0, 6, FPU_VFP_EXT_V1, do_vfp_dp_sp_cvt},
- {"fcvtsd", 0xeeb70bc0, 6, FPU_VFP_EXT_V1, do_vfp_sp_dp_cvt},
- {"fmdhr", 0xee200b10, 5, FPU_VFP_EXT_V1, do_vfp_dp_from_reg},
- {"fmdlr", 0xee000b10, 5, FPU_VFP_EXT_V1, do_vfp_dp_from_reg},
- {"fmrdh", 0xee300b10, 5, FPU_VFP_EXT_V1, do_vfp_reg_from_dp},
- {"fmrdl", 0xee100b10, 5, FPU_VFP_EXT_V1, do_vfp_reg_from_dp},
- {"fsitod", 0xeeb80bc0, 6, FPU_VFP_EXT_V1, do_vfp_dp_sp_cvt},
- {"fuitod", 0xeeb80b40, 6, FPU_VFP_EXT_V1, do_vfp_dp_sp_cvt},
- {"ftosid", 0xeebd0b40, 6, FPU_VFP_EXT_V1, do_vfp_sp_dp_cvt},
- {"ftosizd", 0xeebd0bc0, 7, FPU_VFP_EXT_V1, do_vfp_sp_dp_cvt},
- {"ftouid", 0xeebc0b40, 6, FPU_VFP_EXT_V1, do_vfp_sp_dp_cvt},
- {"ftouizd", 0xeebc0bc0, 7, FPU_VFP_EXT_V1, do_vfp_sp_dp_cvt},
-
- /* Memory operations. */
- {"fldd", 0xed100b00, 4, FPU_VFP_EXT_V1, do_vfp_dp_ldst},
- {"fstd", 0xed000b00, 4, FPU_VFP_EXT_V1, do_vfp_dp_ldst},
- {"fldmiad", 0xec900b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmia},
- {"fldmfdd", 0xec900b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmia},
- {"fldmdbd", 0xed300b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmdb},
- {"fldmead", 0xed300b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmdb},
- {"fstmiad", 0xec800b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmia},
- {"fstmead", 0xec800b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmia},
- {"fstmdbd", 0xed200b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmdb},
- {"fstmfdd", 0xed200b00, 7, FPU_VFP_EXT_V1, do_vfp_dp_ldstmdb},
+ if (unwind.fp_used)
+ {
+ /* Adjust sp as neccessary. */
+ unwind.pending_offset += unwind.fp_offset - unwind.frame_size;
+ flush_pending_unwind ();
- /* Monadic operations. */
- {"fabsd", 0xeeb00bc0, 5, FPU_VFP_EXT_V1, do_vfp_dp_monadic},
- {"fnegd", 0xeeb10b40, 5, FPU_VFP_EXT_V1, do_vfp_dp_monadic},
- {"fsqrtd", 0xeeb10bc0, 6, FPU_VFP_EXT_V1, do_vfp_dp_monadic},
-
- /* Dyadic operations. */
- {"faddd", 0xee300b00, 5, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fsubd", 0xee300b40, 5, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fmuld", 0xee200b00, 5, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fdivd", 0xee800b00, 5, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fmacd", 0xee000b00, 5, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fmscd", 0xee100b00, 5, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fnmuld", 0xee200b40, 6, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fnmacd", 0xee000b40, 6, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
- {"fnmscd", 0xee100b40, 6, FPU_VFP_EXT_V1, do_vfp_dp_dyadic},
+ /* After restoring sp from the frame pointer. */
+ op = 0x90 | unwind.fp_reg;
+ add_unwind_opcode (op, 1);
+ }
+ else
+ flush_pending_unwind ();
+}
- /* Comparisons. */
- {"fcmpd", 0xeeb40b40, 5, FPU_VFP_EXT_V1, do_vfp_dp_monadic},
- {"fcmpzd", 0xeeb50b40, 6, FPU_VFP_EXT_V1, do_vfp_dp_compare_z},
- {"fcmped", 0xeeb40bc0, 6, FPU_VFP_EXT_V1, do_vfp_dp_monadic},
- {"fcmpezd", 0xeeb50bc0, 7, FPU_VFP_EXT_V1, do_vfp_dp_compare_z},
-
- /* VFP V2. */
- {"fmsrr", 0xec400a10, 5, FPU_VFP_EXT_V2, do_vfp_sp2_from_reg2},
- {"fmrrs", 0xec500a10, 5, FPU_VFP_EXT_V2, do_vfp_reg2_from_sp2},
- {"fmdrr", 0xec400b10, 5, FPU_VFP_EXT_V2, do_vfp_dp_from_reg2},
- {"fmrrd", 0xec500b10, 5, FPU_VFP_EXT_V2, do_vfp_reg2_from_dp},
-
- /* Intel XScale extensions to ARM V5 ISA. (All use CP0). */
- {"mia", 0xee200010, 3, ARM_CEXT_XSCALE, do_xsc_mia},
- {"miaph", 0xee280010, 5, ARM_CEXT_XSCALE, do_xsc_mia},
- {"miabb", 0xee2c0010, 5, ARM_CEXT_XSCALE, do_xsc_mia},
- {"miabt", 0xee2d0010, 5, ARM_CEXT_XSCALE, do_xsc_mia},
- {"miatb", 0xee2e0010, 5, ARM_CEXT_XSCALE, do_xsc_mia},
- {"miatt", 0xee2f0010, 5, ARM_CEXT_XSCALE, do_xsc_mia},
- {"mar", 0xec400000, 3, ARM_CEXT_XSCALE, do_xsc_mar},
- {"mra", 0xec500000, 3, ARM_CEXT_XSCALE, do_xsc_mra},
-
- /* Intel Wireless MMX technology instructions. */
- {"tandcb", 0xee130130, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tandc},
- {"tandch", 0xee530130, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tandc},
- {"tandcw", 0xee930130, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tandc},
- {"tbcstb", 0xee400010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tbcst},
- {"tbcsth", 0xee400050, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tbcst},
- {"tbcstw", 0xee400090, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tbcst},
- {"textrcb", 0xee130170, 7, ARM_CEXT_IWMMXT, do_iwmmxt_textrc},
- {"textrch", 0xee530170, 7, ARM_CEXT_IWMMXT, do_iwmmxt_textrc},
- {"textrcw", 0xee930170, 7, ARM_CEXT_IWMMXT, do_iwmmxt_textrc},
- {"textrmub", 0xee100070, 8, ARM_CEXT_IWMMXT, do_iwmmxt_textrm},
- {"textrmuh", 0xee500070, 8, ARM_CEXT_IWMMXT, do_iwmmxt_textrm},
- {"textrmuw", 0xee900070, 8, ARM_CEXT_IWMMXT, do_iwmmxt_textrm},
- {"textrmsb", 0xee100078, 8, ARM_CEXT_IWMMXT, do_iwmmxt_textrm},
- {"textrmsh", 0xee500078, 8, ARM_CEXT_IWMMXT, do_iwmmxt_textrm},
- {"textrmsw", 0xee900078, 8, ARM_CEXT_IWMMXT, do_iwmmxt_textrm},
- {"tinsrb", 0xee600010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tinsr},
- {"tinsrh", 0xee600050, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tinsr},
- {"tinsrw", 0xee600090, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tinsr},
- {"tmcr", 0xee000110, 4, ARM_CEXT_IWMMXT, do_iwmmxt_tmcr},
- {"tmcrr", 0xec400000, 5, ARM_CEXT_IWMMXT, do_iwmmxt_tmcrr},
- {"tmia", 0xee200010, 4, ARM_CEXT_IWMMXT, do_iwmmxt_tmia},
- {"tmiaph", 0xee280010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tmia},
- {"tmiabb", 0xee2c0010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tmia},
- {"tmiabt", 0xee2d0010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tmia},
- {"tmiatb", 0xee2e0010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tmia},
- {"tmiatt", 0xee2f0010, 6, ARM_CEXT_IWMMXT, do_iwmmxt_tmia},
- {"tmovmskb", 0xee100030, 8, ARM_CEXT_IWMMXT, do_iwmmxt_tmovmsk},
- {"tmovmskh", 0xee500030, 8, ARM_CEXT_IWMMXT, do_iwmmxt_tmovmsk},
- {"tmovmskw", 0xee900030, 8, ARM_CEXT_IWMMXT, do_iwmmxt_tmovmsk},
- {"tmrc", 0xee100110, 4, ARM_CEXT_IWMMXT, do_iwmmxt_tmrc},
- {"tmrrc", 0xec500000, 5, ARM_CEXT_IWMMXT, do_iwmmxt_tmrrc},
- {"torcb", 0xee130150, 5, ARM_CEXT_IWMMXT, do_iwmmxt_torc},
- {"torch", 0xee530150, 5, ARM_CEXT_IWMMXT, do_iwmmxt_torc},
- {"torcw", 0xee930150, 5, ARM_CEXT_IWMMXT, do_iwmmxt_torc},
- {"waccb", 0xee0001c0, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wacch", 0xee4001c0, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"waccw", 0xee8001c0, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"waddbss", 0xee300180, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddb", 0xee000180, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddbus", 0xee100180, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddhss", 0xee700180, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddh", 0xee400180, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddhus", 0xee500180, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddwss", 0xeeb00180, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddw", 0xee800180, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waddwus", 0xee900180, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"waligni", 0xee000020, 7, ARM_CEXT_IWMMXT, do_iwmmxt_waligni},
- {"walignr0", 0xee800020, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"walignr1", 0xee900020, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"walignr2", 0xeea00020, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"walignr3", 0xeeb00020, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wand", 0xee200000, 4, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wandn", 0xee300000, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wavg2b", 0xee800000, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wavg2br", 0xee900000, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wavg2h", 0xeec00000, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wavg2hr", 0xeed00000, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpeqb", 0xee000060, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpeqh", 0xee400060, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpeqw", 0xee800060, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpgtub", 0xee100060, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpgtuh", 0xee500060, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpgtuw", 0xee900060, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpgtsb", 0xee300060, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpgtsh", 0xee700060, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wcmpgtsw", 0xeeb00060, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wldrb", 0xec100000, 5, ARM_CEXT_IWMMXT, do_iwmmxt_byte_addr},
- {"wldrh", 0xec100100, 5, ARM_CEXT_IWMMXT, do_iwmmxt_byte_addr},
- {"wldrw", 0xec100200, 5, ARM_CEXT_IWMMXT, do_iwmmxt_word_addr},
- {"wldrd", 0xec100300, 5, ARM_CEXT_IWMMXT, do_iwmmxt_word_addr},
- {"wmacs", 0xee600100, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmacsz", 0xee700100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmacu", 0xee400100, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmacuz", 0xee500100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmadds", 0xeea00100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaddu", 0xee800100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaxsb", 0xee200160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaxsh", 0xee600160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaxsw", 0xeea00160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaxub", 0xee000160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaxuh", 0xee400160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmaxuw", 0xee800160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wminsb", 0xee300160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wminsh", 0xee700160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wminsw", 0xeeb00160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wminub", 0xee100160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wminuh", 0xee500160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wminuw", 0xee900160, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmov", 0xee000000, 4, ARM_CEXT_IWMMXT, do_iwmmxt_wmov},
- {"wmulsm", 0xee300100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmulsl", 0xee200100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmulum", 0xee100100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wmulul", 0xee000100, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wor", 0xee000000, 3, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wpackhss", 0xee700080, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wpackhus", 0xee500080, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wpackwss", 0xeeb00080, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wpackwus", 0xee900080, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wpackdss", 0xeef00080, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wpackdus", 0xeed00080, 8, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wrorh", 0xee700040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wrorhg", 0xee700148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wrorw", 0xeeb00040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wrorwg", 0xeeb00148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wrord", 0xeef00040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wrordg", 0xeef00148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsadb", 0xee000120, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsadbz", 0xee100120, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsadh", 0xee400120, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsadhz", 0xee500120, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wshufh", 0xee0001e0, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wshufh},
- {"wsllh", 0xee500040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsllhg", 0xee500148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsllw", 0xee900040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsllwg", 0xee900148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wslld", 0xeed00040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wslldg", 0xeed00148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsrah", 0xee400040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsrahg", 0xee400148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsraw", 0xee800040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsrawg", 0xee800148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsrad", 0xeec00040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsradg", 0xeec00148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsrlh", 0xee600040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsrlhg", 0xee600148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsrlw", 0xeea00040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsrlwg", 0xeea00148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wsrld", 0xeee00040, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsrldg", 0xeee00148, 6, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwcg},
- {"wstrb", 0xec000000, 5, ARM_CEXT_IWMMXT, do_iwmmxt_byte_addr},
- {"wstrh", 0xec000100, 5, ARM_CEXT_IWMMXT, do_iwmmxt_byte_addr},
- {"wstrw", 0xec000200, 5, ARM_CEXT_IWMMXT, do_iwmmxt_word_addr},
- {"wstrd", 0xec000300, 5, ARM_CEXT_IWMMXT, do_iwmmxt_word_addr},
- {"wsubbss", 0xee3001a0, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubb", 0xee0001a0, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubbus", 0xee1001a0, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubhss", 0xee7001a0, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubh", 0xee4001a0, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubhus", 0xee5001a0, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubwss", 0xeeb001a0, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubw", 0xee8001a0, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wsubwus", 0xee9001a0, 7, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wunpckehub", 0xee0000c0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckehuh", 0xee4000c0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckehuw", 0xee8000c0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckehsb", 0xee2000c0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckehsh", 0xee6000c0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckehsw", 0xeea000c0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckihb", 0xee1000c0, 9, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wunpckihh", 0xee5000c0, 9, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wunpckihw", 0xee9000c0, 9, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wunpckelub", 0xee0000e0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckeluh", 0xee4000e0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckeluw", 0xee8000e0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckelsb", 0xee2000e0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckelsh", 0xee6000e0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckelsw", 0xeea000e0, 10, ARM_CEXT_IWMMXT, do_iwmmxt_wrwr},
- {"wunpckilb", 0xee1000e0, 9, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wunpckilh", 0xee5000e0, 9, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wunpckilw", 0xee9000e0, 9, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wxor", 0xee100000, 4, ARM_CEXT_IWMMXT, do_iwmmxt_wrwrwr},
- {"wzero", 0xee300000, 5, ARM_CEXT_IWMMXT, do_iwmmxt_wzero},
-
- /* Cirrus Maverick instructions. */
- {"cfldrs", 0xec100400, 6, ARM_CEXT_MAVERICK, do_mav_ldst_1},
- {"cfldrd", 0xec500400, 6, ARM_CEXT_MAVERICK, do_mav_ldst_2},
- {"cfldr32", 0xec100500, 7, ARM_CEXT_MAVERICK, do_mav_ldst_3},
- {"cfldr64", 0xec500500, 7, ARM_CEXT_MAVERICK, do_mav_ldst_4},
- {"cfstrs", 0xec000400, 6, ARM_CEXT_MAVERICK, do_mav_ldst_1},
- {"cfstrd", 0xec400400, 6, ARM_CEXT_MAVERICK, do_mav_ldst_2},
- {"cfstr32", 0xec000500, 7, ARM_CEXT_MAVERICK, do_mav_ldst_3},
- {"cfstr64", 0xec400500, 7, ARM_CEXT_MAVERICK, do_mav_ldst_4},
- {"cfmvsr", 0xee000450, 6, ARM_CEXT_MAVERICK, do_mav_binops_2a},
- {"cfmvrs", 0xee100450, 6, ARM_CEXT_MAVERICK, do_mav_binops_1a},
- {"cfmvdlr", 0xee000410, 7, ARM_CEXT_MAVERICK, do_mav_binops_2b},
- {"cfmvrdl", 0xee100410, 7, ARM_CEXT_MAVERICK, do_mav_binops_1b},
- {"cfmvdhr", 0xee000430, 7, ARM_CEXT_MAVERICK, do_mav_binops_2b},
- {"cfmvrdh", 0xee100430, 7, ARM_CEXT_MAVERICK, do_mav_binops_1b},
- {"cfmv64lr", 0xee000510, 8, ARM_CEXT_MAVERICK, do_mav_binops_2c},
- {"cfmvr64l", 0xee100510, 8, ARM_CEXT_MAVERICK, do_mav_binops_1c},
- {"cfmv64hr", 0xee000530, 8, ARM_CEXT_MAVERICK, do_mav_binops_2c},
- {"cfmvr64h", 0xee100530, 8, ARM_CEXT_MAVERICK, do_mav_binops_1c},
- {"cfmval32", 0xee200440, 8, ARM_CEXT_MAVERICK, do_mav_binops_3a},
- {"cfmv32al", 0xee100440, 8, ARM_CEXT_MAVERICK, do_mav_binops_3b},
- {"cfmvam32", 0xee200460, 8, ARM_CEXT_MAVERICK, do_mav_binops_3a},
- {"cfmv32am", 0xee100460, 8, ARM_CEXT_MAVERICK, do_mav_binops_3b},
- {"cfmvah32", 0xee200480, 8, ARM_CEXT_MAVERICK, do_mav_binops_3a},
- {"cfmv32ah", 0xee100480, 8, ARM_CEXT_MAVERICK, do_mav_binops_3b},
- {"cfmva32", 0xee2004a0, 7, ARM_CEXT_MAVERICK, do_mav_binops_3a},
- {"cfmv32a", 0xee1004a0, 7, ARM_CEXT_MAVERICK, do_mav_binops_3b},
- {"cfmva64", 0xee2004c0, 7, ARM_CEXT_MAVERICK, do_mav_binops_3c},
- {"cfmv64a", 0xee1004c0, 7, ARM_CEXT_MAVERICK, do_mav_binops_3d},
- {"cfmvsc32", 0xee2004e0, 8, ARM_CEXT_MAVERICK, do_mav_dspsc_1},
- {"cfmv32sc", 0xee1004e0, 8, ARM_CEXT_MAVERICK, do_mav_dspsc_2},
- {"cfcpys", 0xee000400, 6, ARM_CEXT_MAVERICK, do_mav_binops_1d},
- {"cfcpyd", 0xee000420, 6, ARM_CEXT_MAVERICK, do_mav_binops_1e},
- {"cfcvtsd", 0xee000460, 7, ARM_CEXT_MAVERICK, do_mav_binops_1f},
- {"cfcvtds", 0xee000440, 7, ARM_CEXT_MAVERICK, do_mav_binops_1g},
- {"cfcvt32s", 0xee000480, 8, ARM_CEXT_MAVERICK, do_mav_binops_1h},
- {"cfcvt32d", 0xee0004a0, 8, ARM_CEXT_MAVERICK, do_mav_binops_1i},
- {"cfcvt64s", 0xee0004c0, 8, ARM_CEXT_MAVERICK, do_mav_binops_1j},
- {"cfcvt64d", 0xee0004e0, 8, ARM_CEXT_MAVERICK, do_mav_binops_1k},
- {"cfcvts32", 0xee100580, 8, ARM_CEXT_MAVERICK, do_mav_binops_1l},
- {"cfcvtd32", 0xee1005a0, 8, ARM_CEXT_MAVERICK, do_mav_binops_1m},
- {"cftruncs32", 0xee1005c0, 10, ARM_CEXT_MAVERICK, do_mav_binops_1l},
- {"cftruncd32", 0xee1005e0, 10, ARM_CEXT_MAVERICK, do_mav_binops_1m},
- {"cfrshl32", 0xee000550, 8, ARM_CEXT_MAVERICK, do_mav_triple_4a},
- {"cfrshl64", 0xee000570, 8, ARM_CEXT_MAVERICK, do_mav_triple_4b},
- {"cfsh32", 0xee000500, 6, ARM_CEXT_MAVERICK, do_mav_shift_1},
- {"cfsh64", 0xee200500, 6, ARM_CEXT_MAVERICK, do_mav_shift_2},
- {"cfcmps", 0xee100490, 6, ARM_CEXT_MAVERICK, do_mav_triple_5a},
- {"cfcmpd", 0xee1004b0, 6, ARM_CEXT_MAVERICK, do_mav_triple_5b},
- {"cfcmp32", 0xee100590, 7, ARM_CEXT_MAVERICK, do_mav_triple_5c},
- {"cfcmp64", 0xee1005b0, 7, ARM_CEXT_MAVERICK, do_mav_triple_5d},
- {"cfabss", 0xee300400, 6, ARM_CEXT_MAVERICK, do_mav_binops_1d},
- {"cfabsd", 0xee300420, 6, ARM_CEXT_MAVERICK, do_mav_binops_1e},
- {"cfnegs", 0xee300440, 6, ARM_CEXT_MAVERICK, do_mav_binops_1d},
- {"cfnegd", 0xee300460, 6, ARM_CEXT_MAVERICK, do_mav_binops_1e},
- {"cfadds", 0xee300480, 6, ARM_CEXT_MAVERICK, do_mav_triple_5e},
- {"cfaddd", 0xee3004a0, 6, ARM_CEXT_MAVERICK, do_mav_triple_5f},
- {"cfsubs", 0xee3004c0, 6, ARM_CEXT_MAVERICK, do_mav_triple_5e},
- {"cfsubd", 0xee3004e0, 6, ARM_CEXT_MAVERICK, do_mav_triple_5f},
- {"cfmuls", 0xee100400, 6, ARM_CEXT_MAVERICK, do_mav_triple_5e},
- {"cfmuld", 0xee100420, 6, ARM_CEXT_MAVERICK, do_mav_triple_5f},
- {"cfabs32", 0xee300500, 7, ARM_CEXT_MAVERICK, do_mav_binops_1n},
- {"cfabs64", 0xee300520, 7, ARM_CEXT_MAVERICK, do_mav_binops_1o},
- {"cfneg32", 0xee300540, 7, ARM_CEXT_MAVERICK, do_mav_binops_1n},
- {"cfneg64", 0xee300560, 7, ARM_CEXT_MAVERICK, do_mav_binops_1o},
- {"cfadd32", 0xee300580, 7, ARM_CEXT_MAVERICK, do_mav_triple_5g},
- {"cfadd64", 0xee3005a0, 7, ARM_CEXT_MAVERICK, do_mav_triple_5h},
- {"cfsub32", 0xee3005c0, 7, ARM_CEXT_MAVERICK, do_mav_triple_5g},
- {"cfsub64", 0xee3005e0, 7, ARM_CEXT_MAVERICK, do_mav_triple_5h},
- {"cfmul32", 0xee100500, 7, ARM_CEXT_MAVERICK, do_mav_triple_5g},
- {"cfmul64", 0xee100520, 7, ARM_CEXT_MAVERICK, do_mav_triple_5h},
- {"cfmac32", 0xee100540, 7, ARM_CEXT_MAVERICK, do_mav_triple_5g},
- {"cfmsc32", 0xee100560, 7, ARM_CEXT_MAVERICK, do_mav_triple_5g},
- {"cfmadd32", 0xee000600, 8, ARM_CEXT_MAVERICK, do_mav_quad_6a},
- {"cfmsub32", 0xee100600, 8, ARM_CEXT_MAVERICK, do_mav_quad_6a},
- {"cfmadda32", 0xee200600, 9, ARM_CEXT_MAVERICK, do_mav_quad_6b},
- {"cfmsuba32", 0xee300600, 9, ARM_CEXT_MAVERICK, do_mav_quad_6b},
-};
-/* Iterate over the base tables to create the instruction patterns. */
+/* Start an exception table entry. If idx is nonzero this is an index table
+ entry. */
static void
-build_arm_ops_hsh (void)
+start_unwind_section (const segT text_seg, int idx)
{
- unsigned int i;
- unsigned int j;
- static struct obstack insn_obstack;
-
- obstack_begin (&insn_obstack, 4000);
+ const char * text_name;
+ const char * prefix;
+ const char * prefix_once;
+ const char * group_name;
+ size_t prefix_len;
+ size_t text_len;
+ char * sec_name;
+ size_t sec_name_len;
+ int type;
+ int flags;
+ int linkonce;
- for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
+ if (idx)
{
- const struct asm_opcode *insn = insns + i;
-
- if (insn->cond_offset != 0)
- {
- /* Insn supports conditional execution. Build the varaints
- and insert them in the hash table. */
- for (j = 0; j < sizeof (conds) / sizeof (struct asm_cond); j++)
- {
- unsigned len = strlen (insn->template);
- struct asm_opcode *new;
- char *template;
-
- new = obstack_alloc (&insn_obstack, sizeof (struct asm_opcode));
- /* All condition codes are two characters. */
- template = obstack_alloc (&insn_obstack, len + 3);
-
- strncpy (template, insn->template, insn->cond_offset);
- strcpy (template + insn->cond_offset, conds[j].template);
- if (len > insn->cond_offset)
- strcpy (template + insn->cond_offset + 2,
- insn->template + insn->cond_offset);
- new->template = template;
- new->cond_offset = 0;
- new->variant = insn->variant;
- new->parms = insn->parms;
- new->value = (insn->value & ~COND_MASK) | conds[j].value;
-
- hash_insert (arm_ops_hsh, new->template, (PTR) new);
- }
- }
- /* Finally, insert the unconditional insn in the table directly;
- no need to build a copy. */
- hash_insert (arm_ops_hsh, insn->template, (PTR) insn);
+ prefix = ELF_STRING_ARM_unwind;
+ prefix_once = ELF_STRING_ARM_unwind_once;
+ type = SHT_ARM_EXIDX;
+ }
+ else
+ {
+ prefix = ELF_STRING_ARM_unwind_info;
+ prefix_once = ELF_STRING_ARM_unwind_info_once;
+ type = SHT_PROGBITS;
}
-}
-
-
-static const struct thumb_opcode tinsns[] =
-{
- /* Thumb v1 (ARMv4T). */
- {"adc", 0x4140, 2, ARM_EXT_V4T, do_t_arit},
- {"add", 0x0000, 2, ARM_EXT_V4T, do_t_add},
- {"and", 0x4000, 2, ARM_EXT_V4T, do_t_arit},
- {"asr", 0x0000, 2, ARM_EXT_V4T, do_t_asr},
- {"b", T_OPCODE_BRANCH, 2, ARM_EXT_V4T, do_t_branch12},
- {"beq", 0xd0fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bne", 0xd1fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bcs", 0xd2fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bhs", 0xd2fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bcc", 0xd3fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bul", 0xd3fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"blo", 0xd3fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bmi", 0xd4fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bpl", 0xd5fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bvs", 0xd6fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bvc", 0xd7fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bhi", 0xd8fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bls", 0xd9fe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bge", 0xdafe, 2, ARM_EXT_V4T, do_t_branch9},
- {"blt", 0xdbfe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bgt", 0xdcfe, 2, ARM_EXT_V4T, do_t_branch9},
- {"ble", 0xddfe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bal", 0xdefe, 2, ARM_EXT_V4T, do_t_branch9},
- {"bic", 0x4380, 2, ARM_EXT_V4T, do_t_arit},
- {"bl", 0xf7fffffe, 4, ARM_EXT_V4T, do_t_branch23},
- {"bx", 0x4700, 2, ARM_EXT_V4T, do_t_bx},
- {"cmn", T_OPCODE_CMN, 2, ARM_EXT_V4T, do_t_arit},
- {"cmp", 0x0000, 2, ARM_EXT_V4T, do_t_compare},
- {"eor", 0x4040, 2, ARM_EXT_V4T, do_t_arit},
- {"ldmia", 0xc800, 2, ARM_EXT_V4T, do_t_ldmstm},
- {"ldr", 0x0000, 2, ARM_EXT_V4T, do_t_ldr},
- {"ldrb", 0x0000, 2, ARM_EXT_V4T, do_t_ldrb},
- {"ldrh", 0x0000, 2, ARM_EXT_V4T, do_t_ldrh},
- {"ldrsb", 0x5600, 2, ARM_EXT_V4T, do_t_lds},
- {"ldrsh", 0x5e00, 2, ARM_EXT_V4T, do_t_lds},
- {"ldsb", 0x5600, 2, ARM_EXT_V4T, do_t_lds},
- {"ldsh", 0x5e00, 2, ARM_EXT_V4T, do_t_lds},
- {"lsl", 0x0000, 2, ARM_EXT_V4T, do_t_lsl},
- {"lsr", 0x0000, 2, ARM_EXT_V4T, do_t_lsr},
- {"mov", 0x0000, 2, ARM_EXT_V4T, do_t_mov},
- {"mul", T_OPCODE_MUL, 2, ARM_EXT_V4T, do_t_arit},
- {"mvn", T_OPCODE_MVN, 2, ARM_EXT_V4T, do_t_arit},
- {"neg", T_OPCODE_NEG, 2, ARM_EXT_V4T, do_t_arit},
- {"orr", 0x4300, 2, ARM_EXT_V4T, do_t_arit},
- {"pop", 0xbc00, 2, ARM_EXT_V4T, do_t_push_pop},
- {"push", 0xb400, 2, ARM_EXT_V4T, do_t_push_pop},
- {"ror", 0x41c0, 2, ARM_EXT_V4T, do_t_arit},
- {"sbc", 0x4180, 2, ARM_EXT_V4T, do_t_arit},
- {"stmia", 0xc000, 2, ARM_EXT_V4T, do_t_ldmstm},
- {"str", 0x0000, 2, ARM_EXT_V4T, do_t_str},
- {"strb", 0x0000, 2, ARM_EXT_V4T, do_t_strb},
- {"strh", 0x0000, 2, ARM_EXT_V4T, do_t_strh},
- {"swi", 0xdf00, 2, ARM_EXT_V4T, do_t_swi},
- {"sub", 0x0000, 2, ARM_EXT_V4T, do_t_sub},
- {"tst", T_OPCODE_TST, 2, ARM_EXT_V4T, do_t_arit},
- /* Pseudo ops: */
- {"adr", 0x0000, 2, ARM_EXT_V4T, do_t_adr},
- {"nop", 0x46C0, 2, ARM_EXT_V4T, do_t_nop}, /* mov r8,r8 */
- /* Thumb v2 (ARMv5T). */
- {"blx", 0, 0, ARM_EXT_V5T, do_t_blx},
- {"bkpt", 0xbe00, 2, ARM_EXT_V5T, do_t_bkpt},
-
- /* ARM V6. */
- {"cpsie", 0xb660, 2, ARM_EXT_V6, do_t_cps},
- {"cpsid", 0xb670, 2, ARM_EXT_V6, do_t_cps},
- {"cpy", 0x4600, 2, ARM_EXT_V6, do_t_cpy},
- {"rev", 0xba00, 2, ARM_EXT_V6, do_t_arit},
- {"rev16", 0xba40, 2, ARM_EXT_V6, do_t_arit},
- {"revsh", 0xbac0, 2, ARM_EXT_V6, do_t_arit},
- {"setend", 0xb650, 2, ARM_EXT_V6, do_t_setend},
- {"sxth", 0xb200, 2, ARM_EXT_V6, do_t_arit},
- {"sxtb", 0xb240, 2, ARM_EXT_V6, do_t_arit},
- {"uxth", 0xb280, 2, ARM_EXT_V6, do_t_arit},
- {"uxtb", 0xb2c0, 2, ARM_EXT_V6, do_t_arit},
-
- /* ARM V6K. */
- {"sev", 0xbf40, 2, ARM_EXT_V6K, do_empty},
- {"wfe", 0xbf20, 2, ARM_EXT_V6K, do_empty},
- {"wfi", 0xbf30, 2, ARM_EXT_V6K, do_empty},
- {"yield", 0xbf10, 2, ARM_EXT_V6K, do_empty},
-};
-
-void
-md_begin (void)
-{
- unsigned mach;
- unsigned int i;
- if ( (arm_ops_hsh = hash_new ()) == NULL
- || (arm_tops_hsh = hash_new ()) == NULL
- || (arm_cond_hsh = hash_new ()) == NULL
- || (arm_shift_hsh = hash_new ()) == NULL
- || (arm_psr_hsh = hash_new ()) == NULL)
- as_fatal (_("virtual memory exhausted"));
+ text_name = segment_name (text_seg);
+ if (streq (text_name, ".text"))
+ text_name = "";
- build_arm_ops_hsh ();
- for (i = 0; i < sizeof (tinsns) / sizeof (struct thumb_opcode); i++)
- hash_insert (arm_tops_hsh, tinsns[i].template, (PTR) (tinsns + i));
- for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
- hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
- for (i = 0; i < sizeof (shift_names) / sizeof (struct asm_shift_name); i++)
- hash_insert (arm_shift_hsh, shift_names[i].name, (PTR) (shift_names + i));
- for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
- hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
+ if (strncmp (text_name, ".gnu.linkonce.t.",
+ strlen (".gnu.linkonce.t.")) == 0)
+ {
+ prefix = prefix_once;
+ text_name += strlen (".gnu.linkonce.t.");
+ }
- for (i = (int) REG_TYPE_FIRST; i < (int) REG_TYPE_MAX; i++)
- build_reg_hsh (all_reg_maps + i);
+ prefix_len = strlen (prefix);
+ text_len = strlen (text_name);
+ sec_name_len = prefix_len + text_len;
+ sec_name = xmalloc (sec_name_len + 1);
+ memcpy (sec_name, prefix, prefix_len);
+ memcpy (sec_name + prefix_len, text_name, text_len);
+ sec_name[prefix_len + text_len] = '\0';
- set_constant_flonums ();
+ flags = SHF_ALLOC;
+ linkonce = 0;
+ group_name = 0;
- /* Set the cpu variant based on the command-line options. We prefer
- -mcpu= over -march= if both are set (as for GCC); and we prefer
- -mfpu= over any other way of setting the floating point unit.
- Use of legacy options with new options are faulted. */
- if (legacy_cpu != -1)
+ /* Handle COMDAT group. */
+ if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
{
- if (mcpu_cpu_opt != -1 || march_cpu_opt != -1)
- as_bad (_("use of old and new-style options to set CPU type"));
-
- mcpu_cpu_opt = legacy_cpu;
+ group_name = elf_group_name (text_seg);
+ if (group_name == NULL)
+ {
+ as_bad ("Group section `%s' has no group signature",
+ segment_name (text_seg));
+ ignore_rest_of_line ();
+ return;
+ }
+ flags |= SHF_GROUP;
+ linkonce = 1;
}
- else if (mcpu_cpu_opt == -1)
- mcpu_cpu_opt = march_cpu_opt;
- if (legacy_fpu != -1)
- {
- if (mfpu_opt != -1)
- as_bad (_("use of old and new-style options to set FPU type"));
+ obj_elf_change_section (sec_name, type, flags, 0, group_name, linkonce, 0);
- mfpu_opt = legacy_fpu;
- }
- else if (mfpu_opt == -1)
- {
-#if !(defined (TE_LINUX) || defined (TE_NetBSD) || defined (TE_VXWORKS))
- /* Some environments specify a default FPU. If they don't, infer it
- from the processor. */
- if (mcpu_fpu_opt != -1)
- mfpu_opt = mcpu_fpu_opt;
- else
- mfpu_opt = march_fpu_opt;
-#else
- mfpu_opt = FPU_DEFAULT;
-#endif
- }
+ /* Set the setion link for index tables. */
+ if (idx)
+ elf_linked_to_section (now_seg) = text_seg;
+}
- if (mfpu_opt == -1)
- {
- if (mcpu_cpu_opt == -1)
- mfpu_opt = FPU_DEFAULT;
- else if (mcpu_cpu_opt & ARM_EXT_V5)
- mfpu_opt = FPU_ARCH_VFP_V2;
- else
- mfpu_opt = FPU_ARCH_FPA;
- }
- if (mcpu_cpu_opt == -1)
- mcpu_cpu_opt = CPU_DEFAULT;
+/* Start an unwind table entry. HAVE_DATA is nonzero if we have additional
+ personality routine data. Returns zero, or the index table value for
+ and inline entry. */
- cpu_variant = mcpu_cpu_opt | mfpu_opt;
+static valueT
+create_unwind_entry (int have_data)
+{
+ int size;
+ addressT where;
+ char *ptr;
+ /* The current word of data. */
+ valueT data;
+ /* The number of bytes left in this word. */
+ int n;
-#if defined OBJ_COFF || defined OBJ_ELF
- {
- unsigned int flags = 0;
+ finish_unwind_opcodes ();
-#if defined OBJ_ELF
- flags = meabi_flags;
+ /* Remember the current text section. */
+ unwind.saved_seg = now_seg;
+ unwind.saved_subseg = now_subseg;
- switch (meabi_flags)
- {
- case EF_ARM_EABI_UNKNOWN:
-#endif
- /* Set the flags in the private structure. */
- if (uses_apcs_26) flags |= F_APCS26;
- if (support_interwork) flags |= F_INTERWORK;
- if (uses_apcs_float) flags |= F_APCS_FLOAT;
- if (pic_code) flags |= F_PIC;
- if ((cpu_variant & FPU_ANY) == FPU_NONE
- || (cpu_variant & FPU_ANY) == FPU_ARCH_VFP) /* VFP layout only. */
- flags |= F_SOFT_FLOAT;
+ start_unwind_section (now_seg, 0);
- switch (mfloat_abi_opt)
- {
- case ARM_FLOAT_ABI_SOFT:
- case ARM_FLOAT_ABI_SOFTFP:
- flags |= F_SOFT_FLOAT;
- break;
+ if (unwind.personality_routine == NULL)
+ {
+ if (unwind.personality_index == -2)
+ {
+ if (have_data)
+ as_bad (_("handerdata in cantunwind frame"));
+ return 1; /* EXIDX_CANTUNWIND. */
+ }
- case ARM_FLOAT_ABI_HARD:
- if (flags & F_SOFT_FLOAT)
- as_bad (_("hard-float conflicts with specified fpu"));
- break;
- }
+ /* Use a default personality routine if none is specified. */
+ if (unwind.personality_index == -1)
+ {
+ if (unwind.opcode_count > 3)
+ unwind.personality_index = 1;
+ else
+ unwind.personality_index = 0;
+ }
- /* Using VFP conventions (even if soft-float). */
- if (cpu_variant & FPU_VFP_EXT_NONE)
- flags |= F_VFP_FLOAT;
+ /* Space for the personality routine entry. */
+ if (unwind.personality_index == 0)
+ {
+ if (unwind.opcode_count > 3)
+ as_bad (_("too many unwind opcodes for personality routine 0"));
-#if defined OBJ_ELF
- if (cpu_variant & FPU_ARCH_MAVERICK)
- flags |= EF_ARM_MAVERICK_FLOAT;
- break;
+ if (!have_data)
+ {
+ /* All the data is inline in the index table. */
+ data = 0x80;
+ n = 3;
+ while (unwind.opcode_count > 0)
+ {
+ unwind.opcode_count--;
+ data = (data << 8) | unwind.opcodes[unwind.opcode_count];
+ n--;
+ }
- case EF_ARM_EABI_VER4:
- /* No additional flags to set. */
- break;
+ /* Pad with "finish" opcodes. */
+ while (n--)
+ data = (data << 8) | 0xb0;
- default:
- abort ();
- }
-#endif
- bfd_set_private_flags (stdoutput, flags);
+ return data;
+ }
+ size = 0;
+ }
+ else
+ /* We get two opcodes "free" in the first word. */
+ size = unwind.opcode_count - 2;
+ }
+ else
+ /* An extra byte is required for the opcode count. */
+ size = unwind.opcode_count + 1;
- /* We have run out flags in the COFF header to encode the
- status of ATPCS support, so instead we create a dummy,
- empty, debug section called .arm.atpcs. */
- if (atpcs)
- {
- asection * sec;
+ size = (size + 3) >> 2;
+ if (size > 0xff)
+ as_bad (_("too many unwind opcodes"));
- sec = bfd_make_section (stdoutput, ".arm.atpcs");
+ frag_align (2, 0, 0);
+ record_alignment (now_seg, 2);
+ unwind.table_entry = expr_build_dot ();
- if (sec != NULL)
- {
- bfd_set_section_flags
- (stdoutput, sec, SEC_READONLY | SEC_DEBUGGING /* | SEC_HAS_CONTENTS */);
- bfd_set_section_size (stdoutput, sec, 0);
- bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
- }
- }
- }
-#endif
+ /* Allocate the table entry. */
+ ptr = frag_more ((size << 2) + 4);
+ where = frag_now_fix () - ((size << 2) + 4);
- /* Record the CPU type as well. */
- switch (cpu_variant & ARM_CPU_MASK)
+ switch (unwind.personality_index)
{
- case ARM_2:
- mach = bfd_mach_arm_2;
+ case -1:
+ /* ??? Should this be a PLT generating relocation? */
+ /* Custom personality routine. */
+ fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
+ BFD_RELOC_ARM_PREL31);
+
+ where += 4;
+ ptr += 4;
+
+ /* Set the first byte to the number of additional words. */
+ data = size - 1;
+ n = 3;
break;
- case ARM_3: /* Also ARM_250. */
- mach = bfd_mach_arm_2a;
+ /* ABI defined personality routines. */
+ case 0:
+ /* Three opcodes bytes are packed into the first word. */
+ data = 0x80;
+ n = 3;
break;
- case ARM_6: /* Also ARM_7. */
- mach = bfd_mach_arm_3;
+ case 1:
+ case 2:
+ /* The size and first two opcode bytes go in the first word. */
+ data = ((0x80 + unwind.personality_index) << 8) | size;
+ n = 2;
break;
default:
- mach = bfd_mach_arm_unknown;
- break;
+ /* Should never happen. */
+ abort ();
}
- /* Catch special cases. */
- if (cpu_variant & ARM_CEXT_IWMMXT)
- mach = bfd_mach_arm_iWMMXt;
- else if (cpu_variant & ARM_CEXT_XSCALE)
- mach = bfd_mach_arm_XScale;
- else if (cpu_variant & ARM_CEXT_MAVERICK)
- mach = bfd_mach_arm_ep9312;
- else if (cpu_variant & ARM_EXT_V5E)
- mach = bfd_mach_arm_5TE;
- else if (cpu_variant & ARM_EXT_V5)
- {
- if (cpu_variant & ARM_EXT_V4T)
- mach = bfd_mach_arm_5T;
- else
- mach = bfd_mach_arm_5;
- }
- else if (cpu_variant & ARM_EXT_V4)
+ /* Pack the opcodes into words (MSB first), reversing the list at the same
+ time. */
+ while (unwind.opcode_count > 0)
{
- if (cpu_variant & ARM_EXT_V4T)
- mach = bfd_mach_arm_4T;
- else
- mach = bfd_mach_arm_4;
+ if (n == 0)
+ {
+ md_number_to_chars (ptr, data, 4);
+ ptr += 4;
+ n = 4;
+ data = 0;
+ }
+ unwind.opcode_count--;
+ n--;
+ data = (data << 8) | unwind.opcodes[unwind.opcode_count];
}
- else if (cpu_variant & ARM_EXT_V3M)
- mach = bfd_mach_arm_3M;
-
- bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
-}
-
-/* Turn an integer of n bytes (in val) into a stream of bytes appropriate
- for use in the a.out file, and stores them in the array pointed to by buf.
- This knows about the endian-ness of the target machine and does
- THE RIGHT THING, whatever it is. Possible values for n are 1 (byte)
- 2 (short) and 4 (long) Floating numbers are put out as a series of
- LITTLENUMS (shorts, here at least). */
-
-void
-md_number_to_chars (char * buf, valueT val, int n)
-{
- if (target_big_endian)
- number_to_chars_bigendian (buf, val, n);
- else
- number_to_chars_littleendian (buf, val, n);
-}
-
-static valueT
-md_chars_to_number (char * buf, int n)
-{
- valueT result = 0;
- unsigned char * where = (unsigned char *) buf;
- if (target_big_endian)
+ /* Finish off the last word. */
+ if (n < 4)
{
+ /* Pad with "finish" opcodes. */
while (n--)
- {
- result <<= 8;
- result |= (*where++ & 255);
- }
+ data = (data << 8) | 0xb0;
+
+ md_number_to_chars (ptr, data, 4);
}
- else
+
+ if (!have_data)
{
- while (n--)
- {
- result <<= 8;
- result |= (where[n] & 255);
- }
+ /* Add an empty descriptor if there is no user-specified data. */
+ ptr = frag_more (4);
+ md_number_to_chars (ptr, 0, 4);
}
- return result;
+ return 0;
}
-/* 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. */
+/* Convert REGNAME to a DWARF-2 register number. */
-char *
-md_atof (int type, char * litP, int * sizeP)
+int
+tc_arm_regname_to_dw2regnum (const char *regname)
{
- int prec;
- LITTLENUM_TYPE words[MAX_LITTLENUMS];
- char *t;
- int i;
+ int reg = arm_reg_parse ((char **) &regname, REG_TYPE_RN);
- switch (type)
- {
- case 'f':
- case 'F':
- case 's':
- case 'S':
- prec = 2;
- break;
+ if (reg == FAIL)
+ return -1;
- case 'd':
- case 'D':
- case 'r':
- case 'R':
- prec = 4;
- break;
+ return reg;
+}
- case 'x':
- case 'X':
- prec = 6;
- break;
+/* Initialize the DWARF-2 unwind information for this procedure. */
- case 'p':
- case 'P':
- prec = 6;
- break;
+void
+tc_arm_frame_initial_instructions (void)
+{
+ cfi_add_CFA_def_cfa (REG_SP, 0);
+}
+#endif /* OBJ_ELF */
- default:
- *sizeP = 0;
- return _("bad call to MD_ATOF()");
- }
- t = atof_ieee (input_line_pointer, type, words);
- if (t)
- input_line_pointer = t;
- *sizeP = prec * 2;
-
- if (target_big_endian)
- {
- for (i = 0; i < prec; i++)
- {
- md_number_to_chars (litP, (valueT) words[i], 2);
- litP += 2;
- }
- }
- else
- {
- if (cpu_variant & FPU_ARCH_VFP)
- for (i = prec - 1; i >= 0; i--)
- {
- md_number_to_chars (litP, (valueT) words[i], 2);
- litP += 2;
- }
- 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], 2);
- md_number_to_chars (litP + 2, (valueT) words[i], 2);
- litP += 4;
- }
- }
-
- return 0;
-}
+/* MD interface: Symbol and relocation handling. */
/* The knowledge of the PC's pipeline offset is built into the insns
- themselves. */
+ themselves. */
long
md_pcrel_from (fixS * fixP)
@@ -11629,37 +9878,32 @@ md_pcrel_from (fixS * fixP)
&& fixP->fx_subsy == NULL)
return 0;
- if (fixP->fx_pcrel && (fixP->fx_r_type == BFD_RELOC_ARM_THUMB_ADD))
+ /* PC relative addressing on the Thumb is slightly odd as the bottom
+ two bits of the PC are forced to zero for the calculation. This
+ happens *after* application of the pipeline offset. However,
+ Thumb adrl already adjusts for this, so we need not do it again. */
+ switch (fixP->fx_r_type)
{
- /* PC relative addressing on the Thumb is slightly odd
- as the bottom two bits of the PC are forced to zero
- for the calculation. */
+ case BFD_RELOC_ARM_THUMB_ADD:
return (fixP->fx_where + fixP->fx_frag->fr_address) & ~3;
+
+ case BFD_RELOC_ARM_THUMB_OFFSET:
+ case BFD_RELOC_ARM_T32_OFFSET_IMM:
+ return (fixP->fx_where + fixP->fx_frag->fr_address + 4) & ~3;
+
+ default:
+ break;
}
#ifdef TE_WINCE
/* The pattern was adjusted to accommodate CE's off-by-one fixups,
- so we un-adjust here to compensate for the accommodation. */
+ so we un-adjust here to compensate for the accommodation. */
return fixP->fx_where + fixP->fx_frag->fr_address + 8;
#else
return fixP->fx_where + fixP->fx_frag->fr_address;
#endif
}
-/* Round up a section size to the appropriate boundary. */
-
-valueT
-md_section_align (segT segment ATTRIBUTE_UNUSED,
- valueT size)
-{
-#ifdef OBJ_ELF
- return size;
-#else
- /* Round all sects to multiple of 4. */
- return (size + 3) & ~3;
-#endif
-}
-
/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
Otherwise we have no need to default values of symbols. */
@@ -11686,18 +9930,156 @@ md_undefined_symbol (char * name ATTRIBUTE_UNUSED)
return 0;
}
+/* Subroutine of md_apply_fix3. Check to see if an immediate can be
+ computed as two separate immediate values, added together. We
+ already know that this value cannot be computed by just one ARM
+ instruction. */
+
+static unsigned int
+validate_immediate_twopart (unsigned int val,
+ unsigned int * highpart)
+{
+ unsigned int a;
+ unsigned int i;
+
+ for (i = 0; i < 32; i += 2)
+ if (((a = rotate_left (val, i)) & 0xff) != 0)
+ {
+ if (a & 0xff00)
+ {
+ if (a & ~ 0xffff)
+ continue;
+ * highpart = (a >> 8) | ((i + 24) << 7);
+ }
+ else if (a & 0xff0000)
+ {
+ if (a & 0xff000000)
+ continue;
+ * highpart = (a >> 16) | ((i + 16) << 7);
+ }
+ else
+ {
+ assert (a & 0xff000000);
+ * highpart = (a >> 24) | ((i + 8) << 7);
+ }
+
+ return (a & 0xff) | (i << 7);
+ }
+
+ return FAIL;
+}
+
+static int
+validate_offset_imm (unsigned int val, int hwse)
+{
+ if ((hwse && val > 255) || val > 4095)
+ return FAIL;
+ return val;
+}
+
+/* Subroutine of md_apply_fix3. Do those data_ops which can take a
+ negative immediate constant by altering the instruction. A bit of
+ a hack really.
+ MOV <-> MVN
+ AND <-> BIC
+ ADC <-> SBC
+ by inverting the second operand, and
+ ADD <-> SUB
+ CMP <-> CMN
+ by negating the second operand. */
+
+static int
+negate_data_op (unsigned long * instruction,
+ unsigned long value)
+{
+ int op, new_inst;
+ unsigned long negated, inverted;
+
+ negated = encode_arm_immediate (-value);
+ inverted = encode_arm_immediate (~value);
+
+ op = (*instruction >> DATA_OP_SHIFT) & 0xf;
+ switch (op)
+ {
+ /* First negates. */
+ case OPCODE_SUB: /* ADD <-> SUB */
+ new_inst = OPCODE_ADD;
+ value = negated;
+ break;
+
+ case OPCODE_ADD:
+ new_inst = OPCODE_SUB;
+ value = negated;
+ break;
+
+ case OPCODE_CMP: /* CMP <-> CMN */
+ new_inst = OPCODE_CMN;
+ value = negated;
+ break;
+
+ case OPCODE_CMN:
+ new_inst = OPCODE_CMP;
+ value = negated;
+ break;
+
+ /* Now Inverted ops. */
+ case OPCODE_MOV: /* MOV <-> MVN */
+ new_inst = OPCODE_MVN;
+ value = inverted;
+ break;
+
+ case OPCODE_MVN:
+ new_inst = OPCODE_MOV;
+ value = inverted;
+ break;
+
+ case OPCODE_AND: /* AND <-> BIC */
+ new_inst = OPCODE_BIC;
+ value = inverted;
+ break;
+
+ case OPCODE_BIC:
+ new_inst = OPCODE_AND;
+ value = inverted;
+ break;
+
+ case OPCODE_ADC: /* ADC <-> SBC */
+ new_inst = OPCODE_SBC;
+ value = inverted;
+ break;
+
+ case OPCODE_SBC:
+ new_inst = OPCODE_ADC;
+ value = inverted;
+ break;
+
+ /* We cannot do anything. */
+ default:
+ return FAIL;
+ }
+
+ if (value == (unsigned) FAIL)
+ return FAIL;
+
+ *instruction &= OPCODE_MASK;
+ *instruction |= new_inst << DATA_OP_SHIFT;
+ return value;
+}
+
void
-md_apply_fix3 (fixS * fixP,
+md_apply_fix3 (fixS * fixP,
valueT * valP,
- segT seg)
-{
- offsetT value = * valP;
- offsetT newval;
- unsigned int newimm;
- unsigned long temp;
- int sign;
- char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
- arm_fix_data * arm_data = (arm_fix_data *) fixP->tc_fix_data;
+ segT seg)
+{
+ offsetT value = * valP;
+ offsetT newval;
+ unsigned int newimm;
+ unsigned long temp;
+ int sign;
+ char * buf = fixP->fx_where + fixP->fx_frag->fr_literal;
+ /* The double cast here prevents warnings about converting a pointer
+ to an integer of different size. We know the value is 0, 1, or 2. */
+ int fix_is_thumb = (int) (size_t) fixP->tc_fix_data;
assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
@@ -11707,7 +10089,7 @@ md_apply_fix3 (fixS * fixP,
/* If this symbol is in a different section then we need to leave it for
the linker to deal with. Unfortunately, md_pcrel_from can't tell,
- so we have to undo it's effects here. */
+ so we have to undo its effects here. */
if (fixP->fx_pcrel)
{
if (fixP->fx_addsy != NULL
@@ -11725,7 +10107,7 @@ md_apply_fix3 (fixS * fixP,
/* This will need to go in the object file. */
fixP->fx_done = 0;
break;
-
+
case BFD_RELOC_ARM_IMMEDIATE:
/* We claim that this fixup has been processed here,
even if in fact we generate an error because we do
@@ -11741,7 +10123,7 @@ md_apply_fix3 (fixS * fixP,
break;
}
- newimm = validate_immediate (value);
+ newimm = encode_arm_immediate (value);
temp = md_chars_to_number (buf, INSN_SIZE);
/* If the instruction will fail, see if we can fix things up by
@@ -11764,20 +10146,20 @@ md_apply_fix3 (fixS * fixP,
unsigned int highpart = 0;
unsigned int newinsn = 0xe1a00000; /* nop. */
- newimm = validate_immediate (value);
+ newimm = encode_arm_immediate (value);
temp = md_chars_to_number (buf, INSN_SIZE);
/* If the instruction will fail, see if we can fix things up by
- changing the opcode. */
+ changing the opcode. */
if (newimm == (unsigned int) FAIL
&& (newimm = negate_data_op (& temp, value)) == (unsigned int) FAIL)
{
/* No ? OK - try using two ADD instructions to generate
- the value. */
+ the value. */
newimm = validate_immediate_twopart (value, & highpart);
/* Yes - then make sure that the second instruction is
- also an add. */
+ also an add. */
if (newimm != (unsigned int) FAIL)
newinsn = temp;
/* Still No ? Try using a negated value. */
@@ -11809,6 +10191,7 @@ md_apply_fix3 (fixS * fixP,
break;
case BFD_RELOC_ARM_OFFSET_IMM:
+ case BFD_RELOC_ARM_LITERAL:
sign = value >= 0;
if (value < 0)
@@ -11816,9 +10199,13 @@ md_apply_fix3 (fixS * fixP,
if (validate_offset_imm (value, 0) == FAIL)
{
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("bad immediate value for offset (%ld)"),
- (long) value);
+ if (fixP->fx_r_type == BFD_RELOC_ARM_LITERAL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid literal constant: pool needs to be closer"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for offset (%ld)"),
+ (long) value);
break;
}
@@ -11852,23 +10239,131 @@ md_apply_fix3 (fixS * fixP,
md_number_to_chars (buf, newval, INSN_SIZE);
break;
- case BFD_RELOC_ARM_LITERAL:
- sign = value >= 0;
+ case BFD_RELOC_ARM_T32_OFFSET_U8:
+ if (value < 0 || value > 1020 || value % 4 != 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for offset (%ld)"), (long) value);
+ value /= 4;
- if (value < 0)
- value = - value;
+ newval = md_chars_to_number (buf+2, THUMB_SIZE);
+ newval &= 0xff00;
+ newval |= value;
+ md_number_to_chars (buf+2, newval, THUMB_SIZE);
+ break;
- if (validate_offset_imm (value, 0) == FAIL)
+ case BFD_RELOC_ARM_T32_OFFSET_IMM:
+ /* This is a complicated relocation used for all varieties of Thumb32
+ load/store instruction with immediate offset:
+
+ 1110 100P u1WL NNNN XXXX YYYY iiii iiii - +/-(U) pre/post(P) 8-bit,
+ *4, optional writeback(W)
+ (doubleword load/store)
+
+ 1111 100S uTTL 1111 XXXX iiii iiii iiii - +/-(U) 12-bit PC-rel
+ 1111 100S 0TTL NNNN XXXX 1Pu1 iiii iiii - +/-(U) pre/post(P) 8-bit
+ 1111 100S 0TTL NNNN XXXX 1110 iiii iiii - positive 8-bit (T instruction)
+ 1111 100S 1TTL NNNN XXXX iiii iiii iiii - positive 12-bit
+ 1111 100S 0TTL NNNN XXXX 1100 iiii iiii - negative 8-bit
+
+ Uppercase letters indicate bits that are already encoded at
+ this point. Lowercase letters are our problem. For the
+ second block of instructions, the secondary opcode nybble
+ (bits 8..11) is present, and bit 23 is zero, even if this is
+ a PC-relative operation. */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval <<= 16;
+ newval |= md_chars_to_number (buf+THUMB_SIZE, THUMB_SIZE);
+
+ if ((newval & 0xf0000000) == 0xe0000000)
{
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid literal constant: pool needs to be closer"));
- break;
+ /* Doubleword load/store: 8-bit offset, scaled by 4. */
+ if (value >= 0)
+ newval |= (1 << 23);
+ else
+ value = -value;
+ if (value % 4 != 0)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset not a multiple of 4"));
+ break;
+ }
+ value /= 4;
+ if (value >= 0xff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xff;
+ }
+ else if ((newval & 0x000f0000) == 0x000f0000)
+ {
+ /* PC-relative, 12-bit offset. */
+ if (value >= 0)
+ newval |= (1 << 23);
+ else
+ value = -value;
+ if (value >= 0xfff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xfff;
+ }
+ else if ((newval & 0x00000100) == 0x00000100)
+ {
+ /* Writeback: 8-bit, +/- offset. */
+ if (value >= 0)
+ newval |= (1 << 9);
+ else
+ value = -value;
+ if (value >= 0xff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xff;
+ }
+ else if ((newval & 0x00000f00) == 0x00000e00)
+ {
+ /* T-instruction: positive 8-bit offset. */
+ if (value < 0 || value >= 0xff)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~0xff;
+ newval |= value;
+ }
+ else
+ {
+ /* Positive 12-bit or negative 8-bit offset. */
+ int limit;
+ if (value >= 0)
+ {
+ newval |= (1 << 23);
+ limit = 0xfff;
+ }
+ else
+ {
+ value = -value;
+ limit = 0xff;
+ }
+ if (value > limit)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("offset out of range"));
+ break;
+ }
+ newval &= ~limit;
}
- newval = md_chars_to_number (buf, INSN_SIZE);
- newval &= 0xff7ff000;
- newval |= value | (sign ? INDEX_UP : 0);
- md_number_to_chars (buf, newval, INSN_SIZE);
+ newval |= value;
+ md_number_to_chars (buf, (newval >> 16) & 0xffff, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval & 0xffff, THUMB_SIZE);
break;
case BFD_RELOC_ARM_SHIFT_IMM:
@@ -11883,7 +10378,7 @@ md_apply_fix3 (fixS * fixP,
}
if (value == 0)
- /* Shifts of zero must be done as lsl. */
+ /* Shifts of zero must be done as lsl. */
newval &= ~0x60;
else if (value == 32)
value = 0;
@@ -11892,6 +10387,45 @@ md_apply_fix3 (fixS * fixP,
md_number_to_chars (buf, newval, INSN_SIZE);
break;
+ case BFD_RELOC_ARM_T32_IMMEDIATE:
+ /* We claim that this fixup has been processed here,
+ even if in fact we generate an error because we do
+ not have a reloc for it, so tc_gen_reloc will reject it. */
+ fixP->fx_done = 1;
+
+ if (fixP->fx_addsy
+ && ! S_IS_DEFINED (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("undefined symbol %s used as an immediate value"),
+ S_GET_NAME (fixP->fx_addsy));
+ break;
+ }
+
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval <<= 16;
+ newval |= md_chars_to_number (buf+2, THUMB_SIZE);
+
+ newimm = encode_thumb32_immediate (value);
+
+ /* FUTURE: Implement analogue of negate_data_op for T32. */
+ if (newimm == (unsigned int)FAIL)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid constant (%lx) after fixup"),
+ (unsigned long) value);
+ break;
+ }
+
+ newval &= 0xfbff8f00;
+ newval |= (newimm & 0x800) << 15;
+ newval |= (newimm & 0x700) << 4;
+ newval |= (newimm & 0x0ff);
+
+ md_number_to_chars (buf, (valueT) ((newval >> 16) & 0xffff), THUMB_SIZE);
+ md_number_to_chars (buf+2, (valueT) (newval & 0xffff), THUMB_SIZE);
+ break;
+
case BFD_RELOC_ARM_SMI:
if (((unsigned long) value) > 0xffff)
as_bad_where (fixP->fx_file, fixP->fx_line,
@@ -11902,7 +10436,7 @@ md_apply_fix3 (fixS * fixP,
break;
case BFD_RELOC_ARM_SWI:
- if (arm_data->thumb_mode)
+ if (fix_is_thumb)
{
if (((unsigned long) value) > 0xff)
as_bad_where (fixP->fx_file, fixP->fx_line,
@@ -11941,16 +10475,16 @@ md_apply_fix3 (fixS * fixP,
#endif
/* We are going to store value (shifted right by two) in the
- instruction, in a 24 bit, signed field. Thus we need to check
+ instruction, in a 24 bit, signed field Thus we need to check
that none of the top 8 bits of the shifted value (top 7 bits of
- the unshifted, unsigned value) are set, or that they are all set. */
+ the unshifted, unsigned value) are set, or that they are all set. */
if ((value & ~ ((offsetT) 0x1ffffff)) != 0
&& ((value & ~ ((offsetT) 0x1ffffff)) != ~ ((offsetT) 0x1ffffff)))
{
#ifdef OBJ_ELF
/* Normally we would be stuck at this point, since we cannot store
the absolute address that is the destination of the branch in the
- 24 bits of the branch instruction. If however, we happen to know
+ 24 bits of the branch instruction. If however, we happen to know
that the destination of the branch is in the same section as the
branch instruction itself, then we can compute the relocation for
ourselves and not have to bother the linker with it.
@@ -11966,7 +10500,7 @@ md_apply_fix3 (fixS * fixP,
/* Permit a backward branch provided that enough bits
are set. Allow a forwards branch, provided that
- enough bits are clear. */
+ enough bits are clear. */
if ( (value & ~ ((offsetT) 0x1ffffff)) == ~ ((offsetT) 0x1ffffff)
|| (value & ~ ((offsetT) 0x1ffffff)) == 0)
fixP->fx_done = 1;
@@ -12031,7 +10565,32 @@ md_apply_fix3 (fixS * fixP,
}
break;
- case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
+ case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CZB */
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ {
+ addressT diff = ((newval & 0x00f8) >> 2) | (newval & 0x0200) >> 3;
+ /* This one does not have the offset encoded in the pattern. */
+ value = value + diff - 4;
+ /* CZB can only branch forward. */
+ if (value & ~0x7e)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
+
+ newval &= 0xfd07;
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ }
+ else
+ newval |= ((value & 0x2e) << 2) | ((value & 0x40) << 3);
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ break;
+
+ case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
newval = md_chars_to_number (buf, THUMB_SIZE);
{
addressT diff = (newval & 0xff) << 1;
@@ -12081,13 +10640,61 @@ md_apply_fix3 (fixS * fixP,
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
+ case BFD_RELOC_THUMB_PCREL_BRANCH20:
+ {
+ offsetT newval2;
+ addressT diff, S, J1, J2, lo, hi;
+
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+
+ S = !(newval & 0x0400); /* flipped - 0=negative */
+ hi = (newval & 0x003f);
+ J1 = (newval2 & 0x2000) >> 13;
+ J2 = (newval2 & 0x0800) >> 11;
+ lo = (newval2 & 0x07ff);
+
+ diff = ((S << 20) | (J2 << 19) | (J1 << 18) | (hi << 12) | (lo << 1));
+ diff -= (1 << 20); /* sign extend */
+ value += diff;
+
+ if ((value & ~0x1fffff) && ((value & ~0x1fffff) != ~0x1fffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("conditional branch out of range"));
+
+ newval = newval & 0xfbc0;
+ newval2 = newval2 & 0xd000;
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ }
+ else
+ {
+ S = (value & 0x00100000) >> 20;
+ J2 = (value & 0x00080000) >> 19;
+ J1 = (value & 0x00040000) >> 18;
+ hi = (value & 0x0003f000) >> 12;
+ lo = (value & 0x00000ffe) >> 1;
+
+ newval = newval | (S << 10) | hi;
+ newval2 = newval2 | (J1 << 13) | (J2 << 11) | lo;
+ }
+
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+ }
+ break;
+
case BFD_RELOC_THUMB_PCREL_BLX:
case BFD_RELOC_THUMB_PCREL_BRANCH23:
{
offsetT newval2;
addressT diff;
- newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval = md_chars_to_number (buf, THUMB_SIZE);
newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
diff = ((newval & 0x7ff) << 12) | ((newval2 & 0x7ff) << 1);
if (diff & 0x400000)
@@ -12141,6 +10748,60 @@ md_apply_fix3 (fixS * fixP,
#endif
break;
+ case BFD_RELOC_THUMB_PCREL_BRANCH25:
+ {
+ offsetT newval2;
+ addressT diff, S, I1, I2, lo, hi;
+
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
+
+ S = (newval & 0x0400) >> 10;
+ hi = (newval & 0x03ff);
+ I1 = (newval2 & 0x2000) >> 13;
+ I2 = (newval2 & 0x0800) >> 11;
+ lo = (newval2 & 0x07ff);
+
+ I1 = !(I1 ^ S);
+ I2 = !(I2 ^ S);
+ S = !S;
+
+ diff = ((S << 24) | (I1 << 23) | (I2 << 22) | (hi << 12) | (lo << 1));
+ diff -= (1 << 24); /* sign extend */
+ value += diff;
+
+ if ((value & ~0x1ffffff) && ((value & ~0x1ffffff) != ~0x1ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
+
+ newval = newval & 0xf800;
+ newval2 = newval2 & 0xd000;
+ if (seg->use_rela_p && !fixP->fx_done)
+ {
+#ifdef OBJ_ELF
+ fixP->fx_offset = value;
+#endif
+ fixP->fx_addnumber = value;
+ }
+ else
+ {
+ S = (value & 0x01000000) >> 24;
+ I1 = (value & 0x00800000) >> 23;
+ I2 = (value & 0x00400000) >> 22;
+ hi = (value & 0x003ff000) >> 12;
+ lo = (value & 0x00000ffe) >> 1;
+
+ I1 = !(I1 ^ S);
+ I2 = !(I2 ^ S);
+
+ newval = newval | (S << 10) | hi;
+ newval2 = newval2 | (I1 << 13) | (I2 << 11) | lo;
+ }
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ md_number_to_chars (buf + THUMB_SIZE, newval2, THUMB_SIZE);
+ }
+ break;
+
case BFD_RELOC_16:
if (seg->use_rela_p && !fixP->fx_done)
break;
@@ -12156,13 +10817,13 @@ md_apply_fix3 (fixS * fixP,
break;
#ifdef OBJ_ELF
- case BFD_RELOC_ARM_TLS_GD32:
- case BFD_RELOC_ARM_TLS_LE32:
- case BFD_RELOC_ARM_TLS_IE32:
- case BFD_RELOC_ARM_TLS_LDM32:
- case BFD_RELOC_ARM_TLS_LDO32:
- S_SET_THREAD_LOCAL (fixP->fx_addsy);
- /* fall through */
+ case BFD_RELOC_ARM_TLS_GD32:
+ case BFD_RELOC_ARM_TLS_LE32:
+ case BFD_RELOC_ARM_TLS_IE32:
+ case BFD_RELOC_ARM_TLS_LDM32:
+ case BFD_RELOC_ARM_TLS_LDO32:
+ S_SET_THREAD_LOCAL (fixP->fx_addsy);
+ /* fall through */
case BFD_RELOC_ARM_GOT32:
case BFD_RELOC_ARM_GOTOFF:
@@ -12213,28 +10874,25 @@ md_apply_fix3 (fixS * fixP,
#endif
case BFD_RELOC_ARM_CP_OFF_IMM:
- sign = value >= 0;
if (value < -1023 || value > 1023 || (value & 3))
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("illegal value for co-processor offset"));
+ _("co-processor offset out of range"));
+ cp_off_common:
+ sign = value >= 0;
if (value < 0)
value = -value;
newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= ~WRITE_BACK;
md_number_to_chars (buf, newval, INSN_SIZE);
break;
case BFD_RELOC_ARM_CP_OFF_IMM_S2:
- sign = value >= 0;
if (value < -255 || value > 255)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Illegal value for co-processor offset"));
- if (value < 0)
- value = -value;
- newval = md_chars_to_number (buf, INSN_SIZE) & 0xff7fff00;
- newval |= value | (sign ? INDEX_UP : 0);
- md_number_to_chars (buf, newval , INSN_SIZE);
- break;
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("co-processor offset out of range"));
+ goto cp_off_common;
case BFD_RELOC_ARM_THUMB_OFFSET:
newval = md_chars_to_number (buf, THUMB_SIZE);
@@ -12245,24 +10903,20 @@ md_apply_fix3 (fixS * fixP,
{
case 4: /* PC load. */
/* Thumb PC loads are somewhat odd, bit 1 of the PC is
- forced to zero for these loads, so we will need to round
- up the offset if the instruction address is not word
- aligned (since the final address produced must be, and
- we can only describe word-aligned immediate offsets). */
-
- if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
+ forced to zero for these loads; md_pcrel_from has already
+ compensated for this. */
+ if (value & 3)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid offset, target not word aligned (0x%08X)"),
- (unsigned int) (fixP->fx_frag->fr_address
- + fixP->fx_where + value));
+ _("invalid offset, target not word aligned (0x%08lX)"),
+ (((unsigned int) fixP->fx_frag->fr_address
+ + (unsigned int) fixP->fx_where) & ~3) + value);
- if ((value + 2) & ~0x3fe)
+ if (value & ~0x3fc)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid offset, value too big (0x%08lX)"),
(long) value);
- /* Round up, since pc will be rounded down. */
- newval |= (value + 2) >> 2;
+ newval |= value >> 2;
break;
case 9: /* SP load/store. */
@@ -12289,7 +10943,7 @@ md_apply_fix3 (fixS * fixP,
newval |= value << 6;
break;
- case 8: /* Halfword load/store. */
+ case 8: /* Halfword load/store. */
if (value & ~0x3e)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid offset, value too big (0x%08lX)"),
@@ -12308,15 +10962,15 @@ md_apply_fix3 (fixS * fixP,
case BFD_RELOC_ARM_THUMB_ADD:
/* This is a complicated relocation, since we use it for all of
- the following immediate relocations:
+ the following immediate relocations:
3bit ADD/SUB
8bit ADD/SUB
9bit ADD/SUB SP word-aligned
10bit ADD PC/SP word-aligned
- The type of instruction being processed is encoded in the
- instruction field:
+ The type of instruction being processed is encoded in the
+ instruction field:
0x8000 SUB
0x00F0 Rd
@@ -12326,7 +10980,24 @@ md_apply_fix3 (fixS * fixP,
{
int rd = (newval >> 4) & 0xf;
int rs = newval & 0xf;
- int subtract = newval & 0x8000;
+ int subtract = !!(newval & 0x8000);
+
+ /* Check for HI regs, only very restricted cases allowed:
+ Adjusting SP, and using PC or SP to get an address. */
+ if ((rd > 7 && (rd != REG_SP || rs != REG_SP))
+ || (rs > 7 && rs != REG_SP && rs != REG_PC))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid Hi register with immediate"));
+
+ /* If value is negative, choose the opposite instruction. */
+ if (value < 0)
+ {
+ value = -value;
+ subtract = !subtract;
+ if (value < 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value out of range"));
+ }
if (rd == REG_SP)
{
@@ -12338,8 +11009,7 @@ md_apply_fix3 (fixS * fixP,
}
else if (rs == REG_PC || rs == REG_SP)
{
- if (subtract ||
- value & ~0x3fc)
+ if (subtract || value & ~0x3fc)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid immediate for address calculation (value = 0x%08lX)"),
(unsigned long) value);
@@ -12351,7 +11021,7 @@ md_apply_fix3 (fixS * fixP,
{
if (value & ~0xff)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid 8bit immediate"));
+ _("immediate value out of range"));
newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
newval |= (rd << 8) | value;
}
@@ -12359,7 +11029,7 @@ md_apply_fix3 (fixS * fixP,
{
if (value & ~0x7)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid 3bit immediate"));
+ _("immediate value out of range"));
newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
newval |= rd | (rs << 3) | (value << 6);
}
@@ -12369,29 +11039,27 @@ md_apply_fix3 (fixS * fixP,
case BFD_RELOC_ARM_THUMB_IMM:
newval = md_chars_to_number (buf, THUMB_SIZE);
- switch (newval >> 11)
- {
- case 0x04: /* 8bit immediate MOV. */
- case 0x05: /* 8bit immediate CMP. */
- if (value < 0 || value > 255)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid immediate: %ld is too large"),
- (long) value);
- newval |= value;
- break;
-
- default:
- abort ();
- }
+ if (value < 0 || value > 255)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid immediate: %ld is too large"),
+ (long) value);
+ newval |= value;
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
case BFD_RELOC_ARM_THUMB_SHIFT:
- /* 5bit shift value (0..31). */
- if (value < 0 || value > 31)
+ /* 5bit shift value (0..32). LSL cannot take 32. */
+ newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf83f;
+ temp = newval & 0xf800;
+ if (value < 0 || value > 32 || (value == 32 && temp == T_OPCODE_LSL_I))
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("illegal Thumb shift value: %ld"), (long) value);
- newval = md_chars_to_number (buf, THUMB_SIZE) & 0xf03f;
+ _("invalid shift value: %ld"), (long) value);
+ /* Shifts of zero must be encoded as LSL. */
+ if (value == 0)
+ newval = (newval & 0x003f) | T_OPCODE_LSL_I;
+ /* Shifts of 32 are encoded as zero. */
+ else if (value == 32)
+ value = 0;
newval |= value << 6;
md_number_to_chars (buf, newval, THUMB_SIZE);
break;
@@ -12413,7 +11081,7 @@ md_apply_fix3 (fixS * fixP,
arelent *
tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
- fixS * fixp)
+ fixS * fixp)
{
arelent * reloc;
bfd_reloc_code_real_type code;
@@ -12461,9 +11129,12 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
case BFD_RELOC_ARM_PCREL_BRANCH:
case BFD_RELOC_ARM_PCREL_BLX:
case BFD_RELOC_RVA:
+ case BFD_RELOC_THUMB_PCREL_BRANCH7:
case BFD_RELOC_THUMB_PCREL_BRANCH9:
case BFD_RELOC_THUMB_PCREL_BRANCH12:
+ case BFD_RELOC_THUMB_PCREL_BRANCH20:
case BFD_RELOC_THUMB_PCREL_BRANCH23:
+ case BFD_RELOC_THUMB_PCREL_BRANCH25:
case BFD_RELOC_THUMB_PCREL_BLX:
case BFD_RELOC_VTABLE_ENTRY:
case BFD_RELOC_VTABLE_INHERIT:
@@ -12495,7 +11166,7 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
case BFD_RELOC_ARM_TLS_GD32:
case BFD_RELOC_ARM_TLS_IE32:
case BFD_RELOC_ARM_TLS_LDM32:
- /* BFD will include the symbol's address in the addend.
+ /* BFD will include the symbol's address in the addend.
But we don't want that, so subtract it out again here. */
if (!S_IS_COMMON (fixp->fx_addsy))
reloc->addend -= (*reloc->sym_ptr_ptr)->value;
@@ -12534,18 +11205,18 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
switch (fixp->fx_r_type)
{
- case BFD_RELOC_NONE: type = "NONE"; break;
+ case BFD_RELOC_NONE: type = "NONE"; break;
case BFD_RELOC_ARM_OFFSET_IMM8: type = "OFFSET_IMM8"; break;
- case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break;
- case BFD_RELOC_ARM_SMI: type = "SMI"; break;
- case BFD_RELOC_ARM_SWI: type = "SWI"; break;
- case BFD_RELOC_ARM_MULTI: type = "MULTI"; break;
- case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break;
- case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break;
+ case BFD_RELOC_ARM_SHIFT_IMM: type = "SHIFT_IMM"; break;
+ case BFD_RELOC_ARM_SMI: type = "SMI"; break;
+ case BFD_RELOC_ARM_SWI: type = "SWI"; break;
+ case BFD_RELOC_ARM_MULTI: type = "MULTI"; break;
+ case BFD_RELOC_ARM_CP_OFF_IMM: type = "CP_OFF_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_ADD: type = "THUMB_ADD"; break;
case BFD_RELOC_ARM_THUMB_SHIFT: type = "THUMB_SHIFT"; break;
- case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break;
+ case BFD_RELOC_ARM_THUMB_IMM: type = "THUMB_IMM"; break;
case BFD_RELOC_ARM_THUMB_OFFSET: type = "THUMB_OFFSET"; break;
- default: type = _("<unknown>"); break;
+ default: type = _("<unknown>"); break;
}
as_bad_where (fixp->fx_file, fixp->fx_line,
_("cannot represent %s relocation in this object file format"),
@@ -12582,185 +11253,482 @@ tc_gen_reloc (asection * section ATTRIBUTE_UNUSED,
return reloc;
}
-int
-md_estimate_size_before_relax (fragS * fragP ATTRIBUTE_UNUSED,
- segT segtype ATTRIBUTE_UNUSED)
-{
- as_fatal (_("md_estimate_size_before_relax\n"));
- return 1;
-}
-
-/* We need to be able to fix up arbitrary expressions in some statements.
- This is so that we can handle symbols that are an arbitrary distance from
- the pc. The most common cases are of the form ((+/-sym -/+ . - 8) & mask),
- which returns part of an address in a form which will be valid for
- a data instruction. We do this by pushing the expression into a symbol
- in the expr_section, and creating a fix for that. */
+/* This fix_new is called by cons via TC_CONS_FIX_NEW. */
-static void
-fix_new_arm (fragS * frag,
- int where,
- short int size,
- expressionS * exp,
- int pc_rel,
- int reloc)
+void
+cons_fix_new_arm (fragS * frag,
+ int where,
+ int size,
+ expressionS * exp)
{
- fixS * new_fix;
- arm_fix_data * arm_data;
+ bfd_reloc_code_real_type type;
+ int pcrel = 0;
- switch (exp->X_op)
+ /* Pick a reloc.
+ FIXME: @@ Should look at CPU word size. */
+ switch (size)
{
- case O_constant:
- case O_symbol:
- case O_add:
- case O_subtract:
- new_fix = fix_new_exp (frag, where, size, exp, pc_rel, reloc);
+ case 1:
+ type = BFD_RELOC_8;
break;
-
+ case 2:
+ type = BFD_RELOC_16;
+ break;
+ case 4:
default:
- new_fix = fix_new (frag, where, size, make_expr_symbol (exp), 0,
- pc_rel, reloc);
+ type = BFD_RELOC_32;
+ break;
+ case 8:
+ type = BFD_RELOC_64;
break;
}
- /* Mark whether the fix is to a THUMB instruction, or an ARM
- instruction. */
- arm_data = obstack_alloc (& notes, sizeof (arm_fix_data));
- new_fix->tc_fix_data = (PTR) arm_data;
- arm_data->thumb_mode = thumb_mode;
+ fix_new_exp (frag, where, (int) size, exp, pcrel, type);
}
-static void
-output_inst (const char * str)
+#if defined OBJ_COFF || defined OBJ_ELF
+void
+arm_validate_fix (fixS * fixP)
{
- char * to = NULL;
-
- if (inst.error)
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
+ && fixP->fx_addsy != NULL
+ && S_IS_DEFINED (fixP->fx_addsy)
+ && ! THUMB_IS_FUNC (fixP->fx_addsy))
{
- as_bad ("%s -- `%s'", inst.error, str);
- return;
+ fixP->fx_addsy = find_real_start (fixP->fx_addsy);
}
+}
+#endif
- to = frag_more (inst.size);
+int
+arm_force_relocation (struct fix * fixp)
+{
+#if defined (OBJ_COFF) && defined (TE_PE)
+ if (fixp->fx_r_type == BFD_RELOC_RVA)
+ return 1;
+#endif
+#ifdef OBJ_ELF
+ if (fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
+ || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
+ || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX
+ || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23)
+ return 1;
+#endif
- if (thumb_mode && (inst.size > THUMB_SIZE))
- {
- assert (inst.size == (2 * THUMB_SIZE));
- md_number_to_chars (to, inst.instruction >> 16, THUMB_SIZE);
- md_number_to_chars (to + THUMB_SIZE, inst.instruction, THUMB_SIZE);
- }
- else if (inst.size > INSN_SIZE)
- {
- assert (inst.size == (2 * INSN_SIZE));
- md_number_to_chars (to, inst.instruction, INSN_SIZE);
- md_number_to_chars (to + INSN_SIZE, inst.instruction, INSN_SIZE);
- }
- else
- md_number_to_chars (to, inst.instruction, inst.size);
+ /* Resolve these relocations even if the symbol is extern or weak. */
+ if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+ return 0;
- if (inst.reloc.type != BFD_RELOC_UNUSED)
- fix_new_arm (frag_now, to - frag_now->fr_literal,
- inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
- inst.reloc.type);
+ return generic_force_reloc (fixp);
+}
+
+#ifdef OBJ_COFF
+/* This is a little hack to help the gas/arm/adrl.s test. It prevents
+ local labels from being added to the output symbol table when they
+ are used with the ADRL pseudo op. The ADRL relocation should always
+ be resolved before the binbary is emitted, so it is safe to say that
+ it is adjustable. */
+
+bfd_boolean
+arm_fix_adjustable (fixS * fixP)
+{
+ if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+ return 1;
+ return 0;
+}
+#endif
#ifdef OBJ_ELF
- dwarf2_emit_insn (inst.size);
+/* Relocations against Thumb function names must be left unadjusted,
+ so that the linker can use this information to correctly set the
+ bottom bit of their addresses. The MIPS version of this function
+ also prevents relocations that are mips-16 specific, but I do not
+ know why it does this.
+
+ FIXME:
+ There is one other problem that ought to be addressed here, but
+ which currently is not: Taking the address of a label (rather
+ than a function) and then later jumping to that address. Such
+ addresses also ought to have their bottom bit set (assuming that
+ they reside in Thumb code), but at the moment they will not. */
+
+bfd_boolean
+arm_fix_adjustable (fixS * fixP)
+{
+ if (fixP->fx_addsy == NULL)
+ return 1;
+
+ if (THUMB_IS_FUNC (fixP->fx_addsy)
+ && fixP->fx_subsy == NULL)
+ return 0;
+
+ /* We need the symbol name for the VTABLE entries. */
+ if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
+ || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
+ return 0;
+
+ /* Don't allow symbols to be discarded on GOT related relocs. */
+ if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
+ || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
+ || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
+ || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
+ return 0;
+
+ return 1;
+}
+
+const char *
+elf32_arm_target_format (void)
+{
+#ifdef TE_SYMBIAN
+ return (target_big_endian
+ ? "elf32-bigarm-symbian"
+ : "elf32-littlearm-symbian");
+#elif defined (TE_VXWORKS)
+ return (target_big_endian
+ ? "elf32-bigarm-vxworks"
+ : "elf32-littlearm-vxworks");
+#else
+ if (target_big_endian)
+ return "elf32-bigarm";
+ else
+ return "elf32-littlearm";
#endif
}
void
-md_assemble (char * str)
+armelf_frob_symbol (symbolS * symp,
+ int * puntp)
{
- char c;
- char *p;
- char *start;
+ elf_frob_symbol (symp, puntp);
+}
+#endif
- /* Align the previous label if needed. */
- if (last_label_seen != NULL)
- {
- symbol_set_frag (last_label_seen, frag_now);
- S_SET_VALUE (last_label_seen, (valueT) frag_now_fix ());
- S_SET_SEGMENT (last_label_seen, now_seg);
- }
+/* MD interface: Finalization. */
- memset (&inst, '\0', sizeof (inst));
- inst.reloc.type = BFD_RELOC_UNUSED;
-
- skip_whitespace (str);
+/* A good place to do this, although this was probably not intended
+ for this kind of use. We need to dump the literal pool before
+ references are made to a null symbol pointer. */
- /* Scan up to the end of the op-code, which must end in white space or
- end of string. */
- for (start = p = str; *p != '\0'; p++)
- if (*p == ' ')
- break;
+void
+arm_cleanup (void)
+{
+ literal_pool * pool;
- if (p == str)
+ for (pool = list_of_pools; pool; pool = pool->next)
{
- as_bad (_("no operator -- statement `%s'\n"), str);
- return;
+ /* Put it at the end of the relevent section. */
+ subseg_set (pool->section, pool->sub_section);
+#ifdef OBJ_ELF
+ arm_elf_change_section ();
+#endif
+ s_ltorg (0);
}
+}
- if (thumb_mode)
- {
- const struct thumb_opcode * opcode;
+/* Adjust the symbol table. This marks Thumb symbols as distinct from
+ ARM ones. */
- c = *p;
- *p = '\0';
- opcode = (const struct thumb_opcode *) hash_find (arm_tops_hsh, str);
- *p = c;
+void
+arm_adjust_symtab (void)
+{
+#ifdef OBJ_COFF
+ symbolS * sym;
- if (opcode)
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
+ {
+ if (ARM_IS_THUMB (sym))
{
- /* Check that this instruction is supported for this CPU. */
- if (thumb_mode == 1 && (opcode->variant & cpu_variant) == 0)
+ if (THUMB_IS_FUNC (sym))
{
- as_bad (_("selected processor does not support `%s'"), str);
- return;
- }
+ /* Mark the symbol as a Thumb function. */
+ if ( S_GET_STORAGE_CLASS (sym) == C_STAT
+ || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
- mapping_state (MAP_THUMB);
- inst.instruction = opcode->value;
- inst.size = opcode->size;
- opcode->parms (p);
- output_inst (str);
- return;
+ else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
+ else
+ as_bad (_("%s: unexpected function type: %d"),
+ S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
+ }
+ else switch (S_GET_STORAGE_CLASS (sym))
+ {
+ case C_EXT:
+ S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
+ break;
+ case C_STAT:
+ S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
+ break;
+ case C_LABEL:
+ S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
+ break;
+ default:
+ /* Do nothing. */
+ break;
+ }
}
+
+ if (ARM_IS_INTERWORK (sym))
+ coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
}
- else
+#endif
+#ifdef OBJ_ELF
+ symbolS * sym;
+ char bind;
+
+ for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
{
- const struct asm_opcode * opcode;
+ if (ARM_IS_THUMB (sym))
+ {
+ elf_symbol_type * elf_sym;
- c = *p;
- *p = '\0';
- opcode = (const struct asm_opcode *) hash_find (arm_ops_hsh, str);
- *p = c;
+ elf_sym = elf_symbol (symbol_get_bfdsym (sym));
+ bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info);
- if (opcode)
- {
- /* Check that this instruction is supported for this CPU. */
- if ((opcode->variant & cpu_variant) == 0)
+ if (! bfd_is_arm_mapping_symbol_name (elf_sym->symbol.name))
{
- as_bad (_("selected processor does not support `%s'"), str);
- return;
+ /* If it's a .thumb_func, declare it as so,
+ otherwise tag label as .code 16. */
+ if (THUMB_IS_FUNC (sym))
+ elf_sym->internal_elf_sym.st_info =
+ ELF_ST_INFO (bind, STT_ARM_TFUNC);
+ else
+ elf_sym->internal_elf_sym.st_info =
+ ELF_ST_INFO (bind, STT_ARM_16BIT);
}
-
- mapping_state (MAP_ARM);
- inst.instruction = opcode->value;
- inst.size = INSN_SIZE;
- opcode->parms (p);
- output_inst (str);
- return;
}
}
+#endif
+}
+
+/* MD interface: Initialization. */
+
+static void
+set_constant_flonums (void)
+{
+ int i;
+
+ for (i = 0; i < NUM_FLOAT_VALS; i++)
+ if (atof_ieee ((char *) fp_const[i], 'x', fp_values[i]) == NULL)
+ abort ();
+}
+
+void
+md_begin (void)
+{
+ unsigned mach;
+ unsigned int i;
+
+ if ( (arm_ops_hsh = hash_new ()) == NULL
+ || (arm_cond_hsh = hash_new ()) == NULL
+ || (arm_shift_hsh = hash_new ()) == NULL
+ || (arm_psr_hsh = hash_new ()) == NULL
+ || (arm_reg_hsh = hash_new ()) == NULL
+ || (arm_reloc_hsh = hash_new ()) == NULL)
+ as_fatal (_("virtual memory exhausted"));
+
+ for (i = 0; i < sizeof (insns) / sizeof (struct asm_opcode); i++)
+ hash_insert (arm_ops_hsh, insns[i].template, (PTR) (insns + i));
+ for (i = 0; i < sizeof (conds) / sizeof (struct asm_cond); i++)
+ hash_insert (arm_cond_hsh, conds[i].template, (PTR) (conds + i));
+ for (i = 0; i < sizeof (shift_names) / sizeof (struct asm_shift_name); i++)
+ hash_insert (arm_shift_hsh, shift_names[i].name, (PTR) (shift_names + i));
+ for (i = 0; i < sizeof (psrs) / sizeof (struct asm_psr); i++)
+ hash_insert (arm_psr_hsh, psrs[i].template, (PTR) (psrs + i));
+ for (i = 0; i < sizeof (reg_names) / sizeof (struct reg_entry); i++)
+ hash_insert (arm_reg_hsh, reg_names[i].name, (PTR) (reg_names + i));
+#ifdef OBJ_ELF
+ for (i = 0; i < sizeof (reloc_names) / sizeof (struct reloc_entry); i++)
+ hash_insert (arm_reloc_hsh, reloc_names[i].name, (PTR) (reloc_names + i));
+#endif
+
+ set_constant_flonums ();
+
+ /* Set the cpu variant based on the command-line options. We prefer
+ -mcpu= over -march= if both are set (as for GCC); and we prefer
+ -mfpu= over any other way of setting the floating point unit.
+ Use of legacy options with new options are faulted. */
+ if (legacy_cpu != -1)
+ {
+ if (mcpu_cpu_opt != -1 || march_cpu_opt != -1)
+ as_bad (_("use of old and new-style options to set CPU type"));
+
+ mcpu_cpu_opt = legacy_cpu;
+ }
+ else if (mcpu_cpu_opt == -1)
+ mcpu_cpu_opt = march_cpu_opt;
- /* It wasn't an instruction, but it might be a register alias of the form
- alias .req reg. */
- if (create_register_alias (str, p))
- return;
+ if (legacy_fpu != -1)
+ {
+ if (mfpu_opt != -1)
+ as_bad (_("use of old and new-style options to set FPU type"));
+
+ mfpu_opt = legacy_fpu;
+ }
+ else if (mfpu_opt == -1)
+ {
+#if !(defined (TE_LINUX) || defined (TE_NetBSD) || defined (TE_VXWORKS))
+ /* Some environments specify a default FPU. If they don't, infer it
+ from the processor. */
+ if (mcpu_fpu_opt != -1)
+ mfpu_opt = mcpu_fpu_opt;
+ else
+ mfpu_opt = march_fpu_opt;
+#else
+ mfpu_opt = FPU_DEFAULT;
+#endif
+ }
+
+ if (mfpu_opt == -1)
+ {
+ if (mcpu_cpu_opt == -1)
+ mfpu_opt = FPU_DEFAULT;
+ else if (mcpu_cpu_opt & ARM_EXT_V5)
+ mfpu_opt = FPU_ARCH_VFP_V2;
+ else
+ mfpu_opt = FPU_ARCH_FPA;
+ }
+
+ if (mcpu_cpu_opt == -1)
+ mcpu_cpu_opt = CPU_DEFAULT;
+
+ cpu_variant = mcpu_cpu_opt | mfpu_opt;
+
+#if defined OBJ_COFF || defined OBJ_ELF
+ {
+ unsigned int flags = 0;
+
+#if defined OBJ_ELF
+ flags = meabi_flags;
+
+ switch (meabi_flags)
+ {
+ case EF_ARM_EABI_UNKNOWN:
+#endif
+ /* Set the flags in the private structure. */
+ if (uses_apcs_26) flags |= F_APCS26;
+ if (support_interwork) flags |= F_INTERWORK;
+ if (uses_apcs_float) flags |= F_APCS_FLOAT;
+ if (pic_code) flags |= F_PIC;
+ if ((cpu_variant & FPU_ANY) == FPU_NONE
+ || (cpu_variant & FPU_ANY) == FPU_ARCH_VFP) /* VFP layout only. */
+ flags |= F_SOFT_FLOAT;
- as_bad (_("bad instruction `%s'"), start);
+ switch (mfloat_abi_opt)
+ {
+ case ARM_FLOAT_ABI_SOFT:
+ case ARM_FLOAT_ABI_SOFTFP:
+ flags |= F_SOFT_FLOAT;
+ break;
+
+ case ARM_FLOAT_ABI_HARD:
+ if (flags & F_SOFT_FLOAT)
+ as_bad (_("hard-float conflicts with specified fpu"));
+ break;
+ }
+
+ /* Using VFP conventions (even if soft-float). */
+ if (cpu_variant & FPU_VFP_EXT_NONE)
+ flags |= F_VFP_FLOAT;
+
+#if defined OBJ_ELF
+ if (cpu_variant & FPU_ARCH_MAVERICK)
+ flags |= EF_ARM_MAVERICK_FLOAT;
+ break;
+
+ case EF_ARM_EABI_VER4:
+ /* No additional flags to set. */
+ break;
+
+ default:
+ abort ();
+ }
+#endif
+ bfd_set_private_flags (stdoutput, flags);
+
+ /* We have run out flags in the COFF header to encode the
+ status of ATPCS support, so instead we create a dummy,
+ empty, debug section called .arm.atpcs. */
+ if (atpcs)
+ {
+ asection * sec;
+
+ sec = bfd_make_section (stdoutput, ".arm.atpcs");
+
+ if (sec != NULL)
+ {
+ bfd_set_section_flags
+ (stdoutput, sec, SEC_READONLY | SEC_DEBUGGING /* | SEC_HAS_CONTENTS */);
+ bfd_set_section_size (stdoutput, sec, 0);
+ bfd_set_section_contents (stdoutput, sec, NULL, 0, 0);
+ }
+ }
+ }
+#endif
+
+ /* Record the CPU type as well. */
+ switch (cpu_variant & ARM_CPU_MASK)
+ {
+ case ARM_2:
+ mach = bfd_mach_arm_2;
+ break;
+
+ case ARM_3: /* Also ARM_250. */
+ mach = bfd_mach_arm_2a;
+ break;
+
+ case ARM_6: /* Also ARM_7. */
+ mach = bfd_mach_arm_3;
+ break;
+
+ default:
+ mach = bfd_mach_arm_unknown;
+ break;
+ }
+
+ /* Catch special cases. */
+ if (cpu_variant & ARM_CEXT_IWMMXT)
+ mach = bfd_mach_arm_iWMMXt;
+ else if (cpu_variant & ARM_CEXT_XSCALE)
+ mach = bfd_mach_arm_XScale;
+ else if (cpu_variant & ARM_CEXT_MAVERICK)
+ mach = bfd_mach_arm_ep9312;
+ else if (cpu_variant & ARM_EXT_V5E)
+ mach = bfd_mach_arm_5TE;
+ else if (cpu_variant & ARM_EXT_V5)
+ {
+ if (cpu_variant & ARM_EXT_V4T)
+ mach = bfd_mach_arm_5T;
+ else
+ mach = bfd_mach_arm_5;
+ }
+ else if (cpu_variant & ARM_EXT_V4)
+ {
+ if (cpu_variant & ARM_EXT_V4T)
+ mach = bfd_mach_arm_4T;
+ else
+ mach = bfd_mach_arm_4;
+ }
+ else if (cpu_variant & ARM_EXT_V3M)
+ mach = bfd_mach_arm_3M;
+
+ bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach);
}
+/* Command line processing. */
+
/* md_parse_option
Invocation line includes a switch not recognized by the base assembler.
See if it's a processor-specific option.
@@ -12793,23 +11761,23 @@ md_assemble (char * str)
The remaining options are only supported for back-wards compatibility.
Cpu variants, the arm part is optional:
- -m[arm]1 Currently not supported.
- -m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor
- -m[arm]3 Arm 3 processor
- -m[arm]6[xx], Arm 6 processors
- -m[arm]7[xx][t][[d]m] Arm 7 processors
- -m[arm]8[10] Arm 8 processors
- -m[arm]9[20][tdmi] Arm 9 processors
- -mstrongarm[110[0]] StrongARM processors
- -mxscale XScale processors
- -m[arm]v[2345[t[e]]] Arm architectures
- -mall All (except the ARM1)
+ -m[arm]1 Currently not supported.
+ -m[arm]2, -m[arm]250 Arm 2 and Arm 250 processor
+ -m[arm]3 Arm 3 processor
+ -m[arm]6[xx], Arm 6 processors
+ -m[arm]7[xx][t][[d]m] Arm 7 processors
+ -m[arm]8[10] Arm 8 processors
+ -m[arm]9[20][tdmi] Arm 9 processors
+ -mstrongarm[110[0]] StrongARM processors
+ -mxscale XScale processors
+ -m[arm]v[2345[t[e]]] Arm architectures
+ -mall All (except the ARM1)
FP variants:
- -mfpa10, -mfpa11 FPA10 and 11 co-processor instructions
- -mfpe-old (No float load/store multiples)
+ -mfpa10, -mfpa11 FPA10 and 11 co-processor instructions
+ -mfpe-old (No float load/store multiples)
-mvfpxd VFP Single precision
-mvfp All VFP
- -mno-fpu Disable all floating point instructions
+ -mno-fpu Disable all floating point instructions
The following CPU names are recognized:
arm1, arm2, arm250, arm3, arm6, arm600, arm610, arm620,
@@ -12852,15 +11820,15 @@ struct arm_option_table
{
char *option; /* Option name to match. */
char *help; /* Help information. */
- int *var; /* Variable to change. */
- int value; /* What to change it to. */
+ int *var; /* Variable to change. */
+ int value; /* What to change it to. */
char *deprecated; /* If non-null, print this message. */
};
struct arm_option_table arm_opts[] =
{
- {"k", N_("generate PIC code"), &pic_code, 1, NULL},
- {"mthumb", N_("assemble Thumb code"), &thumb_mode, 1, NULL},
+ {"k", N_("generate PIC code"), &pic_code, 1, NULL},
+ {"mthumb", N_("assemble Thumb code"), &thumb_mode, 1, NULL},
{"mthumb-interwork", N_("support ARM/Thumb interworking"),
&support_interwork, 1, NULL},
{"mapcs-32", N_("code uses 32-bit program counter"), &uses_apcs_26, 0, NULL},
@@ -12873,7 +11841,7 @@ struct arm_option_table arm_opts[] =
{"mlittle-endian", N_("assemble for little-endian"), &target_big_endian, 0,
NULL},
- /* These are recognized by the assembler, but have no affect on code. */
+ /* These are recognized by the assembler, but have no affect on code. */
{"mapcs-frame", N_("use frame pointer"), NULL, 0, NULL},
{"mapcs-stack-check", N_("use stack size checking"), NULL, 0, NULL},
@@ -12956,7 +11924,7 @@ struct arm_option_table arm_opts[] =
N_("use -mcpu=strongarm1110")},
{"mxscale", NULL, &legacy_cpu, ARM_ARCH_XSCALE, N_("use -mcpu=xscale")},
{"miwmmxt", NULL, &legacy_cpu, ARM_ARCH_IWMMXT, N_("use -mcpu=iwmmxt")},
- {"mall", NULL, &legacy_cpu, ARM_ANY, N_("use -mcpu=all")},
+ {"mall", NULL, &legacy_cpu, ARM_ANY, N_("use -mcpu=all")},
/* Architecture variants -- don't add any more to this list either. */
{"mv2", NULL, &legacy_cpu, ARM_ARCH_V2, N_("use -march=armv2")},
@@ -12978,7 +11946,7 @@ struct arm_option_table arm_opts[] =
{"mv5e", NULL, &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
{"marmv5e", NULL, &legacy_cpu, ARM_ARCH_V5TE, N_("use -march=armv5te")},
- /* Floating point variants -- don't add any more to this list either. */
+ /* Floating point variants -- don't add any more to this list either. */
{"mfpe-old", NULL, &legacy_fpu, FPU_ARCH_FPE, N_("use -mfpu=fpe")},
{"mfpa10", NULL, &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa10")},
{"mfpa11", NULL, &legacy_fpu, FPU_ARCH_FPA, N_("use -mfpu=fpa11")},
@@ -12991,10 +11959,10 @@ struct arm_option_table arm_opts[] =
struct arm_cpu_option_table
{
char *name;
- int value;
+ int value;
/* For some CPUs we assume an FPU unless the user explicitly sets
- -mfpu=... */
- int default_fpu;
+ -mfpu=... */
+ int default_fpu;
};
/* This list should, at a minimum, contain all the cpu names
@@ -13046,14 +12014,14 @@ static struct arm_cpu_option_table arm_cpus[] =
{"arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA},
{"arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA},
/* For V5 or later processors we default to using VFP; but the user
- should really set the FPU type explicitly. */
+ should really set the FPU type explicitly. */
{"arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
- {"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
+ {"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
{"arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
{"arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
{"arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
- {"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
+ {"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2},
{"arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1},
@@ -13063,14 +12031,14 @@ static struct arm_cpu_option_table arm_cpus[] =
{"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2},
{"arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
{"arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2},
- {"arm1136js", ARM_ARCH_V6, FPU_NONE},
- {"arm1136j-s", ARM_ARCH_V6, FPU_NONE},
- {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
- {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
- {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2},
- {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE},
- {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE},
- {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2},
+ {"arm1136js", ARM_ARCH_V6, FPU_NONE},
+ {"arm1136j-s", ARM_ARCH_V6, FPU_NONE},
+ {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
+ {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2},
+ {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2},
+ {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE},
+ {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE},
+ {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2},
/* ??? XSCALE is really an architecture. */
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2},
/* ??? iwmmxt is not a processor. */
@@ -13084,8 +12052,8 @@ static struct arm_cpu_option_table arm_cpus[] =
struct arm_arch_option_table
{
char *name;
- int value;
- int default_fpu;
+ int value;
+ int default_fpu;
};
/* This list should, at a minimum, contain all the architecture names
@@ -13108,15 +12076,15 @@ static struct arm_arch_option_table arm_archs[] =
{"armv5txm", ARM_ARCH_V5TxM, FPU_ARCH_VFP},
{"armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP},
{"armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP},
- {"armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP},
- {"armv6", ARM_ARCH_V6, FPU_ARCH_VFP},
- {"armv6j", ARM_ARCH_V6, FPU_ARCH_VFP},
- {"armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP},
- {"armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP},
- {"armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP},
- {"armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP},
- {"armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP},
- {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP},
+ {"armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP},
+ {"armv6", ARM_ARCH_V6, FPU_ARCH_VFP},
+ {"armv6j", ARM_ARCH_V6, FPU_ARCH_VFP},
+ {"armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP},
+ {"armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP},
+ {"armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP},
+ {"armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP},
+ {"armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP},
+ {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP},
{"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP},
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP},
{"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
@@ -13124,13 +12092,13 @@ static struct arm_arch_option_table arm_archs[] =
};
/* ISA extensions in the co-processor space. */
-struct arm_arch_extension_table
+struct arm_option_value_table
{
char *name;
int value;
};
-static struct arm_arch_extension_table arm_extensions[] =
+static struct arm_option_value_table arm_extensions[] =
{
{"maverick", ARM_CEXT_MAVERICK},
{"xscale", ARM_CEXT_XSCALE},
@@ -13138,15 +12106,9 @@ static struct arm_arch_extension_table arm_extensions[] =
{NULL, 0}
};
-struct arm_fpu_option_table
-{
- char *name;
- int value;
-};
-
/* This list should, at a minimum, contain all the fpu names
recognized by GCC. */
-static struct arm_fpu_option_table arm_fpus[] =
+static struct arm_option_value_table arm_fpus[] =
{
{"softfpa", FPU_NONE},
{"fpe", FPU_ARCH_FPE},
@@ -13171,13 +12133,7 @@ static struct arm_fpu_option_table arm_fpus[] =
{NULL, 0}
};
-struct arm_float_abi_option_table
-{
- char *name;
- int value;
-};
-
-static struct arm_float_abi_option_table arm_float_abis[] =
+static struct arm_option_value_table arm_float_abis[] =
{
{"hard", ARM_FLOAT_ABI_HARD},
{"softfp", ARM_FLOAT_ABI_SOFTFP},
@@ -13185,15 +12141,9 @@ static struct arm_float_abi_option_table arm_float_abis[] =
{NULL, 0}
};
-struct arm_eabi_option_table
-{
- char *name;
- unsigned int value;
-};
-
#ifdef OBJ_ELF
/* We only know how to output GNU and ver 4 (AAELF) formats. */
-static struct arm_eabi_option_table arm_eabis[] =
+static struct arm_option_value_table arm_eabis[] =
{
{"gnu", EF_ARM_EABI_UNKNOWN},
{"4", EF_ARM_EABI_VER4},
@@ -13203,7 +12153,7 @@ static struct arm_eabi_option_table arm_eabis[] =
struct arm_long_option_table
{
- char * option; /* Substring to match. */
+ char * option; /* Substring to match. */
char * help; /* Help information. */
int (* func) (char * subopt); /* Function to decode sub-option. */
char * deprecated; /* If non-null, print this message. */
@@ -13214,7 +12164,7 @@ arm_parse_extension (char * str, int * opt_p)
{
while (str != NULL && *str != 0)
{
- struct arm_arch_extension_table * opt;
+ struct arm_option_value_table * opt;
char * ext;
int optlen;
@@ -13329,7 +12279,7 @@ arm_parse_arch (char * str)
static int
arm_parse_fpu (char * str)
{
- struct arm_fpu_option_table * opt;
+ struct arm_option_value_table * opt;
for (opt = arm_fpus; opt->name != NULL; opt++)
if (streq (opt->name, str))
@@ -13345,7 +12295,7 @@ arm_parse_fpu (char * str)
static int
arm_parse_float_abi (char * str)
{
- struct arm_float_abi_option_table * opt;
+ struct arm_option_value_table * opt;
for (opt = arm_float_abis; opt->name != NULL; opt++)
if (streq (opt->name, str))
@@ -13362,7 +12312,7 @@ arm_parse_float_abi (char * str)
static int
arm_parse_eabi (char * str)
{
- struct arm_eabi_option_table *opt;
+ struct arm_option_value_table *opt;
for (opt = arm_eabis; opt->name != NULL; opt++)
if (streq (opt->name, str))
@@ -13414,7 +12364,7 @@ md_parse_option (int c, char * arg)
case 'a':
/* Listing option. Just ignore these, we don't support additional
- ones. */
+ ones. */
return 0;
default:
@@ -13490,1769 +12440,3 @@ md_show_usage (FILE * fp)
-EL assemble code for a little-endian cpu\n"));
#endif
}
-
-/* This fix_new is called by cons via TC_CONS_FIX_NEW. */
-
-void
-cons_fix_new_arm (fragS * frag,
- int where,
- int size,
- expressionS * exp)
-{
- bfd_reloc_code_real_type type;
- int pcrel = 0;
-
- /* Pick a reloc.
- FIXME: @@ Should look at CPU word size. */
- switch (size)
- {
- case 1:
- type = BFD_RELOC_8;
- break;
- case 2:
- type = BFD_RELOC_16;
- break;
- case 4:
- default:
- type = BFD_RELOC_32;
- break;
- case 8:
- type = BFD_RELOC_64;
- break;
- }
-
- fix_new_exp (frag, where, (int) size, exp, pcrel, type);
-}
-
-/* A good place to do this, although this was probably not intended
- for this kind of use. We need to dump the literal pool before
- references are made to a null symbol pointer. */
-
-void
-arm_cleanup (void)
-{
- literal_pool * pool;
-
- for (pool = list_of_pools; pool; pool = pool->next)
- {
- /* Put it at the end of the relevent section. */
- subseg_set (pool->section, pool->sub_section);
-#ifdef OBJ_ELF
- arm_elf_change_section ();
-#endif
- s_ltorg (0);
- }
-}
-
-void
-arm_start_line_hook (void)
-{
- last_label_seen = NULL;
-}
-
-void
-arm_frob_label (symbolS * sym)
-{
- last_label_seen = sym;
-
- ARM_SET_THUMB (sym, thumb_mode);
-
-#if defined OBJ_COFF || defined OBJ_ELF
- ARM_SET_INTERWORK (sym, support_interwork);
-#endif
-
- /* Note - do not allow local symbols (.Lxxx) to be labeled
- as Thumb functions. This is because these labels, whilst
- they exist inside Thumb code, are not the entry points for
- possible ARM->Thumb calls. Also, these labels can be used
- as part of a computed goto or switch statement. eg gcc
- can generate code that looks like this:
-
- ldr r2, [pc, .Laaa]
- lsl r3, r3, #2
- ldr r2, [r3, r2]
- mov pc, r2
-
- .Lbbb: .word .Lxxx
- .Lccc: .word .Lyyy
- ..etc...
- .Laaa: .word Lbbb
-
- The first instruction loads the address of the jump table.
- The second instruction converts a table index into a byte offset.
- The third instruction gets the jump address out of the table.
- The fourth instruction performs the jump.
-
- If the address stored at .Laaa is that of a symbol which has the
- Thumb_Func bit set, then the linker will arrange for this address
- to have the bottom bit set, which in turn would mean that the
- address computation performed by the third instruction would end
- up with the bottom bit set. Since the ARM is capable of unaligned
- word loads, the instruction would then load the incorrect address
- out of the jump table, and chaos would ensue. */
- if (label_is_thumb_function_name
- && (S_GET_NAME (sym)[0] != '.' || S_GET_NAME (sym)[1] != 'L')
- && (bfd_get_section_flags (stdoutput, now_seg) & SEC_CODE) != 0)
- {
- /* When the address of a Thumb function is taken the bottom
- bit of that address should be set. This will allow
- interworking between Arm and Thumb functions to work
- correctly. */
-
- THUMB_SET_FUNC (sym, 1);
-
- label_is_thumb_function_name = FALSE;
- }
-}
-
-/* Adjust the symbol table. This marks Thumb symbols as distinct from
- ARM ones. */
-
-void
-arm_adjust_symtab (void)
-{
-#ifdef OBJ_COFF
- symbolS * sym;
-
- for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
- {
- if (ARM_IS_THUMB (sym))
- {
- if (THUMB_IS_FUNC (sym))
- {
- /* Mark the symbol as a Thumb function. */
- if ( S_GET_STORAGE_CLASS (sym) == C_STAT
- || S_GET_STORAGE_CLASS (sym) == C_LABEL) /* This can happen! */
- S_SET_STORAGE_CLASS (sym, C_THUMBSTATFUNC);
-
- else if (S_GET_STORAGE_CLASS (sym) == C_EXT)
- S_SET_STORAGE_CLASS (sym, C_THUMBEXTFUNC);
- else
- as_bad (_("%s: unexpected function type: %d"),
- S_GET_NAME (sym), S_GET_STORAGE_CLASS (sym));
- }
- else switch (S_GET_STORAGE_CLASS (sym))
- {
- case C_EXT:
- S_SET_STORAGE_CLASS (sym, C_THUMBEXT);
- break;
- case C_STAT:
- S_SET_STORAGE_CLASS (sym, C_THUMBSTAT);
- break;
- case C_LABEL:
- S_SET_STORAGE_CLASS (sym, C_THUMBLABEL);
- break;
- default:
- /* Do nothing. */
- break;
- }
- }
-
- if (ARM_IS_INTERWORK (sym))
- coffsymbol (symbol_get_bfdsym (sym))->native->u.syment.n_flags = 0xFF;
- }
-#endif
-#ifdef OBJ_ELF
- symbolS * sym;
- char bind;
-
- for (sym = symbol_rootP; sym != NULL; sym = symbol_next (sym))
- {
- if (ARM_IS_THUMB (sym))
- {
- elf_symbol_type * elf_sym;
-
- elf_sym = elf_symbol (symbol_get_bfdsym (sym));
- bind = ELF_ST_BIND (elf_sym->internal_elf_sym.st_info);
-
- if (! bfd_is_arm_mapping_symbol_name (elf_sym->symbol.name))
- {
- /* If it's a .thumb_func, declare it as so,
- otherwise tag label as .code 16. */
- if (THUMB_IS_FUNC (sym))
- elf_sym->internal_elf_sym.st_info =
- ELF_ST_INFO (bind, STT_ARM_TFUNC);
- else
- elf_sym->internal_elf_sym.st_info =
- ELF_ST_INFO (bind, STT_ARM_16BIT);
- }
- }
- }
-#endif
-}
-
-int
-arm_data_in_code (void)
-{
- if (thumb_mode && ! strncmp (input_line_pointer + 1, "data:", 5))
- {
- *input_line_pointer = '/';
- input_line_pointer += 5;
- *input_line_pointer = 0;
- return 1;
- }
-
- return 0;
-}
-
-char *
-arm_canonicalize_symbol_name (char * name)
-{
- int len;
-
- if (thumb_mode && (len = strlen (name)) > 5
- && streq (name + len - 5, "/data"))
- *(name + len - 5) = 0;
-
- return name;
-}
-
-#if defined OBJ_COFF || defined OBJ_ELF
-void
-arm_validate_fix (fixS * fixP)
-{
- /* If the destination of the branch is a defined symbol which does not have
- the THUMB_FUNC attribute, then we must be calling a function which has
- the (interfacearm) attribute. We look for the Thumb entry point to that
- function and change the branch to refer to that function instead. */
- if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23
- && fixP->fx_addsy != NULL
- && S_IS_DEFINED (fixP->fx_addsy)
- && ! THUMB_IS_FUNC (fixP->fx_addsy))
- {
- fixP->fx_addsy = find_real_start (fixP->fx_addsy);
- }
-}
-#endif
-
-int
-arm_force_relocation (struct fix * fixp)
-{
-#if defined (OBJ_COFF) && defined (TE_PE)
- if (fixp->fx_r_type == BFD_RELOC_RVA)
- return 1;
-#endif
-#ifdef OBJ_ELF
- if (fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BRANCH
- || fixp->fx_r_type == BFD_RELOC_ARM_PCREL_BLX
- || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX
- || fixp->fx_r_type == BFD_RELOC_THUMB_PCREL_BRANCH23)
- return 1;
-#endif
-
- /* Resolve these relocations even if the symbol is extern or weak. */
- if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
- || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
- || fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
- return 0;
-
- return generic_force_reloc (fixp);
-}
-
-#ifdef OBJ_COFF
-/* This is a little hack to help the gas/arm/adrl.s test. It prevents
- local labels from being added to the output symbol table when they
- are used with the ADRL pseudo op. The ADRL relocation should always
- be resolved before the binbary is emitted, so it is safe to say that
- it is adjustable. */
-
-bfd_boolean
-arm_fix_adjustable (fixS * fixP)
-{
- if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
- return 1;
- return 0;
-}
-#endif
-
-#ifdef OBJ_ELF
-/* Relocations against Thumb function names must be left unadjusted,
- so that the linker can use this information to correctly set the
- bottom bit of their addresses. The MIPS version of this function
- also prevents relocations that are mips-16 specific, but I do not
- know why it does this.
-
- FIXME:
- There is one other problem that ought to be addressed here, but
- which currently is not: Taking the address of a label (rather
- than a function) and then later jumping to that address. Such
- addresses also ought to have their bottom bit set (assuming that
- they reside in Thumb code), but at the moment they will not. */
-
-bfd_boolean
-arm_fix_adjustable (fixS * fixP)
-{
- if (fixP->fx_addsy == NULL)
- return 1;
-
- if (THUMB_IS_FUNC (fixP->fx_addsy)
- && fixP->fx_subsy == NULL)
- return 0;
-
- /* We need the symbol name for the VTABLE entries. */
- if ( fixP->fx_r_type == BFD_RELOC_VTABLE_INHERIT
- || fixP->fx_r_type == BFD_RELOC_VTABLE_ENTRY)
- return 0;
-
- /* Don't allow symbols to be discarded on GOT related relocs. */
- if (fixP->fx_r_type == BFD_RELOC_ARM_PLT32
- || fixP->fx_r_type == BFD_RELOC_ARM_GOT32
- || fixP->fx_r_type == BFD_RELOC_ARM_GOTOFF
- || fixP->fx_r_type == BFD_RELOC_ARM_TLS_GD32
- || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LE32
- || fixP->fx_r_type == BFD_RELOC_ARM_TLS_IE32
- || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDM32
- || fixP->fx_r_type == BFD_RELOC_ARM_TLS_LDO32
- || fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
- return 0;
-
- return 1;
-}
-
-const char *
-elf32_arm_target_format (void)
-{
-#ifdef TE_SYMBIAN
- return (target_big_endian
- ? "elf32-bigarm-symbian"
- : "elf32-littlearm-symbian");
-#elif defined (TE_VXWORKS)
- return (target_big_endian
- ? "elf32-bigarm-vxworks"
- : "elf32-littlearm-vxworks");
-#else
- if (target_big_endian)
- return "elf32-bigarm";
- else
- return "elf32-littlearm";
-#endif
-}
-
-void
-armelf_frob_symbol (symbolS * symp,
- int * puntp)
-{
- elf_frob_symbol (symp, puntp);
-}
-
-static void
-s_arm_elf_cons (int nbytes)
-{
- expressionS exp;
-
-#ifdef md_flush_pending_output
- md_flush_pending_output ();
-#endif
-
- if (is_it_end_of_statement ())
- {
- demand_empty_rest_of_line ();
- return;
- }
-
-#ifdef md_cons_align
- md_cons_align (nbytes);
-#endif
-
- mapping_state (MAP_DATA);
- do
- {
- bfd_reloc_code_real_type reloc;
- char *sym_start;
- int sym_len;
-
- sym_start = input_line_pointer;
- expression (& exp);
- sym_len = input_line_pointer - sym_start;
-
- if (exp.X_op == O_symbol
- && * input_line_pointer == '('
- && (reloc = arm_parse_reloc ()) != BFD_RELOC_UNUSED)
- {
- reloc_howto_type *howto = bfd_reloc_type_lookup (stdoutput, reloc);
- int size = bfd_get_reloc_size (howto);
-
- if (size > nbytes)
- as_bad ("%s relocations do not fit in %d bytes",
- howto->name, nbytes);
- else
- {
- char *p;
- int offset = nbytes - size;
- char *saved_buf = alloca (sym_len), *saved_input;
-
- /* We've parsed an expression stopping at O_symbol. But there
- may be more expression left now that we have parsed the
- relocation marker. Parse it again. */
- saved_input = input_line_pointer - sym_len;
- memcpy (saved_buf, saved_input, sym_len);
- memmove (saved_input, sym_start, sym_len);
- input_line_pointer = saved_input;
- expression (& exp);
- memcpy (saved_input, saved_buf, sym_len);
- assert (input_line_pointer >= saved_input + sym_len);
-
- p = frag_more ((int) nbytes);
- fix_new_exp (frag_now, p - frag_now->fr_literal + offset, size,
- &exp, 0, reloc);
- }
- }
- else
- emit_expr (&exp, (unsigned int) nbytes);
- }
- while (*input_line_pointer++ == ',');
-
- /* Put terminator back into stream. */
- input_line_pointer --;
- demand_empty_rest_of_line ();
-}
-
-
-/* Parse a .rel31 directive. */
-
-static void
-s_arm_rel31 (int ignored ATTRIBUTE_UNUSED)
-{
- expressionS exp;
- char *p;
- valueT highbit;
-
- SKIP_WHITESPACE ();
-
- highbit = 0;
- if (*input_line_pointer == '1')
- highbit = 0x80000000;
- else if (*input_line_pointer != '0')
- as_bad (_("expected 0 or 1"));
-
- input_line_pointer++;
- SKIP_WHITESPACE ();
- if (*input_line_pointer != ',')
- as_bad (_("missing comma"));
- input_line_pointer++;
-
-#ifdef md_flush_pending_output
- md_flush_pending_output ();
-#endif
-
-#ifdef md_cons_align
- md_cons_align (4);
-#endif
-
- mapping_state (MAP_DATA);
-
- expression (&exp);
-
- p = frag_more (4);
- md_number_to_chars (p, highbit, 4);
- fix_new_arm (frag_now, p - frag_now->fr_literal, 4, &exp, 1,
- BFD_RELOC_ARM_PREL31);
-
- demand_empty_rest_of_line ();
-}
-
-/* Code to deal with unwinding tables. */
-
-static void add_unwind_adjustsp (offsetT);
-
-/* Switch to section NAME and create section if necessary. It's
- rather ugly that we have to manipulate input_line_pointer but I
- don't see any other way to accomplish the same thing without
- changing obj-elf.c (which may be the Right Thing, in the end).
- Copied from tc-ia64.c. */
-
-static void
-set_section (char *name)
-{
- char *saved_input_line_pointer;
-
- saved_input_line_pointer = input_line_pointer;
- input_line_pointer = name;
- obj_elf_section (0);
- input_line_pointer = saved_input_line_pointer;
-}
-
-/* Cenerate and deferred unwind frame offset. */
-
-static void
-flush_pending_unwind (void)
-{
- offsetT offset;
-
- offset = unwind.pending_offset;
- unwind.pending_offset = 0;
- if (offset != 0)
- add_unwind_adjustsp (offset);
-}
-
-/* Add an opcode to this list for this function. Two-byte opcodes should
- be passed as op[0] << 8 | op[1]. The list of opcodes is built in reverse
- order. */
-
-static void
-add_unwind_opcode (valueT op, int length)
-{
- /* Add any deferred stack adjustment. */
- if (unwind.pending_offset)
- flush_pending_unwind ();
-
- unwind.sp_restored = 0;
-
- if (unwind.opcode_count + length > unwind.opcode_alloc)
- {
- unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
- if (unwind.opcodes)
- unwind.opcodes = xrealloc (unwind.opcodes,
- unwind.opcode_alloc);
- else
- unwind.opcodes = xmalloc (unwind.opcode_alloc);
- }
- while (length > 0)
- {
- length--;
- unwind.opcodes[unwind.opcode_count] = op & 0xff;
- op >>= 8;
- unwind.opcode_count++;
- }
-}
-
-/* Add unwind opcodes to adjust the stack pointer. */
-
-static void
-add_unwind_adjustsp (offsetT offset)
-{
- valueT op;
-
- if (offset > 0x200)
- {
- /* We need at most 5 bytes to hold a 32-bit value in a uleb128. */
- char bytes[5];
- int n;
- valueT o;
-
- /* Long form: 0xb2, uleb128. */
- /* This might not fit in a word so add the individual bytes,
- remembering the list is built in reverse order. */
- o = (valueT) ((offset - 0x204) >> 2);
- if (o == 0)
- add_unwind_opcode (0, 1);
-
- /* Calculate the uleb128 encoding of the offset. */
- n = 0;
- while (o)
- {
- bytes[n] = o & 0x7f;
- o >>= 7;
- if (o)
- bytes[n] |= 0x80;
- n++;
- }
- /* Add the insn. */
- for (; n; n--)
- add_unwind_opcode (bytes[n - 1], 1);
- add_unwind_opcode (0xb2, 1);
- }
- else if (offset > 0x100)
- {
- /* Two short opcodes. */
- add_unwind_opcode (0x3f, 1);
- op = (offset - 0x104) >> 2;
- add_unwind_opcode (op, 1);
- }
- else if (offset > 0)
- {
- /* Short opcode. */
- op = (offset - 4) >> 2;
- add_unwind_opcode (op, 1);
- }
- else if (offset < 0)
- {
- offset = -offset;
- while (offset > 0x100)
- {
- add_unwind_opcode (0x7f, 1);
- offset -= 0x100;
- }
- op = ((offset - 4) >> 2) | 0x40;
- add_unwind_opcode (op, 1);
- }
-}
-
-/* Finish the list of unwind opcodes for this function. */
-static void
-finish_unwind_opcodes (void)
-{
- valueT op;
-
- if (unwind.fp_used)
- {
- /* Adjust sp as neccessary. */
- unwind.pending_offset += unwind.fp_offset - unwind.frame_size;
- flush_pending_unwind ();
-
- /* After restoring sp from the frame pointer. */
- op = 0x90 | unwind.fp_reg;
- add_unwind_opcode (op, 1);
- }
- else
- flush_pending_unwind ();
-}
-
-
-/* Start an exception table entry. If idx is nonzero this is an index table
- entry. */
-
-static void
-start_unwind_section (const segT text_seg, int idx)
-{
- const char * text_name;
- const char * prefix;
- const char * prefix_once;
- size_t prefix_len;
- size_t text_len;
- char * sec_name;
- size_t sec_name_len;
-
- if (idx)
- {
- prefix = ELF_STRING_ARM_unwind;
- prefix_once = ELF_STRING_ARM_unwind_once;
- }
- else
- {
- prefix = ELF_STRING_ARM_unwind_info;
- prefix_once = ELF_STRING_ARM_unwind_info_once;
- }
-
- text_name = segment_name (text_seg);
- if (streq (text_name, ".text"))
- text_name = "";
-
- if (strncmp (text_name, ".gnu.linkonce.t.",
- strlen (".gnu.linkonce.t.")) == 0)
- {
- prefix = prefix_once;
- text_name += strlen (".gnu.linkonce.t.");
- }
-
- prefix_len = strlen (prefix);
- text_len = strlen (text_name);
- sec_name_len = prefix_len + text_len;
- sec_name = alloca (sec_name_len + 1);
- memcpy (sec_name, prefix, prefix_len);
- memcpy (sec_name + prefix_len, text_name, text_len);
- sec_name[prefix_len + text_len] = '\0';
-
- /* Handle COMDAT group. */
- if (prefix != prefix_once && (text_seg->flags & SEC_LINK_ONCE) != 0)
- {
- char *section;
- size_t len, group_name_len;
- const char *group_name = elf_group_name (text_seg);
-
- if (group_name == NULL)
- {
- as_bad ("Group section `%s' has no group signature",
- segment_name (text_seg));
- ignore_rest_of_line ();
- return;
- }
- /* We have to construct a fake section directive. */
- group_name_len = strlen (group_name);
- if (idx)
- prefix_len = 13;
- else
- prefix_len = 16;
-
- len = (sec_name_len
- + prefix_len /* ,"aG",%sectiontype, */
- + group_name_len /* ,group_name */
- + 7); /* ,comdat */
-
- section = alloca (len + 1);
- memcpy (section, sec_name, sec_name_len);
- if (idx)
- memcpy (section + sec_name_len, ",\"aG\",%exidx,", 13);
- else
- memcpy (section + sec_name_len, ",\"aG\",%progbits,", 16);
- memcpy (section + sec_name_len + prefix_len, group_name, group_name_len);
- memcpy (section + len - 7, ",comdat", 7);
- section [len] = '\0';
- set_section (section);
- }
- else
- {
- set_section (sec_name);
- bfd_set_section_flags (stdoutput, now_seg,
- SEC_LOAD | SEC_ALLOC | SEC_READONLY);
- }
-
- /* Set the setion link for index tables. */
- if (idx)
- elf_linked_to_section (now_seg) = text_seg;
-}
-
-
-/* Start an unwind table entry. HAVE_DATA is nonzero if we have additional
- personality routine data. Returns zero, or the index table value for
- and inline entry. */
-
-static valueT
-create_unwind_entry (int have_data)
-{
- int size;
- addressT where;
- char *ptr;
- /* The current word of data. */
- valueT data;
- /* The number of bytes left in this word. */
- int n;
-
- finish_unwind_opcodes ();
-
- /* Remember the current text section. */
- unwind.saved_seg = now_seg;
- unwind.saved_subseg = now_subseg;
-
- start_unwind_section (now_seg, 0);
-
- if (unwind.personality_routine == NULL)
- {
- if (unwind.personality_index == -2)
- {
- if (have_data)
- as_bad (_("handerdata in cantunwind frame"));
- return 1; /* EXIDX_CANTUNWIND. */
- }
-
- /* Use a default personality routine if none is specified. */
- if (unwind.personality_index == -1)
- {
- if (unwind.opcode_count > 3)
- unwind.personality_index = 1;
- else
- unwind.personality_index = 0;
- }
-
- /* Space for the personality routine entry. */
- if (unwind.personality_index == 0)
- {
- if (unwind.opcode_count > 3)
- as_bad (_("too many unwind opcodes for personality routine 0"));
-
- if (!have_data)
- {
- /* All the data is inline in the index table. */
- data = 0x80;
- n = 3;
- while (unwind.opcode_count > 0)
- {
- unwind.opcode_count--;
- data = (data << 8) | unwind.opcodes[unwind.opcode_count];
- n--;
- }
-
- /* Pad with "finish" opcodes. */
- while (n--)
- data = (data << 8) | 0xb0;
-
- return data;
- }
- size = 0;
- }
- else
- /* We get two opcodes "free" in the first word. */
- size = unwind.opcode_count - 2;
- }
- else
- /* An extra byte is required for the opcode count. */
- size = unwind.opcode_count + 1;
-
- size = (size + 3) >> 2;
- if (size > 0xff)
- as_bad (_("too many unwind opcodes"));
-
- frag_align (2, 0, 0);
- record_alignment (now_seg, 2);
- unwind.table_entry = expr_build_dot ();
-
- /* Allocate the table entry. */
- ptr = frag_more ((size << 2) + 4);
- where = frag_now_fix () - ((size << 2) + 4);
-
- switch (unwind.personality_index)
- {
- case -1:
- /* ??? Should this be a PLT generating relocation? */
- /* Custom personality routine. */
- fix_new (frag_now, where, 4, unwind.personality_routine, 0, 1,
- BFD_RELOC_ARM_PREL31);
-
- where += 4;
- ptr += 4;
-
- /* Set the first byte to the number of additional words. */
- data = size - 1;
- n = 3;
- break;
-
- /* ABI defined personality routines. */
- case 0:
- /* Three opcodes bytes are packed into the first word. */
- data = 0x80;
- n = 3;
- break;
-
- case 1:
- case 2:
- /* The size and first two opcode bytes go in the first word. */
- data = ((0x80 + unwind.personality_index) << 8) | size;
- n = 2;
- break;
-
- default:
- /* Should never happen. */
- abort ();
- }
-
- /* Pack the opcodes into words (MSB first), reversing the list at the same
- time. */
- while (unwind.opcode_count > 0)
- {
- if (n == 0)
- {
- md_number_to_chars (ptr, data, 4);
- ptr += 4;
- n = 4;
- data = 0;
- }
- unwind.opcode_count--;
- n--;
- data = (data << 8) | unwind.opcodes[unwind.opcode_count];
- }
-
- /* Finish off the last word. */
- if (n < 4)
- {
- /* Pad with "finish" opcodes. */
- while (n--)
- data = (data << 8) | 0xb0;
-
- md_number_to_chars (ptr, data, 4);
- }
-
- if (!have_data)
- {
- /* Add an empty descriptor if there is no user-specified data. */
- ptr = frag_more (4);
- md_number_to_chars (ptr, 0, 4);
- }
-
- return 0;
-}
-
-
-/* Parse an unwind_fnstart directive. Simply records the current location. */
-
-static void
-s_arm_unwind_fnstart (int ignored ATTRIBUTE_UNUSED)
-{
- demand_empty_rest_of_line ();
- /* Mark the start of the function. */
- unwind.proc_start = expr_build_dot ();
-
- /* Reset the rest of the unwind info. */
- unwind.opcode_count = 0;
- unwind.table_entry = NULL;
- unwind.personality_routine = NULL;
- unwind.personality_index = -1;
- unwind.frame_size = 0;
- unwind.fp_offset = 0;
- unwind.fp_reg = 13;
- unwind.fp_used = 0;
- unwind.sp_restored = 0;
-}
-
-
-/* Parse a handlerdata directive. Creates the exception handling table entry
- for the function. */
-
-static void
-s_arm_unwind_handlerdata (int ignored ATTRIBUTE_UNUSED)
-{
- demand_empty_rest_of_line ();
- if (unwind.table_entry)
- as_bad (_("dupicate .handlerdata directive"));
-
- create_unwind_entry (1);
-}
-
-/* Parse an unwind_fnend directive. Generates the index table entry. */
-
-static void
-s_arm_unwind_fnend (int ignored ATTRIBUTE_UNUSED)
-{
- long where;
- char *ptr;
- valueT val;
-
- demand_empty_rest_of_line ();
-
- /* Add eh table entry. */
- if (unwind.table_entry == NULL)
- val = create_unwind_entry (0);
- else
- val = 0;
-
- /* Add index table entry. This is two words. */
- start_unwind_section (unwind.saved_seg, 1);
- frag_align (2, 0, 0);
- record_alignment (now_seg, 2);
-
- ptr = frag_more (8);
- where = frag_now_fix () - 8;
-
- /* Self relative offset of the function start. */
- fix_new (frag_now, where, 4, unwind.proc_start, 0, 1,
- BFD_RELOC_ARM_PREL31);
-
- /* Indicate dependency on EHABI-defined personality routines to the
- linker, if it hasn't been done already. */
- if (unwind.personality_index >= 0 && unwind.personality_index < 3)
- {
- char *name[] = { "__aeabi_unwind_cpp_pr0",
- "__aeabi_unwind_cpp_pr1",
- "__aeabi_unwind_cpp_pr2" };
- if (!(marked_pr_dependency & (1 << unwind.personality_index)))
- {
- symbolS *pr = symbol_find_or_make (name[unwind.personality_index]);
- fix_new (frag_now, where, 0, pr, 0, 1, BFD_RELOC_NONE);
- marked_pr_dependency |= 1 << unwind.personality_index;
- seg_info (now_seg)->tc_segment_info_data.marked_pr_dependency
- = marked_pr_dependency;
- }
- }
-
- if (val)
- /* Inline exception table entry. */
- md_number_to_chars (ptr + 4, val, 4);
- else
- /* Self relative offset of the table entry. */
- fix_new (frag_now, where + 4, 4, unwind.table_entry, 0, 1,
- BFD_RELOC_ARM_PREL31);
-
- /* Restore the original section. */
- subseg_set (unwind.saved_seg, unwind.saved_subseg);
-}
-
-
-/* Parse an unwind_cantunwind directive. */
-
-static void
-s_arm_unwind_cantunwind (int ignored ATTRIBUTE_UNUSED)
-{
- demand_empty_rest_of_line ();
- if (unwind.personality_routine || unwind.personality_index != -1)
- as_bad (_("personality routine specified for cantunwind frame"));
-
- unwind.personality_index = -2;
-}
-
-
-/* Parse a personalityindex directive. */
-
-static void
-s_arm_unwind_personalityindex (int ignored ATTRIBUTE_UNUSED)
-{
- expressionS exp;
-
- if (unwind.personality_routine || unwind.personality_index != -1)
- as_bad (_("duplicate .personalityindex directive"));
-
- SKIP_WHITESPACE ();
-
- expression (&exp);
-
- if (exp.X_op != O_constant
- || exp.X_add_number < 0 || exp.X_add_number > 15)
- {
- as_bad (_("bad personality routine number"));
- ignore_rest_of_line ();
- return;
- }
-
- unwind.personality_index = exp.X_add_number;
-
- demand_empty_rest_of_line ();
-}
-
-
-/* Parse a personality directive. */
-
-static void
-s_arm_unwind_personality (int ignored ATTRIBUTE_UNUSED)
-{
- char *name, *p, c;
-
- if (unwind.personality_routine || unwind.personality_index != -1)
- as_bad (_("duplicate .personality directive"));
-
- SKIP_WHITESPACE ();
- name = input_line_pointer;
- c = get_symbol_end ();
- p = input_line_pointer;
- unwind.personality_routine = symbol_find_or_make (name);
- *p = c;
- SKIP_WHITESPACE ();
- demand_empty_rest_of_line ();
-}
-
-
-/* Parse a directive saving core registers. */
-
-static void
-s_arm_unwind_save_core (void)
-{
- valueT op;
- long range;
- int n;
-
- SKIP_WHITESPACE ();
- range = reg_list (&input_line_pointer);
- if (range == FAIL)
- {
- as_bad (_("expected register list"));
- ignore_rest_of_line ();
- return;
- }
-
- demand_empty_rest_of_line ();
-
- /* Turn .unwind_movsp ip followed by .unwind_save {..., ip, ...}
- into .unwind_save {..., sp...}. We aren't bothered about the value of
- ip because it is clobbered by calls. */
- if (unwind.sp_restored && unwind.fp_reg == 12
- && (range & 0x3000) == 0x1000)
- {
- unwind.opcode_count--;
- unwind.sp_restored = 0;
- range = (range | 0x2000) & ~0x1000;
- unwind.pending_offset = 0;
- }
-
- /* See if we can use the short opcodes. These pop a block of upto 8
- registers starting with r4, plus maybe r14. */
- for (n = 0; n < 8; n++)
- {
- /* Break at the first non-saved register. */
- if ((range & (1 << (n + 4))) == 0)
- break;
- }
- /* See if there are any other bits set. */
- if (n == 0 || (range & (0xfff0 << n) & 0xbff0) != 0)
- {
- /* Use the long form. */
- op = 0x8000 | ((range >> 4) & 0xfff);
- add_unwind_opcode (op, 2);
- }
- else
- {
- /* Use the short form. */
- if (range & 0x4000)
- op = 0xa8; /* Pop r14. */
- else
- op = 0xa0; /* Do not pop r14. */
- op |= (n - 1);
- add_unwind_opcode (op, 1);
- }
-
- /* Pop r0-r3. */
- if (range & 0xf)
- {
- op = 0xb100 | (range & 0xf);
- add_unwind_opcode (op, 2);
- }
-
- /* Record the number of bytes pushed. */
- for (n = 0; n < 16; n++)
- {
- if (range & (1 << n))
- unwind.frame_size += 4;
- }
-}
-
-
-/* Parse a directive saving FPA registers. */
-
-static void
-s_arm_unwind_save_fpa (int reg)
-{
- expressionS exp;
- int num_regs;
- valueT op;
-
- /* Get Number of registers to transfer. */
- if (skip_past_comma (&input_line_pointer) != FAIL)
- expression (&exp);
- else
- exp.X_op = O_illegal;
-
- if (exp.X_op != O_constant)
- {
- as_bad (_("expected , <constant>"));
- ignore_rest_of_line ();
- return;
- }
-
- num_regs = exp.X_add_number;
-
- if (num_regs < 1 || num_regs > 4)
- {
- as_bad (_("number of registers must be in the range [1:4]"));
- ignore_rest_of_line ();
- return;
- }
-
- demand_empty_rest_of_line ();
-
- if (reg == 4)
- {
- /* Short form. */
- op = 0xb4 | (num_regs - 1);
- add_unwind_opcode (op, 1);
- }
- else
- {
- /* Long form. */
- op = 0xc800 | (reg << 4) | (num_regs - 1);
- add_unwind_opcode (op, 2);
- }
- unwind.frame_size += num_regs * 12;
-}
-
-
-/* Parse a directive saving VFP registers. */
-
-static void
-s_arm_unwind_save_vfp (void)
-{
- int count;
- int reg;
- valueT op;
-
- count = vfp_parse_reg_list (&input_line_pointer, &reg, 1);
- if (count == FAIL)
- {
- as_bad (_("expected register list"));
- ignore_rest_of_line ();
- return;
- }
-
- demand_empty_rest_of_line ();
-
- if (reg == 8)
- {
- /* Short form. */
- op = 0xb8 | (count - 1);
- add_unwind_opcode (op, 1);
- }
- else
- {
- /* Long form. */
- op = 0xb300 | (reg << 4) | (count - 1);
- add_unwind_opcode (op, 2);
- }
- unwind.frame_size += count * 8 + 4;
-}
-
-
-/* Parse a directive saving iWMMXt registers. */
-
-static void
-s_arm_unwind_save_wmmx (void)
-{
- int reg;
- int hi_reg;
- int i;
- unsigned wcg_mask;
- unsigned wr_mask;
- valueT op;
-
- if (*input_line_pointer == '{')
- input_line_pointer++;
-
- wcg_mask = 0;
- wr_mask = 0;
- do
- {
- reg = arm_reg_parse (&input_line_pointer,
- all_reg_maps[REG_TYPE_IWMMXT].htab);
-
- if (wr_register (reg))
- {
- i = reg & ~WR_PREFIX;
- if (wr_mask >> i)
- as_tsktsk (_("register list not in ascending order"));
- wr_mask |= 1 << i;
- }
- else if (wcg_register (reg))
- {
- i = (reg & ~WC_PREFIX) - 8;
- if (wcg_mask >> i)
- as_tsktsk (_("register list not in ascending order"));
- wcg_mask |= 1 << i;
- }
- else
- {
- as_bad (_("expected wr or wcgr"));
- goto error;
- }
-
- SKIP_WHITESPACE ();
- if (*input_line_pointer == '-')
- {
- hi_reg = arm_reg_parse (&input_line_pointer,
- all_reg_maps[REG_TYPE_IWMMXT].htab);
- if (wr_register (reg) && wr_register (hi_reg))
- {
- for (; reg < hi_reg; reg++)
- wr_mask |= 1 << (reg & ~WR_PREFIX);
- }
- else if (wcg_register (reg) && wcg_register (hi_reg))
- {
- for (; reg < hi_reg; reg++)
- wcg_mask |= 1 << ((reg & ~WC_PREFIX) - 8);
- }
- else
- {
- as_bad (_("bad register range"));
- goto error;
- }
- }
- }
- while (skip_past_comma (&input_line_pointer) != FAIL);
-
- SKIP_WHITESPACE ();
- if (*input_line_pointer == '}')
- input_line_pointer++;
-
- demand_empty_rest_of_line ();
-
- if (wr_mask && wcg_mask)
- {
- as_bad (_("inconsistent register types"));
- goto error;
- }
-
- /* Generate any deferred opcodes becuuse we're going to be looking at
- the list. */
- flush_pending_unwind ();
-
- if (wcg_mask)
- {
- for (i = 0; i < 16; i++)
- {
- if (wcg_mask & (1 << i))
- unwind.frame_size += 4;
- }
- op = 0xc700 | wcg_mask;
- add_unwind_opcode (op, 2);
- }
- else
- {
- for (i = 0; i < 16; i++)
- {
- if (wr_mask & (1 << i))
- unwind.frame_size += 8;
- }
- /* Attempt to combine with a previous opcode. We do this because gcc
- likes to output separate unwind directives for a single block of
- registers. */
- if (unwind.opcode_count > 0)
- {
- i = unwind.opcodes[unwind.opcode_count - 1];
- if ((i & 0xf8) == 0xc0)
- {
- i &= 7;
- /* Only merge if the blocks are contiguous. */
- if (i < 6)
- {
- if ((wr_mask & 0xfe00) == (1 << 9))
- {
- wr_mask |= ((1 << (i + 11)) - 1) & 0xfc00;
- unwind.opcode_count--;
- }
- }
- else if (i == 6 && unwind.opcode_count >= 2)
- {
- i = unwind.opcodes[unwind.opcode_count - 2];
- reg = i >> 4;
- i &= 0xf;
-
- op = 0xffff << (reg - 1);
- if (reg > 0
- || ((wr_mask & op) == (1u << (reg - 1))))
- {
- op = (1 << (reg + i + 1)) - 1;
- op &= ~((1 << reg) - 1);
- wr_mask |= op;
- unwind.opcode_count -= 2;
- }
- }
- }
- }
-
- hi_reg = 15;
- /* We want to generate opcodes in the order the registers have been
- saved, ie. descending order. */
- for (reg = 15; reg >= -1; reg--)
- {
- /* Save registers in blocks. */
- if (reg < 0
- || !(wr_mask & (1 << reg)))
- {
- /* We found an unsaved reg. Generate opcodes to save the
- preceeding block. */
- if (reg != hi_reg)
- {
- if (reg == 9)
- {
- /* Short form. */
- op = 0xc0 | (hi_reg - 10);
- add_unwind_opcode (op, 1);
- }
- else
- {
- /* Long form. */
- op = 0xc600 | ((reg + 1) << 4) | ((hi_reg - reg) - 1);
- add_unwind_opcode (op, 2);
- }
- }
- hi_reg = reg - 1;
- }
- }
- }
- return;
-error:
- ignore_rest_of_line ();
-}
-
-
-/* Parse an unwind_save directive. */
-
-static void
-s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED)
-{
- char *saved_ptr;
- int reg;
-
- /* Figure out what sort of save we have. */
- SKIP_WHITESPACE ();
- saved_ptr = input_line_pointer;
-
- reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_FN].htab);
- if (reg != FAIL)
- {
- s_arm_unwind_save_fpa (reg);
- return;
- }
-
- if (*input_line_pointer == '{')
- input_line_pointer++;
-
- SKIP_WHITESPACE ();
-
- reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_RN].htab);
- if (reg != FAIL)
- {
- input_line_pointer = saved_ptr;
- s_arm_unwind_save_core ();
- return;
- }
-
- reg = arm_reg_parse (&input_line_pointer, all_reg_maps[REG_TYPE_DN].htab);
- if (reg != FAIL)
- {
- input_line_pointer = saved_ptr;
- s_arm_unwind_save_vfp ();
- return;
- }
-
- reg = arm_reg_parse (&input_line_pointer,
- all_reg_maps[REG_TYPE_IWMMXT].htab);
- if (reg != FAIL)
- {
- input_line_pointer = saved_ptr;
- s_arm_unwind_save_wmmx ();
- return;
- }
-
- /* TODO: Maverick registers. */
- as_bad (_("unrecognised register"));
-}
-
-
-/* Parse an unwind_movsp directive. */
-
-static void
-s_arm_unwind_movsp (int ignored ATTRIBUTE_UNUSED)
-{
- int reg;
- valueT op;
-
- SKIP_WHITESPACE ();
- reg = reg_required_here (&input_line_pointer, -1);
- if (reg == FAIL)
- {
- as_bad (_("ARM register expected"));
- ignore_rest_of_line ();
- return;
- }
-
- if (reg == 13 || reg == 15)
- {
- as_bad (_("r%d not permitted in .unwind_movsp directive"), reg);
- ignore_rest_of_line ();
- return;
- }
-
- if (unwind.fp_reg != 13)
- as_bad (_("unexpected .unwind_movsp directive"));
-
- /* Generate opcode to restore the value. */
- op = 0x90 | reg;
- add_unwind_opcode (op, 1);
-
- /* Record the information for later. */
- unwind.fp_reg = reg;
- unwind.fp_offset = unwind.frame_size;
- unwind.sp_restored = 1;
- demand_empty_rest_of_line ();
-}
-
-
-/* Parse #<number>. */
-
-static int
-require_hashconst (int * val)
-{
- expressionS exp;
-
- SKIP_WHITESPACE ();
- if (*input_line_pointer == '#')
- {
- input_line_pointer++;
- expression (&exp);
- }
- else
- exp.X_op = O_illegal;
-
- if (exp.X_op != O_constant)
- {
- as_bad (_("expected #constant"));
- ignore_rest_of_line ();
- return FAIL;
- }
- *val = exp.X_add_number;
- return SUCCESS;
-}
-
-/* Parse an unwind_pad directive. */
-
-static void
-s_arm_unwind_pad (int ignored ATTRIBUTE_UNUSED)
-{
- int offset;
-
- if (require_hashconst (&offset) == FAIL)
- return;
-
- if (offset & 3)
- {
- as_bad (_("stack increment must be multiple of 4"));
- ignore_rest_of_line ();
- return;
- }
-
- /* Don't generate any opcodes, just record the details for later. */
- unwind.frame_size += offset;
- unwind.pending_offset += offset;
-
- demand_empty_rest_of_line ();
-}
-
-/* Parse an unwind_setfp directive. */
-
-static void
-s_arm_unwind_setfp (int ignored ATTRIBUTE_UNUSED)
-{
- int sp_reg;
- int fp_reg;
- int offset;
-
- fp_reg = reg_required_here (&input_line_pointer, -1);
- if (skip_past_comma (&input_line_pointer) == FAIL)
- sp_reg = FAIL;
- else
- sp_reg = reg_required_here (&input_line_pointer, -1);
-
- if (fp_reg == FAIL || sp_reg == FAIL)
- {
- as_bad (_("expected <reg>, <reg>"));
- ignore_rest_of_line ();
- return;
- }
-
- /* Optonal constant. */
- if (skip_past_comma (&input_line_pointer) != FAIL)
- {
- if (require_hashconst (&offset) == FAIL)
- return;
- }
- else
- offset = 0;
-
- demand_empty_rest_of_line ();
-
- if (sp_reg != 13 && sp_reg != unwind.fp_reg)
- {
- as_bad (_("register must be either sp or set by a previous"
- "unwind_movsp directive"));
- return;
- }
-
- /* Don't generate any opcodes, just record the information for later. */
- unwind.fp_reg = fp_reg;
- unwind.fp_used = 1;
- if (sp_reg == 13)
- unwind.fp_offset = unwind.frame_size - offset;
- else
- unwind.fp_offset -= offset;
-}
-
-/* Parse an unwind_raw directive. */
-
-static void
-s_arm_unwind_raw (int ignored ATTRIBUTE_UNUSED)
-{
- expressionS exp;
- /* This is an arbitary limit. */
- unsigned char op[16];
- int count;
-
- SKIP_WHITESPACE ();
- expression (&exp);
- if (exp.X_op == O_constant
- && skip_past_comma (&input_line_pointer) != FAIL)
- {
- unwind.frame_size += exp.X_add_number;
- expression (&exp);
- }
- else
- exp.X_op = O_illegal;
-
- if (exp.X_op != O_constant)
- {
- as_bad (_("expected <offset>, <opcode>"));
- ignore_rest_of_line ();
- return;
- }
-
- count = 0;
-
- /* Parse the opcode. */
- for (;;)
- {
- if (count >= 16)
- {
- as_bad (_("unwind opcode too long"));
- ignore_rest_of_line ();
- }
- if (exp.X_op != O_constant || exp.X_add_number & ~0xff)
- {
- as_bad (_("invalid unwind opcode"));
- ignore_rest_of_line ();
- return;
- }
- op[count++] = exp.X_add_number;
-
- /* Parse the next byte. */
- if (skip_past_comma (&input_line_pointer) == FAIL)
- break;
-
- expression (&exp);
- }
-
- /* Add the opcode bytes in reverse order. */
- while (count--)
- add_unwind_opcode (op[count], 1);
-
- demand_empty_rest_of_line ();
-}
-
-#endif /* OBJ_ELF */
-
-/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
- of an rs_align_code fragment. */
-
-void
-arm_handle_align (fragS * fragP)
-{
- static char const arm_noop[4] = { 0x00, 0x00, 0xa0, 0xe1 };
- static char const thumb_noop[2] = { 0xc0, 0x46 };
- static char const arm_bigend_noop[4] = { 0xe1, 0xa0, 0x00, 0x00 };
- static char const thumb_bigend_noop[2] = { 0x46, 0xc0 };
-
- int bytes, fix, noop_size;
- char * p;
- const char * noop;
-
- if (fragP->fr_type != rs_align_code)
- return;
-
- bytes = fragP->fr_next->fr_address - fragP->fr_address - fragP->fr_fix;
- p = fragP->fr_literal + fragP->fr_fix;
- fix = 0;
-
- if (bytes > MAX_MEM_FOR_RS_ALIGN_CODE)
- bytes &= MAX_MEM_FOR_RS_ALIGN_CODE;
-
- if (fragP->tc_frag_data)
- {
- if (target_big_endian)
- noop = thumb_bigend_noop;
- else
- noop = thumb_noop;
- noop_size = sizeof (thumb_noop);
- }
- else
- {
- if (target_big_endian)
- noop = arm_bigend_noop;
- else
- noop = arm_noop;
- noop_size = sizeof (arm_noop);
- }
-
- if (bytes & (noop_size - 1))
- {
- fix = bytes & (noop_size - 1);
- memset (p, 0, fix);
- p += fix;
- bytes -= fix;
- }
-
- while (bytes >= noop_size)
- {
- memcpy (p, noop, noop_size);
- p += noop_size;
- bytes -= noop_size;
- fix += noop_size;
- }
-
- fragP->fr_fix += fix;
- fragP->fr_var = noop_size;
-}
-
-/* Called from md_do_align. Used to create an alignment
- frag in a code section. */
-
-void
-arm_frag_align_code (int n, int max)
-{
- char * p;
-
- /* We assume that there will never be a requirement
- to support alignments greater than 32 bytes. */
- if (max > MAX_MEM_FOR_RS_ALIGN_CODE)
- as_fatal (_("alignments greater than 32 bytes not supported in .text sections."));
-
- p = frag_var (rs_align_code,
- MAX_MEM_FOR_RS_ALIGN_CODE,
- 1,
- (relax_substateT) max,
- (symbolS *) NULL,
- (offsetT) n,
- (char *) NULL);
- *p = 0;
-}
-
-/* Perform target specific initialisation of a frag. */
-
-void
-arm_init_frag (fragS * fragP)
-{
- /* Record whether this frag is in an ARM or a THUMB area. */
- fragP->tc_frag_data = thumb_mode;
-}
-
-#ifdef OBJ_ELF
-
-/* Convert REGNAME to a DWARF-2 register number. */
-
-int
-tc_arm_regname_to_dw2regnum (const char *regname)
-{
- unsigned int i;
-
- for (i = 0; rn_table[i].name; i++)
- if (streq (regname, rn_table[i].name))
- return rn_table[i].number;
-
- return -1;
-}
-
-/* Initialize the DWARF-2 unwind information for this procedure. */
-
-void
-tc_arm_frame_initial_instructions (void)
-{
- cfi_add_CFA_def_cfa (REG_SP, 0);
-}
-#endif
-
-/* This table describes all the machine specific pseudo-ops the assembler
- has to support. The fields are:
- pseudo-op name without dot
- function to call to execute this pseudo-op
- Integer arg to pass to the function. */
-
-const pseudo_typeS md_pseudo_table[] =
-{
- /* Never called because '.req' does not start a line. */
- { "req", s_req, 0 },
- { "unreq", s_unreq, 0 },
- { "bss", s_bss, 0 },
- { "align", s_align, 0 },
- { "arm", s_arm, 0 },
- { "thumb", s_thumb, 0 },
- { "code", s_code, 0 },
- { "force_thumb", s_force_thumb, 0 },
- { "thumb_func", s_thumb_func, 0 },
- { "thumb_set", s_thumb_set, 0 },
- { "even", s_even, 0 },
- { "ltorg", s_ltorg, 0 },
- { "pool", s_ltorg, 0 },
-#ifdef OBJ_ELF
- { "word", s_arm_elf_cons, 4 },
- { "long", s_arm_elf_cons, 4 },
- { "rel31", s_arm_rel31, 0 },
- { "fnstart", s_arm_unwind_fnstart, 0 },
- { "fnend", s_arm_unwind_fnend, 0 },
- { "cantunwind", s_arm_unwind_cantunwind, 0 },
- { "personality", s_arm_unwind_personality, 0 },
- { "personalityindex", s_arm_unwind_personalityindex, 0 },
- { "handlerdata", s_arm_unwind_handlerdata, 0 },
- { "save", s_arm_unwind_save, 0 },
- { "movsp", s_arm_unwind_movsp, 0 },
- { "pad", s_arm_unwind_pad, 0 },
- { "setfp", s_arm_unwind_setfp, 0 },
- { "unwind_raw", s_arm_unwind_raw, 0 },
-#else
- { "word", cons, 4},
-#endif
- { "extend", float_cons, 'x' },
- { "ldouble", float_cons, 'x' },
- { "packed", float_cons, 'p' },
- { 0, 0, 0 }
-};