#ifdef OBJ_ELF
#include "elf/arm.h"
-#include "dwarf2dbg.h"
#include "dw2gencfi.h"
#endif
-/* XXX Set this to 1 after the next binutils release. */
-#define WARN_DEPRECATED 0
+#include "dwarf2dbg.h"
+
+#define WARN_DEPRECATED 1
#ifdef OBJ_ELF
/* Must be at least the size of the largest unwind opcode (currently two). */
#endif /* OBJ_ELF */
+/* Results from operand parsing worker functions. */
+
+typedef enum
+{
+ PARSE_OPERAND_SUCCESS,
+ PARSE_OPERAND_FAIL,
+ PARSE_OPERAND_FAIL_NO_BACKTRACK
+} parse_operand_result;
+
enum arm_float_abi
{
ARM_FLOAT_ABI_HARD,
static const arm_feature_set *march_cpu_opt = NULL;
static const arm_feature_set *march_fpu_opt = NULL;
static const arm_feature_set *mfpu_opt = NULL;
+static const arm_feature_set *object_arch = NULL;
/* Constants for known architecture features. */
static const arm_feature_set fpu_default = FPU_DEFAULT;
static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2;
static const arm_feature_set arm_arch_none = ARM_ARCH_NONE;
+static const arm_feature_set arm_cext_iwmmxt2 =
+ ARM_FEATURE (0, ARM_CEXT_IWMMXT2);
static const arm_feature_set arm_cext_iwmmxt =
ARM_FEATURE (0, ARM_CEXT_IWMMXT);
static const arm_feature_set arm_cext_xscale =
# else
static int meabi_flags = EF_ARM_EABI_UNKNOWN;
# endif
+
+bfd_boolean
+arm_is_eabi(void)
+{
+ return (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4);
+}
#endif
#ifdef OBJ_ELF
&& (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD))
|| (type == REG_TYPE_NSDQ
&& (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD
- || reg->type == REG_TYPE_NQ)))
+ || reg->type == REG_TYPE_NQ))
+ || (type == REG_TYPE_MMXWC
+ && (reg->type == REG_TYPE_MMXWCG)))
type = reg->type;
if (type != reg->type)
static enum mstate mapstate = MAP_UNDEFINED;
-static void
+void
mapping_state (enum mstate state)
{
symbolS * symbolP;
}
-/* Parse a directive saving VFP registers. */
+/* Parse a directive saving VFP registers for ARMv6 and above. */
+
+static void
+s_arm_unwind_save_vfp_armv6 (void)
+{
+ int count;
+ unsigned int start;
+ valueT op;
+ int num_vfpv3_regs = 0;
+ int num_regs_below_16;
+
+ count = parse_vfp_reg_list (&input_line_pointer, &start, REGLIST_VFP_D);
+ if (count == FAIL)
+ {
+ as_bad (_("expected register list"));
+ ignore_rest_of_line ();
+ return;
+ }
+
+ demand_empty_rest_of_line ();
+
+ /* We always generate FSTMD/FLDMD-style unwinding opcodes (rather
+ than FSTMX/FLDMX-style ones). */
+
+ /* Generate opcode for (VFPv3) registers numbered in the range 16 .. 31. */
+ if (start >= 16)
+ num_vfpv3_regs = count;
+ else if (start + count > 16)
+ num_vfpv3_regs = start + count - 16;
+
+ if (num_vfpv3_regs > 0)
+ {
+ int start_offset = start > 16 ? start - 16 : 0;
+ op = 0xc800 | (start_offset << 4) | (num_vfpv3_regs - 1);
+ add_unwind_opcode (op, 2);
+ }
+
+ /* Generate opcode for registers numbered in the range 0 .. 15. */
+ num_regs_below_16 = num_vfpv3_regs > 0 ? 16 - (int) start : count;
+ assert (num_regs_below_16 + num_vfpv3_regs == count);
+ if (num_regs_below_16 > 0)
+ {
+ op = 0xc900 | (start << 4) | (num_regs_below_16 - 1);
+ add_unwind_opcode (op, 2);
+ }
+
+ unwind.frame_size += count * 8;
+}
+
+
+/* Parse a directive saving VFP registers for pre-ARMv6. */
static void
s_arm_unwind_save_vfp (void)
op = 0xffff << (reg - 1);
if (reg > 0
- || ((mask & op) == (1u << (reg - 1))))
+ && ((mask & op) == (1u << (reg - 1))))
{
op = (1 << (reg + i + 1)) - 1;
op &= ~((1 << reg) - 1);
}
-/* Parse an unwind_save directive. */
+/* Parse an unwind_save directive.
+ If the argument is non-zero, this is a .vsave directive. */
static void
-s_arm_unwind_save (int ignored ATTRIBUTE_UNUSED)
+s_arm_unwind_save (int arch_v6)
{
char *peek;
struct reg_entry *reg;
return;
case REG_TYPE_RN: s_arm_unwind_save_core (); return;
- case REG_TYPE_VFD: s_arm_unwind_save_vfp (); return;
+ case REG_TYPE_VFD:
+ if (arch_v6)
+ s_arm_unwind_save_vfp_armv6 ();
+ else
+ 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;
{
int reg;
valueT op;
+ int offset;
reg = arm_reg_parse (&input_line_pointer, REG_TYPE_RN);
if (reg == FAIL)
ignore_rest_of_line ();
return;
}
+
+ /* Optional constant. */
+ if (skip_past_comma (&input_line_pointer) != FAIL)
+ {
+ if (immediate_for_directive (&offset) == FAIL)
+ return;
+ }
+ else
+ offset = 0;
+
demand_empty_rest_of_line ();
if (reg == REG_SP || reg == REG_PC)
/* Record the information for later. */
unwind.fp_reg = reg;
- unwind.fp_offset = unwind.frame_size;
+ unwind.fp_offset = unwind.frame_size - offset;
unwind.sp_restored = 1;
}
#endif /* OBJ_ELF */
static void s_arm_arch (int);
+static void s_arm_object_arch (int);
static void s_arm_cpu (int);
static void s_arm_fpu (int);
+#ifdef TE_PE
+
+static void
+pe_directive_secrel (int dummy ATTRIBUTE_UNUSED)
+{
+ expressionS exp;
+
+ do
+ {
+ expression (&exp);
+ if (exp.X_op == O_symbol)
+ exp.X_op = O_secrel;
+
+ emit_expr (&exp, 4);
+ }
+ while (*input_line_pointer++ == ',');
+
+ input_line_pointer--;
+ demand_empty_rest_of_line ();
+}
+#endif /* TE_PE */
+
/* This table describes all the machine specific pseudo-ops the assembler
has to support. The fields are:
pseudo-op name without dot
{ "syntax", s_syntax, 0 },
{ "cpu", s_arm_cpu, 0 },
{ "arch", s_arm_arch, 0 },
+ { "object_arch", s_arm_object_arch, 0 },
{ "fpu", s_arm_fpu, 0 },
#ifdef OBJ_ELF
{ "word", s_arm_elf_cons, 4 },
{ "personalityindex", s_arm_unwind_personalityindex, 0 },
{ "handlerdata", s_arm_unwind_handlerdata, 0 },
{ "save", s_arm_unwind_save, 0 },
+ { "vsave", s_arm_unwind_save, 1 },
{ "movsp", s_arm_unwind_movsp, 0 },
{ "pad", s_arm_unwind_pad, 0 },
{ "setfp", s_arm_unwind_setfp, 0 },
{ "eabi_attribute", s_arm_eabi_attribute, 0 },
#else
{ "word", cons, 4},
+
+ /* These are used for dwarf. */
+ {"2byte", cons, 2},
+ {"4byte", cons, 4},
+ {"8byte", cons, 8},
+ /* These are used for dwarf2. */
+ { "file", (void (*) (int)) dwarf2_directive_file, 0 },
+ { "loc", dwarf2_directive_loc, 0 },
+ { "loc_mark_labels", dwarf2_directive_loc_mark_labels, 0 },
#endif
{ "extend", float_cons, 'x' },
{ "ldouble", float_cons, 'x' },
{ "packed", float_cons, 'p' },
+#ifdef TE_PE
+ {"secrel32", pe_directive_secrel, 0},
+#endif
{ 0, 0, 0 }
};
\f
}
/* Less-generic immediate-value read function with the possibility of loading a
- big (64-bit) immediate, as required by Neon VMOV and VMVN immediate
+ big (64-bit) immediate, as required by Neon VMOV, VMVN and logic immediate
instructions. Puts the result directly in inst.operands[i]. */
static int
my_get_expression (&exp, &ptr, GE_OPT_PREFIX_BIG);
if (exp.X_op == O_constant)
- inst.operands[i].imm = exp.X_add_number;
+ {
+ inst.operands[i].imm = exp.X_add_number & 0xffffffff;
+ /* If we're on a 64-bit host, then a 64-bit number can be returned using
+ O_constant. We have to be careful not to break compilation for
+ 32-bit X_add_number, though. */
+ if ((exp.X_add_number & ~0xffffffffl) != 0)
+ {
+ /* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */
+ inst.operands[i].reg = ((exp.X_add_number >> 16) >> 16) & 0xffffffff;
+ inst.operands[i].regisimm = 1;
+ }
+ }
else if (exp.X_op == O_big
&& LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32
&& LITTLENUM_NUMBER_OF_BITS * exp.X_add_number <= 64)
return SUCCESS;
}
+/* Group relocation information. Each entry in the table contains the
+ textual name of the relocation as may appear in assembler source
+ and must end with a colon.
+ Along with this textual name are the relocation codes to be used if
+ the corresponding instruction is an ALU instruction (ADD or SUB only),
+ an LDR, an LDRS, or an LDC. */
+
+struct group_reloc_table_entry
+{
+ const char *name;
+ int alu_code;
+ int ldr_code;
+ int ldrs_code;
+ int ldc_code;
+};
+
+typedef enum
+{
+ /* Varieties of non-ALU group relocation. */
+
+ GROUP_LDR,
+ GROUP_LDRS,
+ GROUP_LDC
+} group_reloc_type;
+
+static struct group_reloc_table_entry group_reloc_table[] =
+ { /* Program counter relative: */
+ { "pc_g0_nc",
+ BFD_RELOC_ARM_ALU_PC_G0_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "pc_g0",
+ BFD_RELOC_ARM_ALU_PC_G0, /* ALU */
+ BFD_RELOC_ARM_LDR_PC_G0, /* LDR */
+ BFD_RELOC_ARM_LDRS_PC_G0, /* LDRS */
+ BFD_RELOC_ARM_LDC_PC_G0 }, /* LDC */
+ { "pc_g1_nc",
+ BFD_RELOC_ARM_ALU_PC_G1_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "pc_g1",
+ BFD_RELOC_ARM_ALU_PC_G1, /* ALU */
+ BFD_RELOC_ARM_LDR_PC_G1, /* LDR */
+ BFD_RELOC_ARM_LDRS_PC_G1, /* LDRS */
+ BFD_RELOC_ARM_LDC_PC_G1 }, /* LDC */
+ { "pc_g2",
+ BFD_RELOC_ARM_ALU_PC_G2, /* ALU */
+ BFD_RELOC_ARM_LDR_PC_G2, /* LDR */
+ BFD_RELOC_ARM_LDRS_PC_G2, /* LDRS */
+ BFD_RELOC_ARM_LDC_PC_G2 }, /* LDC */
+ /* Section base relative */
+ { "sb_g0_nc",
+ BFD_RELOC_ARM_ALU_SB_G0_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "sb_g0",
+ BFD_RELOC_ARM_ALU_SB_G0, /* ALU */
+ BFD_RELOC_ARM_LDR_SB_G0, /* LDR */
+ BFD_RELOC_ARM_LDRS_SB_G0, /* LDRS */
+ BFD_RELOC_ARM_LDC_SB_G0 }, /* LDC */
+ { "sb_g1_nc",
+ BFD_RELOC_ARM_ALU_SB_G1_NC, /* ALU */
+ 0, /* LDR */
+ 0, /* LDRS */
+ 0 }, /* LDC */
+ { "sb_g1",
+ BFD_RELOC_ARM_ALU_SB_G1, /* ALU */
+ BFD_RELOC_ARM_LDR_SB_G1, /* LDR */
+ BFD_RELOC_ARM_LDRS_SB_G1, /* LDRS */
+ BFD_RELOC_ARM_LDC_SB_G1 }, /* LDC */
+ { "sb_g2",
+ BFD_RELOC_ARM_ALU_SB_G2, /* ALU */
+ BFD_RELOC_ARM_LDR_SB_G2, /* LDR */
+ BFD_RELOC_ARM_LDRS_SB_G2, /* LDRS */
+ BFD_RELOC_ARM_LDC_SB_G2 } }; /* LDC */
+
+/* Given the address of a pointer pointing to the textual name of a group
+ relocation as may appear in assembler source, attempt to find its details
+ in group_reloc_table. The pointer will be updated to the character after
+ the trailing colon. On failure, FAIL will be returned; SUCCESS
+ otherwise. On success, *entry will be updated to point at the relevant
+ group_reloc_table entry. */
+
+static int
+find_group_reloc_table_entry (char **str, struct group_reloc_table_entry **out)
+{
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE (group_reloc_table); i++)
+ {
+ int length = strlen (group_reloc_table[i].name);
+
+ if (strncasecmp (group_reloc_table[i].name, *str, length) == 0 &&
+ (*str)[length] == ':')
+ {
+ *out = &group_reloc_table[i];
+ *str += (length + 1);
+ return SUCCESS;
+ }
+ }
+
+ return FAIL;
+}
+
+/* Parse a <shifter_operand> for an ARM data processing instruction
+ (as for parse_shifter_operand) where group relocations are allowed:
+
+ #<immediate>
+ #<immediate>, <rotate>
+ #:<group_reloc>:<expression>
+ <Rm>
+ <Rm>, <shift>
+
+ where <group_reloc> is one of the strings defined in group_reloc_table.
+ The hashes are optional.
+
+ Everything else is as for parse_shifter_operand. */
+
+static parse_operand_result
+parse_shifter_operand_group_reloc (char **str, int i)
+{
+ /* Determine if we have the sequence of characters #: or just :
+ coming next. If we do, then we check for a group relocation.
+ If we don't, punt the whole lot to parse_shifter_operand. */
+
+ if (((*str)[0] == '#' && (*str)[1] == ':')
+ || (*str)[0] == ':')
+ {
+ struct group_reloc_table_entry *entry;
+
+ if ((*str)[0] == '#')
+ (*str) += 2;
+ else
+ (*str)++;
+
+ /* Try to parse a group relocation. Anything else is an error. */
+ if (find_group_reloc_table_entry (str, &entry) == FAIL)
+ {
+ inst.error = _("unknown group relocation");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
+
+ /* We now have the group relocation table entry corresponding to
+ the name in the assembler source. Next, we parse the expression. */
+ if (my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX))
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+
+ /* Record the relocation type (always the ALU variant here). */
+ inst.reloc.type = entry->alu_code;
+ assert (inst.reloc.type != 0);
+
+ return PARSE_OPERAND_SUCCESS;
+ }
+ else
+ return parse_shifter_operand (str, i) == SUCCESS
+ ? PARSE_OPERAND_SUCCESS : PARSE_OPERAND_FAIL;
+
+ /* Never reached. */
+}
+
/* Parse all forms of an ARM address expression. Information is written
to inst.operands[i] and/or inst.reloc.
It is the caller's responsibility to check for addressing modes not
supported by the instruction, and to set inst.reloc.type. */
-static int
-parse_address (char **str, int i)
+static parse_operand_result
+parse_address_main (char **str, int i, int group_relocations,
+ group_reloc_type group_type)
{
char *p = *str;
int reg;
/* else a load-constant pseudo op, no special treatment needed here */
if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
- return FAIL;
+ return PARSE_OPERAND_FAIL;
*str = p;
- return SUCCESS;
+ return PARSE_OPERAND_SUCCESS;
}
if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
{
inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
inst.operands[i].reg = reg;
inst.operands[i].isreg = 1;
if (skip_past_comma (&p) == SUCCESS)
if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
else if (skip_past_char (&p, ':') == SUCCESS)
{
if (exp.X_op != O_constant)
{
inst.error = _("alignment must be constant");
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
inst.operands[i].imm = exp.X_add_number << 8;
inst.operands[i].immisalign = 1;
inst.operands[i].negative = 0;
p--;
}
- if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return FAIL;
+
+ if (group_relocations &&
+ ((*p == '#' && *(p + 1) == ':') || *p == ':'))
+
+ {
+ struct group_reloc_table_entry *entry;
+
+ /* Skip over the #: or : sequence. */
+ if (*p == '#')
+ p += 2;
+ else
+ p++;
+
+ /* Try to parse a group relocation. Anything else is an
+ error. */
+ if (find_group_reloc_table_entry (&p, &entry) == FAIL)
+ {
+ inst.error = _("unknown group relocation");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
+
+ /* We now have the group relocation table entry corresponding to
+ the name in the assembler source. Next, we parse the
+ expression. */
+ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+
+ /* Record the relocation type. */
+ switch (group_type)
+ {
+ case GROUP_LDR:
+ inst.reloc.type = entry->ldr_code;
+ break;
+
+ case GROUP_LDRS:
+ inst.reloc.type = entry->ldrs_code;
+ break;
+
+ case GROUP_LDC:
+ inst.reloc.type = entry->ldc_code;
+ break;
+
+ default:
+ assert (0);
+ }
+
+ if (inst.reloc.type == 0)
+ {
+ inst.error = _("this group relocation is not allowed on this instruction");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
+ }
+ else
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return PARSE_OPERAND_FAIL;
}
}
if (skip_past_char (&p, ']') == FAIL)
{
inst.error = _("']' expected");
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
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;
+ return PARSE_OPERAND_FAIL;
if (skip_past_char (&p, '}') == FAIL)
{
inst.error = _("'}' expected at end of 'option' field");
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
if (inst.operands[i].preind)
{
inst.error = _("cannot combine index with option");
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
*str = p;
- return SUCCESS;
+ return PARSE_OPERAND_SUCCESS;
}
else
{
if (inst.operands[i].preind)
{
inst.error = _("cannot combine pre- and post-indexing");
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
if (*p == '+') p++;
if (skip_past_comma (&p) == SUCCESS)
if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
else
{
p--;
}
if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return FAIL;
+ return PARSE_OPERAND_FAIL;
}
}
}
inst.reloc.exp.X_add_number = 0;
}
*str = p;
- return SUCCESS;
+ return PARSE_OPERAND_SUCCESS;
+}
+
+static int
+parse_address (char **str, int i)
+{
+ return parse_address_main (str, i, 0, 0) == PARSE_OPERAND_SUCCESS
+ ? SUCCESS : FAIL;
+}
+
+static parse_operand_result
+parse_address_group_reloc (char **str, int i, group_reloc_type type)
+{
+ return parse_address_main (str, i, 1, type);
}
/* Parse an operand for a MOVW or MOVT instruction. */
OP_VMOV, /* Neon VMOV operands. */
OP_RNDQ_IMVNb,/* Neon D or Q reg, or immediate good for VMVN. */
OP_RNDQ_I63b, /* Neon D or Q reg, or immediate for shift. */
+ OP_RIWR_I32z, /* iWMMXt wR register, or immediate 0 .. 32 for iWMMXt2. */
OP_I0, /* immediate zero */
OP_I7, /* immediate value 0 .. 7 */
OP_I31b, /* 0 .. 31 */
OP_SH, /* shifter operand */
+ OP_SHG, /* shifter operand with possible group relocation */
OP_ADDR, /* Memory address expression (any mode) */
+ OP_ADDRGLDR, /* Mem addr expr (any mode) with possible LDR group reloc */
+ OP_ADDRGLDRS, /* Mem addr expr (any mode) with possible LDRS group reloc */
+ OP_ADDRGLDC, /* Mem addr expr (any mode) with possible LDC group reloc */
OP_EXP, /* arbitrary expression */
OP_EXPi, /* same, with optional immediate prefix */
OP_EXPr, /* same, with optional relocation suffix */
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 */
+ OP_RIWC_RIWG, /* iWMMXt wC or wCG reg */
/* Optional operands. */
OP_oI7b, /* immediate, prefix optional, 0 .. 7 */
const char *backtrack_error = 0;
int i, val, backtrack_index = 0;
enum arm_reg_type rtype;
+ parse_operand_result result;
#define po_char_or_fail(chr) do { \
if (skip_past_char (&str, chr) == FAIL) \
goto failure; \
} while (0)
+#define po_misc_or_fail_no_backtrack(expr) do { \
+ result = expr; \
+ if (result == PARSE_OPERAND_FAIL_NO_BACKTRACK)\
+ backtrack_pos = 0; \
+ if (result != PARSE_OPERAND_SUCCESS) \
+ goto failure; \
+} while (0)
+
skip_whitespace (str);
for (i = 0; upat[i] != OP_stop; i++)
case OP_NILO:
{
po_reg_or_goto (REG_TYPE_NDQ, try_imm);
+ inst.operands[i].present = 1;
i++;
skip_past_comma (&str);
po_reg_or_goto (REG_TYPE_NDQ, one_reg_only);
inst.operands[i-1].present = 0;
break;
try_imm:
- /* Immediate gets verified properly later, so accept any now. */
- po_imm_or_fail (INT_MIN, INT_MAX, TRUE);
+ /* There's a possibility of getting a 64-bit immediate here, so
+ we need special handling. */
+ if (parse_big_immediate (&str, i) == FAIL)
+ {
+ inst.error = _("immediate value is out of range");
+ goto failure;
+ }
}
break;
inst.operands[i].isreg = 1;
break;
+ case OP_RIWR_I32z: po_reg_or_goto (REG_TYPE_MMXWR, I32z); break;
+ I32z: po_imm_or_fail (0, 32, FALSE); break;
+
/* 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)
+ if (!rege
+ || (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;
}
break;
+ case OP_RIWC_RIWG:
+ {
+ struct reg_entry *rege = arm_reg_parse_multi (&str);
+ if (!rege
+ || (rege->type != REG_TYPE_MMXWC
+ && rege->type != REG_TYPE_MMXWCG))
+ {
+ inst.error = _("iWMMXt control register expected");
+ goto failure;
+ }
+ inst.operands[i].reg = rege->number;
+ inst.operands[i].isreg = 1;
+ }
+ break;
+
/* Misc */
case OP_CPSF: val = parse_cps_flags (&str); break;
case OP_ENDI: val = parse_endian_specifier (&str); break;
po_misc_or_fail (parse_address (&str, i));
break;
+ case OP_ADDRGLDR:
+ po_misc_or_fail_no_backtrack (
+ parse_address_group_reloc (&str, i, GROUP_LDR));
+ break;
+
+ case OP_ADDRGLDRS:
+ po_misc_or_fail_no_backtrack (
+ parse_address_group_reloc (&str, i, GROUP_LDRS));
+ break;
+
+ case OP_ADDRGLDC:
+ po_misc_or_fail_no_backtrack (
+ parse_address_group_reloc (&str, i, GROUP_LDC));
+ break;
+
case OP_SH:
po_misc_or_fail (parse_shifter_operand (&str, i));
break;
+ case OP_SHG:
+ po_misc_or_fail_no_backtrack (
+ parse_shifter_operand_group_reloc (&str, i));
+ break;
+
case OP_oSHll:
po_misc_or_fail (parse_shift (&str, i, SHIFT_LSL_IMMEDIATE));
break;
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. */
+ of BFD_ARM_CP_OFF_IMM, unless the initial relocation is a group one
+ (in which case it is preserved). */
static int
encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
if (reloc_override)
inst.reloc.type = reloc_override;
- else if (thumb_mode)
- inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
- else
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ else if ((inst.reloc.type < BFD_RELOC_ARM_ALU_PC_G0_NC
+ || inst.reloc.type > BFD_RELOC_ARM_LDC_SB_G2)
+ && inst.reloc.type != BFD_RELOC_ARM_LDR_PC_G0)
+ {
+ if (thumb_mode)
+ inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ }
+
return SUCCESS;
}
do_vfp_sp_const (void)
{
encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- inst.instruction |= (inst.operands[1].imm & 15) << 16;
- inst.instruction |= (inst.operands[1].imm >> 4);
+ inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
+ inst.instruction |= (inst.operands[1].imm & 0x0f);
}
static void
do_vfp_dp_const (void)
{
encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- inst.instruction |= (inst.operands[1].imm & 15) << 16;
- inst.instruction |= (inst.operands[1].imm >> 4);
+ inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
+ inst.instruction |= (inst.operands[1].imm & 0x0f);
}
static void
inst.instruction |= inst.operands[3].imm << 20;
}
+static void
+do_iwmmxt_wmerge (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].imm << 21;
+}
+
static void
do_iwmmxt_wmov (void)
{
do_iwmmxt_wldstd (void)
{
inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_cp_address (1, TRUE, FALSE, 0);
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2)
+ && inst.operands[1].immisreg)
+ {
+ inst.instruction &= ~0x1a000ff;
+ inst.instruction |= (0xf << 28);
+ if (inst.operands[1].preind)
+ inst.instruction |= PRE_INDEX;
+ if (!inst.operands[1].negative)
+ inst.instruction |= INDEX_UP;
+ if (inst.operands[1].writeback)
+ inst.instruction |= WRITE_BACK;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.reloc.exp.X_add_number << 4;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ else
+ encode_arm_cp_address (1, TRUE, FALSE, 0);
}
static void
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[0].reg << 16;
}
+
+static void
+do_iwmmxt_wrwrwr_or_imm5 (void)
+{
+ if (inst.operands[2].isreg)
+ do_rd_rn_rm ();
+ else {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2),
+ _("immediate operand requires iWMMXt2"));
+ do_rd_rn ();
+ if (inst.operands[2].imm == 0)
+ {
+ switch ((inst.instruction >> 20) & 0xf)
+ {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* w...h wrd, wrn, #0 -> wrorh wrd, wrn, #16. */
+ inst.operands[2].imm = 16;
+ inst.instruction = (inst.instruction & 0xff0fffff) | (0x7 << 20);
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ /* w...w wrd, wrn, #0 -> wrorw wrd, wrn, #32. */
+ inst.operands[2].imm = 32;
+ inst.instruction = (inst.instruction & 0xff0fffff) | (0xb << 20);
+ break;
+ case 12:
+ case 13:
+ case 14:
+ case 15:
+ {
+ /* w...d wrd, wrn, #0 -> wor wrd, wrn, wrn. */
+ unsigned long wrn;
+ wrn = (inst.instruction >> 16) & 0xf;
+ inst.instruction &= 0xff0fff0f;
+ inst.instruction |= wrn;
+ /* Bail out here; the instruction is now assembled. */
+ return;
+ }
+ }
+ }
+ /* Map 32 -> 0, etc. */
+ inst.operands[2].imm &= 0x1f;
+ inst.instruction |= (0xf << 28) | ((inst.operands[2].imm & 0x10) << 4) | (inst.operands[2].imm & 0xf);
+ }
+}
\f
/* Cirrus Maverick instructions. Simple 2-, 3-, and 4-register
operations first, then control, shift, and load/store. */
narrow = (current_it_mask != 0);
if (!inst.operands[2].isreg)
{
+ int add;
+
+ add = (inst.instruction == T_MNEM_add
+ || inst.instruction == T_MNEM_adds);
opcode = 0;
if (inst.size_req != 4)
{
- int add;
-
- add = (inst.instruction == T_MNEM_add
- || inst.instruction == T_MNEM_adds);
/* Attempt to use a narrow opcode, with relaxation if
appropriate. */
if (Rd == REG_SP && Rs == REG_SP && !flags)
if (inst.size_req == 4
|| (inst.size_req != 2 && !opcode))
{
- /* ??? Convert large immediates to addw/subw. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ if (Rs == REG_PC)
+ {
+ /* Always use addw/subw. */
+ inst.instruction = add ? 0xf20f0000 : 0xf2af0000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff)
+ | 0x10000000;
+ if (flags)
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_T32_ADD_IMM;
+ }
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].reg << 16;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
}
else
}
static void
-do_t_czb (void)
+do_t_cbz (void)
{
constraint (current_it_mask, BAD_NOT_IT);
constraint (inst.operands[0].reg > 7, BAD_HIREG);
static int
neon_cmode_for_logic_imm (unsigned immediate, unsigned *immbits, int size)
{
- /* Handle .I8 and .I64 as pseudo-instructions. */
- switch (size)
+ /* Handle .I8 pseudo-instructions. */
+ if (size == 8)
{
- case 8:
/* Unfortunately, this will make everything apart from zero out-of-range.
FIXME is this the intended semantics? There doesn't seem much point in
accepting .I8 if so. */
immediate |= immediate << 8;
size = 16;
- break;
- case 64:
- /* Similarly, anything other than zero will be replicated in bits [63:32],
- which probably isn't want we want if we specified .I64. */
- if (immediate != 0)
- goto bad_immediate;
- size = 32;
- break;
- default: ;
+ }
+
+ if (size >= 32)
+ {
+ if (immediate == (immediate & 0x000000ff))
+ {
+ *immbits = immediate;
+ return 0x1;
+ }
+ else if (immediate == (immediate & 0x0000ff00))
+ {
+ *immbits = immediate >> 8;
+ return 0x3;
+ }
+ else if (immediate == (immediate & 0x00ff0000))
+ {
+ *immbits = immediate >> 16;
+ return 0x5;
+ }
+ else if (immediate == (immediate & 0xff000000))
+ {
+ *immbits = immediate >> 24;
+ return 0x7;
+ }
+ if ((immediate & 0xffff) != (immediate >> 16))
+ goto bad_immediate;
+ immediate &= 0xffff;
}
if (immediate == (immediate & 0x000000ff))
{
*immbits = immediate;
- return (size == 16) ? 0x9 : 0x1;
+ return 0x9;
}
else if (immediate == (immediate & 0x0000ff00))
{
*immbits = immediate >> 8;
- return (size == 16) ? 0xb : 0x3;
- }
- else if (immediate == (immediate & 0x00ff0000))
- {
- *immbits = immediate >> 16;
- return 0x5;
- }
- else if (immediate == (immediate & 0xff000000))
- {
- *immbits = immediate >> 24;
- return 0x7;
+ return 0xb;
}
bad_immediate:
the instruction. *OP is passed as the initial value of the op field, and
may be set to a different value depending on the constant (i.e.
"MOV I64, 0bAAAAAAAABBBB..." which uses OP = 1 despite being MOV not
- MVN). */
+ MVN). If the immediate looks like a repeated parttern then also
+ try smaller element sizes. */
static int
neon_cmode_for_move_imm (unsigned immlo, unsigned immhi, unsigned *immbits,
*immbits = neon_qfloat_bits (immlo);
return 0xf;
}
- else if (size == 64 && neon_bits_same_in_bytes (immhi)
- && neon_bits_same_in_bytes (immlo))
- {
- /* Check this one first so we don't have to bother with immhi in later
- tests. */
- if (*op == 1)
- return FAIL;
- *immbits = (neon_squash_bits (immhi) << 4) | neon_squash_bits (immlo);
- *op = 1;
- return 0xe;
- }
- else if (immhi != 0)
- return FAIL;
- else if (immlo == (immlo & 0x000000ff))
- {
- /* 64-bit case was already handled. Don't allow MVN with 8-bit
- immediate. */
- if ((size != 8 && size != 16 && size != 32)
- || (size == 8 && *op == 1))
- return FAIL;
- *immbits = immlo;
- return (size == 8) ? 0xe : (size == 16) ? 0x8 : 0x0;
- }
- else if (immlo == (immlo & 0x0000ff00))
- {
- if (size != 16 && size != 32)
- return FAIL;
- *immbits = immlo >> 8;
- return (size == 16) ? 0xa : 0x2;
- }
- else if (immlo == (immlo & 0x00ff0000))
+
+ if (size == 64)
{
- if (size != 32)
- return FAIL;
- *immbits = immlo >> 16;
- return 0x4;
+ if (neon_bits_same_in_bytes (immhi)
+ && neon_bits_same_in_bytes (immlo))
+ {
+ if (*op == 1)
+ return FAIL;
+ *immbits = (neon_squash_bits (immhi) << 4)
+ | neon_squash_bits (immlo);
+ *op = 1;
+ return 0xe;
+ }
+
+ if (immhi != immlo)
+ return FAIL;
}
- else if (immlo == (immlo & 0xff000000))
+
+ if (size >= 32)
{
- if (size != 32)
- return FAIL;
- *immbits = immlo >> 24;
- return 0x6;
+ if (immlo == (immlo & 0x000000ff))
+ {
+ *immbits = immlo;
+ return 0x0;
+ }
+ else if (immlo == (immlo & 0x0000ff00))
+ {
+ *immbits = immlo >> 8;
+ return 0x2;
+ }
+ else if (immlo == (immlo & 0x00ff0000))
+ {
+ *immbits = immlo >> 16;
+ return 0x4;
+ }
+ else if (immlo == (immlo & 0xff000000))
+ {
+ *immbits = immlo >> 24;
+ return 0x6;
+ }
+ else if (immlo == ((immlo & 0x0000ff00) | 0x000000ff))
+ {
+ *immbits = (immlo >> 8) & 0xff;
+ return 0xc;
+ }
+ else if (immlo == ((immlo & 0x00ff0000) | 0x0000ffff))
+ {
+ *immbits = (immlo >> 16) & 0xff;
+ return 0xd;
+ }
+
+ if ((immlo & 0xffff) != (immlo >> 16))
+ return FAIL;
+ immlo &= 0xffff;
}
- else if (immlo == ((immlo & 0x0000ff00) | 0x000000ff))
+
+ if (size >= 16)
{
- if (size != 32)
- return FAIL;
- *immbits = (immlo >> 8) & 0xff;
- return 0xc;
+ if (immlo == (immlo & 0x000000ff))
+ {
+ *immbits = immlo;
+ return 0x8;
+ }
+ else if (immlo == (immlo & 0x0000ff00))
+ {
+ *immbits = immlo >> 8;
+ return 0xa;
+ }
+
+ if ((immlo & 0xff) != (immlo >> 8))
+ return FAIL;
+ immlo &= 0xff;
}
- else if (immlo == ((immlo & 0x00ff0000) | 0x0000ffff))
+
+ if (immlo == (immlo & 0x000000ff))
{
- if (size != 32)
- return FAIL;
- *immbits = (immlo >> 16) & 0xff;
- return 0xd;
+ /* Don't allow MVN with 8-bit immediate. */
+ if (*op == 1)
+ return FAIL;
+ *immbits = immlo;
+ return 0xe;
}
return FAIL;
inst.instruction = NEON_ENC_IMMED (inst.instruction);
+ immbits = inst.operands[1].imm;
+ if (et.size == 64)
+ {
+ /* .i64 is a pseudo-op, so the immediate must be a repeating
+ pattern. */
+ if (immbits != (inst.operands[1].regisimm ?
+ inst.operands[1].reg : 0))
+ {
+ /* Set immbits to an invalid constant. */
+ immbits = 0xdeadbeef;
+ }
+ }
+
switch (opcode)
{
case N_MNEM_vbic:
- cmode = neon_cmode_for_logic_imm (inst.operands[1].imm, &immbits,
- et.size);
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
break;
case N_MNEM_vorr:
- cmode = neon_cmode_for_logic_imm (inst.operands[1].imm, &immbits,
- et.size);
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
break;
case N_MNEM_vand:
/* Pseudo-instruction for VBIC. */
- immbits = inst.operands[1].imm;
neon_invert_size (&immbits, 0, et.size);
cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
break;
case N_MNEM_vorn:
/* Pseudo-instruction for VORR. */
- immbits = inst.operands[1].imm;
neon_invert_size (&immbits, 0, et.size);
cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
break;
neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
}
-static void
-do_neon_dyadic_if_i (void)
-{
- neon_dyadic_misc (NT_unsigned, N_IF_32, 0);
-}
-
static void
do_neon_dyadic_if_i_d (void)
{
- neon_dyadic_misc (NT_unsigned, N_IF_32, 0);
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
}
enum vfp_or_neon_is_neon_bits
neon_mul_mac (et, neon_quad (rs));
}
else
- do_neon_dyadic_if_i ();
+ {
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
+ }
}
static void
{
struct neon_type_el et = neon_check_type (3, NS_QDD,
N_EQK | N_DBL, N_EQK, N_I16 | N_I32 | N_I64 | N_KEY);
+ /* Operand sign is unimportant, and the U bit is part of the opcode,
+ so force the operand type to integer. */
+ et.type = NT_integer;
neon_mixed_length (et, et.size / 2);
}
symbolS *sym;
int offset;
-#ifdef OBJ_ELF
/* The size of the instruction is unknown, so tie the debug info to the
start of the instruction. */
dwarf2_emit_insn (0);
-#endif
switch (inst.reloc.exp.X_op)
{
inst.size, & inst.reloc.exp, inst.reloc.pc_rel,
inst.reloc.type);
-#ifdef OBJ_ELF
dwarf2_emit_insn (inst.size);
-#endif
}
/* Tag values used in struct asm_opcode's tag field. */
const struct asm_opcode *opcode;
const struct asm_cond *cond;
char save[2];
+ bfd_boolean neon_supported;
+
+ neon_supported = ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1);
/* Scan up to the end of the mnemonic, which must end in white space,
- '.' (in unified mode only), or end of string. */
+ '.' (in unified mode, or for Neon instructions), or end of string. */
for (base = end = *str; *end != '\0'; end++)
- if (*end == ' ' || (unified_syntax && *end == '.'))
+ if (*end == ' ' || ((unified_syntax || neon_supported) && *end == '.'))
break;
if (end == base)
{
int offset = 2;
- if (end[1] == 'w')
+ /* The .w and .n suffixes are only valid if the unified syntax is in
+ use. */
+ if (unified_syntax && end[1] == 'w')
inst.size_req = 4;
- else if (end[1] == 'n')
+ else if (unified_syntax && end[1] == 'n')
inst.size_req = 2;
else
offset = 0;
if (end[offset] == '.')
{
- /* See if we have a Neon type suffix. */
+ /* See if we have a Neon type suffix (possible in either unified or
+ non-unified ARM syntax mode). */
if (parse_neon_type (&inst.vectype, str) == FAIL)
return 0;
}
ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
arm_ext_v6t2);
}
- else
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1))
{
/* Check that this instruction is supported for this CPU. */
if (!opcode->avariant ||
ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
*opcode->avariant);
}
+ else
+ {
+ as_bad (_("attempt to use an ARM instruction on a Thumb-only processor "
+ "-- `%s'"), str);
+ return;
+ }
output_inst (str);
}
label_is_thumb_function_name = FALSE;
}
-#ifdef OBJ_ELF
dwarf2_emit_label (sym);
-#endif
}
int
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(add, 0800000, add, 3, (RR, oRR, SHG), arit, t_add_sub),
+ tC3(adds, 0900000, adds, 3, (RR, oRR, SHG), 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),
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),
+ tCE(ldr, 4100000, ldr, 2, (RR, ADDRGLDR),ldst, t_ldst),
+ tC3(ldrb, 4500000, ldrb, 2, (RR, ADDRGLDR),ldst, t_ldst),
+ tCE(str, 4000000, str, 2, (RR, ADDRGLDR),ldst, t_ldst),
+ tC3(strb, 4400000, strb, 2, (RR, ADDRGLDR),ldst, t_ldst),
tCE(stm, 8800000, stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
tC3(stmia, 8800000, stmia, 2, (RRw, REGLST), ldmstm, t_ldmstm),
/* 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(ldc, c100000, ec100000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TC3(ldcl, c500000, ec500000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TCE(stc, c000000, ec000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TC3(stcl, c400000, ec400000, 3, (RCP, RCN, ADDRGLDC), 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),
#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),
+ tC3(ldrh, 01000b0, ldrh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tC3(strh, 00000b0, strh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tC3(ldrsh, 01000f0, ldrsh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tC3(ldrsb, 01000d0, ldrsb, 2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tCM(ld,sh, 01000f0, ldrsh, 2, (RR, ADDRGLDRS), ldstv4, t_ldst),
+ tCM(ld,sb, 01000d0, ldrsb, 2, (RR, ADDRGLDRS), ldstv4, t_ldst),
#undef ARM_VARIANT
#define ARM_VARIANT &arm_ext_v4t_5
#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(ldc2, c100000, fc100000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF(ldc2l, c500000, fc500000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF(stc2, c000000, fc000000, 3, (RCP, RCN, ADDRGLDC), lstc, lstc),
+ TUF(stc2l, c400000, fc400000, 3, (RCP, RCN, ADDRGLDC), 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_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),
+ TC3(ldrd, 00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
+ TC3(strd, 00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), 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),
TCE(mls, 0600090, fb000010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
TCE(movw, 3000000, f2400000, 2, (RRnpc, HALF), mov16, t_mov16),
TCE(movt, 3400000, f2c00000, 2, (RRnpc, HALF), mov16, t_mov16),
- TCE(rbit, 3ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit),
+ TCE(rbit, 6ff0f30, 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. */
+ UT(cbnz, b900, 2, (RR, EXP), t_cbz),
+ UT(cbz, b100, 2, (RR, EXP), t_cbz),
+ /* ARM does not really have an IT instruction, so always allow it. */
+#undef ARM_VARIANT
+#define ARM_VARIANT &arm_ext_v1
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),
cCE(wfc, e400110, 1, (RR), rd),
cCE(rfc, e500110, 1, (RR), rd),
- cCL(ldfs, c100100, 2, (RF, ADDR), rd_cpaddr),
- cCL(ldfd, c108100, 2, (RF, ADDR), rd_cpaddr),
- cCL(ldfe, c500100, 2, (RF, ADDR), rd_cpaddr),
- cCL(ldfp, c508100, 2, (RF, ADDR), rd_cpaddr),
+ cCL(ldfs, c100100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL(ldfd, c108100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL(ldfe, c500100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL(ldfp, c508100, 2, (RF, ADDRGLDC), rd_cpaddr),
- cCL(stfs, c000100, 2, (RF, ADDR), rd_cpaddr),
- cCL(stfd, c008100, 2, (RF, ADDR), rd_cpaddr),
- cCL(stfe, c400100, 2, (RF, ADDR), rd_cpaddr),
- cCL(stfp, c408100, 2, (RF, ADDR), rd_cpaddr),
+ cCL(stfs, c000100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL(stfd, c008100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL(stfe, c400100, 2, (RF, ADDRGLDC), rd_cpaddr),
+ cCL(stfp, c408100, 2, (RF, ADDRGLDC), rd_cpaddr),
cCL(mvfs, e008100, 2, (RF, RF_IF), rd_rm),
cCL(mvfsp, e008120, 2, (RF, RF_IF), rd_rm),
cCE(fmxr, ee00a10, 2, (RVC, RR), rn_rd),
/* Memory operations. */
- cCE(flds, d100a00, 2, (RVS, ADDR), vfp_sp_ldst),
- cCE(fsts, d000a00, 2, (RVS, ADDR), vfp_sp_ldst),
+ cCE(flds, d100a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst),
+ cCE(fsts, d000a00, 2, (RVS, ADDRGLDC), vfp_sp_ldst),
cCE(fldmias, c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia),
cCE(fldmfds, c900a00, 2, (RRw, VRSLST), vfp_sp_ldstmia),
cCE(fldmdbs, d300a00, 2, (RRw, VRSLST), vfp_sp_ldstmdb),
cCE(ftouizd, ebc0bc0, 2, (RVS, RVD), vfp_sp_dp_cvt),
/* Memory operations. */
- cCE(fldd, d100b00, 2, (RVD, ADDR), vfp_dp_ldst),
- cCE(fstd, d000b00, 2, (RVD, ADDR), vfp_dp_ldst),
+ cCE(fldd, d100b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst),
+ cCE(fstd, d000b00, 2, (RVD, ADDRGLDC), vfp_dp_ldst),
cCE(fldmiad, c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia),
cCE(fldmfdd, c900b00, 2, (RRw, VRDLST), vfp_dp_ldstmia),
cCE(fldmdbd, d300b00, 2, (RRw, VRDLST), vfp_dp_ldstmdb),
NCE(vstm, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm),
NCE(vstmia, c800b00, 2, (RRw, VRSDLST), neon_ldm_stm),
NCE(vstmdb, d000b00, 2, (RRw, VRSDLST), neon_ldm_stm),
- NCE(vldr, d100b00, 2, (RVSD, ADDR), neon_ldr_str),
- NCE(vstr, d000b00, 2, (RVSD, ADDR), neon_ldr_str),
+ NCE(vldr, d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
+ NCE(vstr, d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
nCEF(vcvt, vcvt, 3, (RNSDQ, RNSDQ, oI32b), neon_cvt),
nUF(vcltq, vclt, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp_inv),
nUF(vcle, vcle, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_cmp_inv),
nUF(vcleq, vcle, 3, (RNQ, oRNQ, RNDQ_I0), neon_cmp_inv),
- /* Comparison. Type I8 I16 I32 F32. Non-immediate -> neon_dyadic_if_i. */
+ /* Comparison. Type I8 I16 I32 F32. */
nUF(vceq, vceq, 3, (RNDQ, oRNDQ, RNDQ_I0), neon_ceq),
nUF(vceqq, vceq, 3, (RNQ, oRNQ, RNDQ_I0), neon_ceq),
/* As above, D registers only. */
nUF(vpmax, vpmax, 3, (RND, oRND, RND), neon_dyadic_if_su_d),
nUF(vpmin, vpmin, 3, (RND, oRND, RND), neon_dyadic_if_su_d),
/* Int and float variants, signedness unimportant. */
- /* If not scalar, fall back to neon_dyadic_if_i. */
nUF(vmlaq, vmla, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mac_maybe_scalar),
nUF(vmlsq, vmls, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_mac_maybe_scalar),
nUF(vpadd, vpadd, 3, (RND, oRND, RND), neon_dyadic_if_i_d),
cCE(tinsrb, e600010, 3, (RIWR, RR, I7), iwmmxt_tinsr),
cCE(tinsrh, e600050, 3, (RIWR, RR, I7), iwmmxt_tinsr),
cCE(tinsrw, e600090, 3, (RIWR, RR, I7), iwmmxt_tinsr),
- cCE(tmcr, e000110, 2, (RIWC, RR), rn_rd),
+ cCE(tmcr, e000110, 2, (RIWC_RIWG, RR), rn_rd),
cCE(tmcrr, c400000, 3, (RIWR, RR, RR), rm_rd_rn),
cCE(tmia, e200010, 3, (RIWR, RR, RR), iwmmxt_tmia),
cCE(tmiaph, e280010, 3, (RIWR, RR, RR), iwmmxt_tmia),
cCE(tmovmskb, e100030, 2, (RR, RIWR), rd_rn),
cCE(tmovmskh, e500030, 2, (RR, RIWR), rd_rn),
cCE(tmovmskw, e900030, 2, (RR, RIWR), rd_rn),
- cCE(tmrc, e100110, 2, (RR, RIWC), rd_rn),
+ cCE(tmrc, e100110, 2, (RR, RIWC_RIWG), rd_rn),
cCE(tmrrc, c500000, 3, (RR, RR, RIWR), rd_rn_rm),
cCE(torcb, e13f150, 1, (RR), iwmmxt_tandorc),
cCE(torch, e53f150, 1, (RR), iwmmxt_tandorc),
cCE(wpackwus, e900080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wpackdss, ef00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wpackdus, ed00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE(wrorh, e700040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wrorh, e700040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wrorhg, e700148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wrorw, eb00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wrorw, eb00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wrorwg, eb00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wrord, ef00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wrord, ef00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wrordg, ef00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
cCE(wsadb, e000120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wsadbz, e100120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wsadh, e400120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wsadhz, e500120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wshufh, e0001e0, 3, (RIWR, RIWR, I255), iwmmxt_wshufh),
- cCE(wsllh, e500040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsllh, e500040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsllhg, e500148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsllw, e900040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsllw, e900040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsllwg, e900148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wslld, ed00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wslld, ed00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wslldg, ed00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsrah, e400040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsrah, e400040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsrahg, e400148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsraw, e800040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsraw, e800040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsrawg, e800148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsrad, ec00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsrad, ec00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsradg, ec00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsrlh, e600040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsrlh, e600040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsrlhg, e600148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsrlw, ea00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsrlw, ea00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsrlwg, ea00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
- cCE(wsrld, ee00040, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsrld, ee00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE(wsrldg, ee00148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
cCE(wstrb, c000000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
cCE(wstrh, c400000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
cCE(wxor, e100000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE(wzero, e300000, 1, (RIWR), iwmmxt_wzero),
+#undef ARM_VARIANT
+#define ARM_VARIANT &arm_cext_iwmmxt2 /* Intel Wireless MMX technology, version 2. */
+ cCE(torvscb, e13f190, 1, (RR), iwmmxt_tandorc),
+ cCE(torvsch, e53f190, 1, (RR), iwmmxt_tandorc),
+ cCE(torvscw, e93f190, 1, (RR), iwmmxt_tandorc),
+ cCE(wabsb, e2001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE(wabsh, e6001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE(wabsw, ea001c0, 2, (RIWR, RIWR), rd_rn),
+ cCE(wabsdiffb, e1001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wabsdiffh, e5001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wabsdiffw, e9001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(waddbhusl, e2001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(waddbhusm, e6001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(waddhc, e600180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(waddwc, ea00180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(waddsubhx, ea001a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wavg4, e400000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wavg4r, e500000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmaddsn, ee00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmaddsx, eb00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmaddun, ec00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmaddux, e900100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmerge, e000080, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_wmerge),
+ cCE(wmiabb, e0000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiabt, e1000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiatb, e2000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiatt, e3000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiabbn, e4000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiabtn, e5000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiatbn, e6000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiattn, e7000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawbb, e800120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawbt, e900120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawtb, ea00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawtt, eb00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawbbn, ec00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawbtn, ed00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawtbn, ee00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmiawttn, ef00120, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulsmr, ef00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulumr, ed00100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulwumr, ec000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulwsmr, ee000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulwum, ed000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulwsm, ef000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wmulwl, eb000c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiabb, e8000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiabt, e9000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiatb, ea000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiatt, eb000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiabbn, ec000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiabtn, ed000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiatbn, ee000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmiattn, ef000a0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmulm, e100080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmulmr, e300080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmulwm, ec000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wqmulwmr, ee000e0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE(wsubaddhx, ed001c0, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+
#undef ARM_VARIANT
#define ARM_VARIANT &arm_cext_maverick /* Cirrus Maverick instructions. */
- cCE(cfldrs, c100400, 2, (RMF, ADDR), rd_cpaddr),
- cCE(cfldrd, c500400, 2, (RMD, ADDR), rd_cpaddr),
- cCE(cfldr32, c100500, 2, (RMFX, ADDR), rd_cpaddr),
- cCE(cfldr64, c500500, 2, (RMDX, ADDR), rd_cpaddr),
- cCE(cfstrs, c000400, 2, (RMF, ADDR), rd_cpaddr),
- cCE(cfstrd, c400400, 2, (RMD, ADDR), rd_cpaddr),
- cCE(cfstr32, c000500, 2, (RMFX, ADDR), rd_cpaddr),
- cCE(cfstr64, c400500, 2, (RMDX, ADDR), rd_cpaddr),
+ cCE(cfldrs, c100400, 2, (RMF, ADDRGLDC), rd_cpaddr),
+ cCE(cfldrd, c500400, 2, (RMD, ADDRGLDC), rd_cpaddr),
+ cCE(cfldr32, c100500, 2, (RMFX, ADDRGLDC), rd_cpaddr),
+ cCE(cfldr64, c500500, 2, (RMDX, ADDRGLDC), rd_cpaddr),
+ cCE(cfstrs, c000400, 2, (RMF, ADDRGLDC), rd_cpaddr),
+ cCE(cfstrd, c400400, 2, (RMD, ADDRGLDC), rd_cpaddr),
+ cCE(cfstr32, c000500, 2, (RMFX, ADDRGLDC), rd_cpaddr),
+ cCE(cfstr64, c400500, 2, (RMDX, ADDRGLDC), rd_cpaddr),
cCE(cfmvsr, e000450, 2, (RMF, RR), rn_rd),
cCE(cfmvrs, e100450, 2, (RR, RMF), rd_rn),
cCE(cfmvdlr, e000410, 2, (RMD, RR), rn_rd),
insn = THUMB_OP32 (opcode);
insn |= (old_op & 0xf0) << 4;
put_thumb32_insn (buf, insn);
- reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ if (opcode == T_MNEM_add_pc)
+ reloc_type = BFD_RELOC_ARM_T32_IMM12;
+ else
+ reloc_type = BFD_RELOC_ARM_T32_ADD_IMM;
}
else
reloc_type = BFD_RELOC_ARM_THUMB_ADD;
insn |= (old_op & 0xf0) << 4;
insn |= (old_op & 0xf) << 16;
put_thumb32_insn (buf, insn);
- reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ if (insn & (1 << 20))
+ reloc_type = BFD_RELOC_ARM_T32_ADD_IMM;
+ else
+ reloc_type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
else
reloc_type = BFD_RELOC_ARM_THUMB_ADD;
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;
+#if (defined (OBJ_AOUT) || defined (OBJ_MAYBE_AOUT))
+ if (OUTPUT_FLAVOR == bfd_target_aout_flavour)
+ {
+ /* For a.out, force the section size to be aligned. If we don't do
+ this, BFD will align it for us, but it will not write out the
+ final bytes of the section. This may be a bug in BFD, but it is
+ easier to fix it here since that is how the other a.out targets
+ work. */
+ int align;
+
+ align = bfd_get_section_alignment (stdoutput, segment);
+ size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+ }
#endif
+
+ return size;
}
/* This is called from HANDLE_ALIGN in write.c. Fill in the contents
return 0;
}
+
+/* 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 /* OBJ_ELF */
+
/* Convert REGNAME to a DWARF-2 register number. */
int
return reg;
}
-/* Initialize the DWARF-2 unwind information for this procedure. */
-
+#ifdef TE_PE
void
-tc_arm_frame_initial_instructions (void)
+tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size)
{
- cfi_add_CFA_def_cfa (REG_SP, 0);
-}
-#endif /* OBJ_ELF */
+ expressionS expr;
+ expr.X_op = O_secrel;
+ expr.X_add_symbol = symbol;
+ expr.X_add_number = 0;
+ emit_expr (&expr, size);
+}
+#endif
/* MD interface: Symbol and relocation handling. */
/* Like negate_data_op, but for Thumb-2. */
static unsigned int
-thumb32_negate_data_op (offsetT *instruction, offsetT value)
+thumb32_negate_data_op (offsetT *instruction, unsigned int value)
{
int op, new_inst;
int rd;
- offsetT negated, inverted;
+ unsigned int negated, inverted;
negated = encode_thumb32_immediate (-value);
inverted = encode_thumb32_immediate (~value);
return FAIL;
}
- if (value == FAIL)
+ if (value == (unsigned int)FAIL)
return FAIL;
*instruction &= T2_OPCODE_MASK;
assert (fixP->fx_r_type <= BFD_RELOC_UNUSED);
/* Note whether this will delete the relocation. */
+
if (fixP->fx_addsy == 0 && !fixP->fx_pcrel)
fixP->fx_done = 1;
break;
case BFD_RELOC_ARM_T32_IMMEDIATE:
+ case BFD_RELOC_ARM_T32_ADD_IMM:
case BFD_RELOC_ARM_T32_IMM12:
case BFD_RELOC_ARM_T32_ADD_PC12:
/* We claim that this fixup has been processed here,
newval <<= 16;
newval |= md_chars_to_number (buf+2, THUMB_SIZE);
- /* FUTURE: Implement analogue of negate_data_op for T32. */
- if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE)
+ newimm = FAIL;
+ if (fixP->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
+ || fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM)
{
newimm = encode_thumb32_immediate (value);
if (newimm == (unsigned int) FAIL)
newimm = thumb32_negate_data_op (&newval, value);
}
- else
+ if (fixP->fx_r_type != BFD_RELOC_ARM_T32_IMMEDIATE
+ && newimm == (unsigned int) FAIL)
{
+ /* Turn add/sum into addw/subw. */
+ if (fixP->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM)
+ newval = (newval & 0xfeffffff) | 0x02000000;
+
/* 12 bit immediate for addw/subw. */
if (value < 0)
{
}
break;
- case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CZB */
- /* CZB can only branch forward. */
+ case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CBZ */
+ /* CBZ can only branch forward. */
if (value & ~0x7e)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("branch out of range"));
case BFD_RELOC_ARM_ROSEGREL32:
case BFD_RELOC_ARM_SBREL32:
case BFD_RELOC_32_PCREL:
+#ifdef TE_PE
+ case BFD_RELOC_32_SECREL:
+#endif
if (fixP->fx_done || !seg->use_rela_p)
#ifdef TE_WINCE
/* For WinCE we only do this for pcrel fixups. */
newval = get_thumb32_insn (buf);
newval &= 0xff7fff00;
newval |= (value >> 2) | (sign ? INDEX_UP : 0);
- if (value == 0)
- newval &= ~WRITE_BACK;
if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
|| fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2)
md_number_to_chars (buf, newval, INSN_SIZE);
}
return;
+ case BFD_RELOC_ARM_ALU_PC_G0_NC:
+ case BFD_RELOC_ARM_ALU_PC_G0:
+ case BFD_RELOC_ARM_ALU_PC_G1_NC:
+ case BFD_RELOC_ARM_ALU_PC_G1:
+ case BFD_RELOC_ARM_ALU_PC_G2:
+ case BFD_RELOC_ARM_ALU_SB_G0_NC:
+ case BFD_RELOC_ARM_ALU_SB_G0:
+ case BFD_RELOC_ARM_ALU_SB_G1_NC:
+ case BFD_RELOC_ARM_ALU_SB_G1:
+ case BFD_RELOC_ARM_ALU_SB_G2:
+ assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma encoded_addend;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ expressed as an 8-bit constant plus a rotation. */
+ encoded_addend = encode_arm_immediate (addend_abs);
+ if (encoded_addend == (unsigned int) FAIL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("the offset 0x%08lX is not representable"),
+ addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is positive, use an ADD instruction.
+ Otherwise use a SUB. Take care not to destroy the S bit. */
+ insn &= 0xff1fffff;
+ if (value < 0)
+ insn |= 1 << 22;
+ else
+ insn |= 1 << 23;
+
+ /* Place the encoded addend into the first 12 bits of the
+ instruction. */
+ insn &= 0xfffff000;
+ insn |= encoded_addend;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_LDR_PC_G0:
+ case BFD_RELOC_ARM_LDR_PC_G1:
+ case BFD_RELOC_ARM_LDR_PC_G2:
+ case BFD_RELOC_ARM_LDR_SB_G0:
+ case BFD_RELOC_ARM_LDR_SB_G1:
+ case BFD_RELOC_ARM_LDR_SB_G2:
+ assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ encoded in 12 bits. */
+ if (addend_abs >= 0x1000)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (only 12 bits available for the magnitude)"),
+ addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the absolute value of the addend into the first 12 bits
+ of the instruction. */
+ insn &= 0xfffff000;
+ insn |= addend_abs;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_LDRS_PC_G0:
+ case BFD_RELOC_ARM_LDRS_PC_G1:
+ case BFD_RELOC_ARM_LDRS_PC_G2:
+ case BFD_RELOC_ARM_LDRS_SB_G0:
+ case BFD_RELOC_ARM_LDRS_SB_G1:
+ case BFD_RELOC_ARM_LDRS_SB_G2:
+ assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ encoded in 8 bits. */
+ if (addend_abs >= 0x100)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (only 8 bits available for the magnitude)"),
+ addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the first four bits of the absolute value of the addend
+ into the first 4 bits of the instruction, and the remaining
+ four into bits 8 .. 11. */
+ insn &= 0xfffff0f0;
+ insn |= (addend_abs & 0xf) | ((addend_abs & 0xf0) << 4);
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
+ case BFD_RELOC_ARM_LDC_PC_G0:
+ case BFD_RELOC_ARM_LDC_PC_G1:
+ case BFD_RELOC_ARM_LDC_PC_G2:
+ case BFD_RELOC_ARM_LDC_SB_G0:
+ case BFD_RELOC_ARM_LDC_SB_G1:
+ case BFD_RELOC_ARM_LDC_SB_G2:
+ assert (!fixP->fx_done);
+ if (!seg->use_rela_p)
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend is a multiple of
+ four and, when divided by four, fits in 8 bits. */
+ if (addend_abs & 0x3)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (must be word-aligned)"),
+ addend_abs);
+
+ if ((addend_abs >> 2) > 0xff)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad offset 0x%08lX (must be an 8-bit number of words)"),
+ addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the addend (divided by four) into the first eight
+ bits of the instruction. */
+ insn &= 0xfffffff0;
+ insn |= addend_abs >> 2;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
+ break;
+
case BFD_RELOC_UNUSED:
default:
as_bad_where (fixP->fx_file, fixP->fx_line,
case BFD_RELOC_THUMB_PCREL_BLX:
case BFD_RELOC_VTABLE_ENTRY:
case BFD_RELOC_VTABLE_INHERIT:
+#ifdef TE_PE
+ case BFD_RELOC_32_SECREL:
+#endif
code = fixp->fx_r_type;
break;
case BFD_RELOC_ARM_TLS_LDO32:
case BFD_RELOC_ARM_PCREL_CALL:
case BFD_RELOC_ARM_PCREL_JUMP:
+ case BFD_RELOC_ARM_ALU_PC_G0_NC:
+ case BFD_RELOC_ARM_ALU_PC_G0:
+ case BFD_RELOC_ARM_ALU_PC_G1_NC:
+ case BFD_RELOC_ARM_ALU_PC_G1:
+ case BFD_RELOC_ARM_ALU_PC_G2:
+ case BFD_RELOC_ARM_LDR_PC_G0:
+ case BFD_RELOC_ARM_LDR_PC_G1:
+ case BFD_RELOC_ARM_LDR_PC_G2:
+ case BFD_RELOC_ARM_LDRS_PC_G0:
+ case BFD_RELOC_ARM_LDRS_PC_G1:
+ case BFD_RELOC_ARM_LDRS_PC_G2:
+ case BFD_RELOC_ARM_LDC_PC_G0:
+ case BFD_RELOC_ARM_LDC_PC_G1:
+ case BFD_RELOC_ARM_LDC_PC_G2:
+ case BFD_RELOC_ARM_ALU_SB_G0_NC:
+ case BFD_RELOC_ARM_ALU_SB_G0:
+ case BFD_RELOC_ARM_ALU_SB_G1_NC:
+ case BFD_RELOC_ARM_ALU_SB_G1:
+ case BFD_RELOC_ARM_ALU_SB_G2:
+ case BFD_RELOC_ARM_LDR_SB_G0:
+ case BFD_RELOC_ARM_LDR_SB_G1:
+ case BFD_RELOC_ARM_LDR_SB_G2:
+ case BFD_RELOC_ARM_LDRS_SB_G0:
+ case BFD_RELOC_ARM_LDRS_SB_G1:
+ case BFD_RELOC_ARM_LDRS_SB_G2:
+ case BFD_RELOC_ARM_LDC_SB_G0:
+ case BFD_RELOC_ARM_LDC_SB_G1:
+ case BFD_RELOC_ARM_LDC_SB_G2:
code = fixp->fx_r_type;
break;
break;
}
+#ifdef TE_PE
+ if (exp->X_op == O_secrel)
+ {
+ exp->X_op = O_symbol;
+ type = BFD_RELOC_32_SECREL;
+ }
+#endif
+
fix_new_exp (frag, where, (int) size, exp, pcrel, type);
}
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
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12)
return 0;
- return generic_force_reloc (fixp);
-}
-
-#ifdef OBJ_COFF
-bfd_boolean
-arm_fix_adjustable (fixS * fixP)
-{
- /* 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. */
- if (fixP->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE)
+ /* Always leave these relocations for the linker. */
+ if ((fixp->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC
+ && fixp->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2)
+ || fixp->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0)
return 1;
- /* This is a hack for the gas/all/redef2.s test. This test causes symbols
- to be cloned, and without this test relocs would still be generated
- against the original, pre-cloned symbol. Such symbols would not appear
- in the symbol table however, and so a valid reloc could not be
- generated. So check to see if the fixup is against a symbol which has
- been removed from the symbol chain, and if it is, then allow it to be
- adjusted into a reloc against a section symbol. */
- if (fixP->fx_addsy != NULL
- && ! S_IS_LOCAL (fixP->fx_addsy)
- && symbol_next (fixP->fx_addsy) == NULL
- && symbol_next (fixP->fx_addsy) == symbol_previous (fixP->fx_addsy))
- return 1;
-
- return 0;
+ return generic_force_reloc (fixp);
}
-#endif
-#ifdef OBJ_ELF
+#if defined (OBJ_ELF) || defined (OBJ_COFF)
/* Relocations against function names must be left unadjusted,
so that the linker can use this information to generate interworking
stubs. The MIPS version of this function
|| fixP->fx_r_type == BFD_RELOC_ARM_TARGET2)
return 0;
+ /* Similarly for group relocations. */
+ if ((fixP->fx_r_type >= BFD_RELOC_ARM_ALU_PC_G0_NC
+ && fixP->fx_r_type <= BFD_RELOC_ARM_LDC_SB_G2)
+ || fixP->fx_r_type == BFD_RELOC_ARM_LDR_PC_G0)
+ return 0;
+
return 1;
}
+#endif /* defined (OBJ_ELF) || defined (OBJ_COFF) */
+
+#ifdef OBJ_ELF
const char *
elf32_arm_target_format (void)
if (THUMB_IS_FUNC (sym))
elf_sym->internal_elf_sym.st_info =
ELF_ST_INFO (bind, STT_ARM_TFUNC);
- else
+ else if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
elf_sym->internal_elf_sym.st_info =
ELF_ST_INFO (bind, STT_ARM_16BIT);
}
abort ();
}
+/* Auto-select Thumb mode if it's the only available instruction set for the
+ given architecture. */
+
+static void
+autoselect_thumb_from_cpu_variant (void)
+{
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1))
+ opcode_select (16);
+}
+
void
md_begin (void)
{
ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
+ autoselect_thumb_from_cpu_variant ();
+
arm_arch_used = thumb_arch_used = arm_arch_none;
#if defined OBJ_COFF || defined OBJ_ELF
#endif
/* Record the CPU type as well. */
- if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt))
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2))
+ mach = bfd_mach_arm_iWMMXt2;
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt))
mach = bfd_mach_arm_iWMMXt;
else if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_xscale))
mach = bfd_mach_arm_XScale;
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL},
/* ??? iwmmxt is not a processor. */
{"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL},
+ {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL},
{"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL},
/* Maverick */
{"ep9312", ARM_FEATURE(ARM_AEXT_V4T, ARM_CEXT_MAVERICK), FPU_ARCH_MAVERICK, "ARM920T"},
{"armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP},
{"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP},
{"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
+ {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP},
{NULL, ARM_ARCH_NONE, ARM_ARCH_NONE}
};
{"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK)},
{"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE)},
{"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT)},
+ {"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2)},
{NULL, ARM_ARCH_NONE}
};
ARM_MERGE_FEATURE_SETS (flags, arm_arch_used, thumb_arch_used);
ARM_MERGE_FEATURE_SETS (flags, flags, *mfpu_opt);
ARM_MERGE_FEATURE_SETS (flags, flags, selected_cpu);
-
+ /*Allow the user to override the reported architecture. */
+ if (object_arch)
+ {
+ ARM_CLEAR_FEATURE (flags, flags, arm_arch_any);
+ ARM_MERGE_FEATURE_SETS (flags, flags, *object_arch);
+ }
+
tmp = flags;
arch = 0;
for (p = cpu_arch_ver; p->val; p++)
}
+/* Parse a .object_arch directive. */
+
+static void
+s_arm_object_arch (int ignored ATTRIBUTE_UNUSED)
+{
+ const struct arm_arch_option_table *opt;
+ char saved_char;
+ char *name;
+
+ name = input_line_pointer;
+ while (*input_line_pointer && !ISSPACE(*input_line_pointer))
+ input_line_pointer++;
+ saved_char = *input_line_pointer;
+ *input_line_pointer = 0;
+
+ /* Skip the first "all" entry. */
+ for (opt = arm_archs + 1; opt->name != NULL; opt++)
+ if (streq (opt->name, name))
+ {
+ object_arch = &opt->value;
+ *input_line_pointer = saved_char;
+ demand_empty_rest_of_line ();
+ return;
+ }
+
+ as_bad (_("unknown architecture `%s'\n"), name);
+ *input_line_pointer = saved_char;
+ ignore_rest_of_line ();
+}
+
+
/* Parse a .fpu directive. */
static void