static const arm_feature_set arm_ext_v7a = ARM_FEATURE (ARM_EXT_V7A, 0);
static const arm_feature_set arm_ext_v7r = ARM_FEATURE (ARM_EXT_V7R, 0);
static const arm_feature_set arm_ext_v7m = ARM_FEATURE (ARM_EXT_V7M, 0);
+static const arm_feature_set arm_ext_v8 = ARM_FEATURE (ARM_EXT_V8, 0);
static const arm_feature_set arm_ext_m =
ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_OS | ARM_EXT_V7M, 0);
static const arm_feature_set arm_ext_mp = ARM_FEATURE (ARM_EXT_MP, 0);
static const arm_feature_set fpu_vfp_fp16 = ARM_FEATURE (0, FPU_VFP_EXT_FP16);
static const arm_feature_set fpu_neon_ext_fma = ARM_FEATURE (0, FPU_NEON_EXT_FMA);
static const arm_feature_set fpu_vfp_ext_fma = ARM_FEATURE (0, FPU_VFP_EXT_FMA);
+static const arm_feature_set fpu_vfp_ext_armv8 =
+ ARM_FEATURE (0, FPU_VFP_EXT_ARMV8);
+static const arm_feature_set fpu_neon_ext_armv8 =
+ ARM_FEATURE (0, FPU_NEON_EXT_ARMV8);
+static const arm_feature_set fpu_crypto_ext_armv8 =
+ ARM_FEATURE (0, FPU_CRYPTO_EXT_ARMV8);
static int mfloat_abi_opt = -1;
/* Record user cpu selection for object attributes. */
struct asm_barrier_opt
{
- const char * template_name;
- unsigned long value;
+ const char * template_name;
+ unsigned long value;
+ const arm_feature_set arch;
};
/* The bit that distinguishes CPSR and SPSR. */
#define T2_OPCODE_MASK 0xfe1fffff
#define T2_DATA_OP_SHIFT 21
+#define A_COND_MASK 0xf0000000
+#define A_PUSH_POP_OP_MASK 0x0fff0000
+
+/* Opcodes for pushing/poping registers to/from the stack. */
+#define A1_OPCODE_PUSH 0x092d0000
+#define A2_OPCODE_PUSH 0x052d0004
+#define A2_OPCODE_POP 0x049d0004
+
/* Codes to distinguish the arithmetic instructions. */
#define OPCODE_AND 0
#define OPCODE_EOR 1
return c->value;
}
+/* If the given feature available in the selected CPU, mark it as used.
+ Returns TRUE iff feature is available. */
+static bfd_boolean
+mark_feature_used (const arm_feature_set *feature)
+{
+ /* Ensure the option is valid on the current architecture. */
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
+ return FALSE;
+
+ /* Add the appropriate architecture feature for the barrier option used.
+ */
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, *feature);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *feature);
+
+ return TRUE;
+}
+
/* Parse an option for a barrier instruction. Returns the encoding for the
option, or FAIL. */
static int
if (!o)
return FAIL;
+ if (!mark_feature_used (&o->arch))
+ return FAIL;
+
*str = q;
return o->value;
}
unsigned const int *upat = pattern;
char *backtrack_pos = 0;
const char *backtrack_error = 0;
- int i, val, backtrack_index = 0;
+ int i, val = 0, backtrack_index = 0;
enum arm_reg_type rtype;
parse_operand_result result;
unsigned int op_parse_code;
static void
encode_arm_addr_mode_common (int i, bfd_boolean is_t)
{
- gas_assert (inst.operands[i].isreg);
+ /* PR 14260:
+ Generate an error if the operand is not a register. */
+ constraint (!inst.operands[i].isreg,
+ _("Instruction does not support =N addresses"));
+
inst.instruction |= inst.operands[i].reg << 16;
if (inst.operands[i].preind)
inst.instruction |= inst.operands[1].reg << 12;
}
+static bfd_boolean
+check_obsolete (const arm_feature_set *feature, const char *msg)
+{
+ if (ARM_CPU_IS_ANY (cpu_variant))
+ {
+ as_warn ("%s", msg);
+ return TRUE;
+ }
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
+ {
+ as_bad ("%s", msg);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
do_rd_rm_rn (void)
{
constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg,
_("Rn must not overlap other operands"));
- /* SWP{b} is deprecated for ARMv6* and ARMv7. */
- if (warn_on_deprecated
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
- as_warn (_("swp{b} use is deprecated for this architecture"));
-
+ /* SWP{b} is obsolete for ARMv8-A, and deprecated for ARMv6* and ARMv7.
+ */
+ if (!check_obsolete (&arm_ext_v8,
+ _("swp{b} use is obsoleted for ARMv8 and later"))
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6))
+ as_warn (_("swp{b} use is deprecated for ARMv6 and ARMv7"));
}
+
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg;
inst.instruction |= Rn << 16;
No special properties. */
+struct deprecated_coproc_regs_s
+{
+ unsigned cp;
+ int opc1;
+ unsigned crn;
+ unsigned crm;
+ int opc2;
+ arm_feature_set deprecated;
+ arm_feature_set obsoleted;
+ const char *dep_msg;
+ const char *obs_msg;
+};
+
+#define DEPR_ACCESS_V8 \
+ N_("This coprocessor register access is deprecated in ARMv8")
+
+/* Table of all deprecated coprocessor registers. */
+static struct deprecated_coproc_regs_s deprecated_coproc_regs[] =
+{
+ {15, 0, 7, 10, 5, /* CP15DMB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 10, 4, /* CP15DSB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 5, 4, /* CP15ISB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 1, 0, 0, /* TEEHBR. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 0, 0, 0, /* TEECR. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+};
+
+#undef DEPR_ACCESS_V8
+
+static const size_t deprecated_coproc_reg_count =
+ sizeof (deprecated_coproc_regs) / sizeof (deprecated_coproc_regs[0]);
+
static void
do_co_reg (void)
{
unsigned Rd;
+ size_t i;
Rd = inst.operands[2].reg;
if (thumb_mode)
constraint (Rd == REG_PC, BAD_PC);
}
+ for (i = 0; i < deprecated_coproc_reg_count; ++i)
+ {
+ const struct deprecated_coproc_regs_s *r =
+ deprecated_coproc_regs + i;
+
+ if (inst.operands[0].reg == r->cp
+ && inst.operands[1].imm == r->opc1
+ && inst.operands[3].reg == r->crn
+ && inst.operands[4].reg == r->crm
+ && inst.operands[5].imm == r->opc2)
+ {
+ if (!check_obsolete (&r->obsoleted, r->obs_msg)
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, r->deprecated))
+ as_warn ("%s", r->dep_msg);
+ }
+ }
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].imm << 21;
}
}
+/* If there is only one register in the register list,
+ then return its register number. Otherwise return -1. */
+static int
+only_one_reg_in_list (int range)
+{
+ int i = ffs (range) - 1;
+ return (i > 15 || range != (1 << i)) ? -1 : i;
+}
+
static void
-do_ldmstm (void)
+encode_ldmstm(int from_push_pop_mnem)
{
int base_reg = inst.operands[0].reg;
int range = inst.operands[1].imm;
+ int one_reg;
inst.instruction |= base_reg << 16;
inst.instruction |= range;
as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
}
}
+
+ /* If PUSH/POP has only one register, then use the A2 encoding. */
+ one_reg = only_one_reg_in_list (range);
+ if (from_push_pop_mnem && one_reg >= 0)
+ {
+ int is_push = (inst.instruction & A_PUSH_POP_OP_MASK) == A1_OPCODE_PUSH;
+
+ inst.instruction &= A_COND_MASK;
+ inst.instruction |= is_push ? A2_OPCODE_PUSH : A2_OPCODE_POP;
+ inst.instruction |= one_reg << 12;
+ }
+}
+
+static void
+do_ldmstm (void)
+{
+ encode_ldmstm (/*from_push_pop_mnem=*/FALSE);
}
/* ARMv5TE load-consecutive (argument parse)
inst.operands[0].isreg = 1;
inst.operands[0].writeback = 1;
inst.operands[0].reg = REG_SP;
- do_ldmstm ();
+ encode_ldmstm (/*from_push_pop_mnem=*/TRUE);
}
/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
static void
do_setend (void)
{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_warn (_("setend use is deprecated for ARMv8"));
+
if (inst.operands[0].imm)
inst.instruction |= 0x200;
}
X(_yield, bf10, f3af8001), \
X(_wfe, bf20, f3af8002), \
X(_wfi, bf30, f3af8003), \
- X(_sev, bf40, f3af8004),
+ X(_sev, bf40, f3af8004), \
+ X(_sevl, bf50, f3af8005)
/* 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
set_it_insn_type (IT_INSN);
now_it.mask = (inst.instruction & 0xf) | 0x10;
now_it.cc = cond;
+ now_it.warn_deprecated = FALSE;
/* If the condition is a negative condition, invert the mask. */
if ((cond & 0x1) == 0x0)
unsigned int mask = inst.instruction & 0x000f;
if ((mask & 0x7) == 0)
- /* no conversion needed */;
+ {
+ /* No conversion needed. */
+ now_it.block_length = 1;
+ }
else if ((mask & 0x3) == 0)
- mask ^= 0x8;
+ {
+ mask ^= 0x8;
+ now_it.block_length = 2;
+ }
else if ((mask & 0x1) == 0)
- mask ^= 0xC;
+ {
+ mask ^= 0xC;
+ now_it.block_length = 3;
+ }
else
- mask ^= 0xE;
+ {
+ mask ^= 0xE;
+ now_it.block_length = 4;
+ }
inst.instruction &= 0xfff0;
inst.instruction |= mask;
int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
- constraint (flags != 0, _("selected processor does not support "
- "requested special purpose register"));
+ {
+ /* PR gas/12698: The constraint is only applied for m_profile.
+ If the user has specified -march=all, we want to ignore it as
+ we are building for any CPU type, including non-m variants. */
+ bfd_boolean m_profile = selected_cpu.core != arm_arch_any.core;
+ constraint ((flags != 0) && m_profile, _("selected processor does "
+ "not support requested special purpose register"));
+ }
else
/* mrs only accepts APSR/CPSR/SPSR/CPSR_all/SPSR_all (for non-M profile
devices). */
{
int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
- constraint ((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
- && (bits & ~(PSR_s | PSR_f)) != 0)
- || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
- && bits != PSR_f),
- _("selected processor does not support requested special "
- "purpose register"));
+ /* PR gas/12698: The constraint is only applied for m_profile.
+ If the user has specified -march=all, we want to ignore it as
+ we are building for any CPU type, including non-m variants. */
+ bfd_boolean m_profile = selected_cpu.core != arm_arch_any.core;
+ constraint (((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && (bits & ~(PSR_s | PSR_f)) != 0)
+ || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && bits != PSR_f)) && m_profile,
+ _("selected processor does not support requested special "
+ "purpose register"));
}
else
constraint ((flags & 0xff) != 0, _("selected processor does not support "
static void
do_t_setend (void)
{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_warn (_("setend use is deprecated for ARMv8"));
+
set_it_insn_type (OUTSIDE_IT_INSN);
if (inst.operands[0].imm)
inst.instruction |= 0x8;
now_it.block_length = 1;
mapping_state (MAP_THUMB);
now_it.insn = output_it_inst (cond, now_it.mask, NULL);
+ now_it.warn_deprecated = FALSE;
+ now_it.insn_cond = TRUE;
}
/* Close an automatic IT block.
handle_it_state (void)
{
now_it.state_handled = 1;
+ now_it.insn_cond = FALSE;
switch (now_it.state)
{
}
else
{
+ now_it.insn_cond = TRUE;
now_it_add_mask (inst.cond);
}
case NEUTRAL_IT_INSN:
now_it.block_length++;
+ now_it.insn_cond = TRUE;
if (now_it.block_length > 4)
force_automatic_it_block_close ();
now_it.mask <<= 1;
now_it.mask &= 0x1f;
is_last = (now_it.mask == 0x10);
+ now_it.insn_cond = TRUE;
switch (inst.it_insn_type)
{
return SUCCESS;
}
+struct depr_insn_mask
+{
+ unsigned long pattern;
+ unsigned long mask;
+ const char* description;
+};
+
+/* List of 16-bit instruction patterns deprecated in an IT block in
+ ARMv8. */
+static const struct depr_insn_mask depr_it_insns[] = {
+ { 0xc000, 0xc000, N_("Short branches, Undefined, SVC, LDM/STM") },
+ { 0xb000, 0xb000, N_("Miscellaneous 16-bit instructions") },
+ { 0xa000, 0xb800, N_("ADR") },
+ { 0x4800, 0xf800, N_("Literal loads") },
+ { 0x4478, 0xf478, N_("Hi-register ADD, MOV, CMP, BX, BLX using pc") },
+ { 0x4487, 0xfc87, N_("Hi-register ADD, MOV, CMP using pc") },
+ { 0, 0, NULL }
+};
+
static void
it_fsm_post_encode (void)
{
if (!now_it.state_handled)
handle_it_state ();
+ if (now_it.insn_cond
+ && !now_it.warn_deprecated
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ {
+ if (inst.instruction >= 0x10000)
+ {
+ as_warn (_("it blocks containing wide Thumb instructions are "
+ "deprecated in ARMv8"));
+ now_it.warn_deprecated = TRUE;
+ }
+ else
+ {
+ const struct depr_insn_mask *p = depr_it_insns;
+
+ while (p->mask != 0)
+ {
+ if ((inst.instruction & p->mask) == p->pattern)
+ {
+ as_warn (_("it blocks containing 16-bit Thumb intsructions "
+ "of the following class are deprecated in ARMv8: "
+ "%s"), p->description);
+ now_it.warn_deprecated = TRUE;
+ break;
+ }
+
+ ++p;
+ }
+ }
+
+ if (now_it.block_length > 1)
+ {
+ as_warn (_("it blocks of more than one conditional instruction are "
+ "deprecated in ARMv8"));
+ now_it.warn_deprecated = TRUE;
+ }
+ }
+
is_last = (now_it.mask == 0x10);
if (is_last)
{
{"al", 0xe}
};
+#define UL_BARRIER(L,U,CODE,FEAT) \
+ { L, CODE, ARM_FEATURE (FEAT, 0) }, \
+ { U, CODE, ARM_FEATURE (FEAT, 0) }
+
static struct asm_barrier_opt barrier_opt_names[] =
{
- { "sy", 0xf }, { "SY", 0xf },
- { "un", 0x7 }, { "UN", 0x7 },
- { "st", 0xe }, { "ST", 0xe },
- { "unst", 0x6 }, { "UNST", 0x6 },
- { "ish", 0xb }, { "ISH", 0xb },
- { "sh", 0xb }, { "SH", 0xb },
- { "ishst", 0xa }, { "ISHST", 0xa },
- { "shst", 0xa }, { "SHST", 0xa },
- { "nsh", 0x7 }, { "NSH", 0x7 },
- { "nshst", 0x6 }, { "NSHST", 0x6 },
- { "osh", 0x3 }, { "OSH", 0x3 },
- { "oshst", 0x2 }, { "OSHST", 0x2 }
+ UL_BARRIER ("sy", "SY", 0xf, ARM_EXT_BARRIER),
+ UL_BARRIER ("st", "ST", 0xe, ARM_EXT_BARRIER),
+ UL_BARRIER ("ld", "LD", 0xd, ARM_EXT_V8),
+ UL_BARRIER ("ish", "ISH", 0xb, ARM_EXT_BARRIER),
+ UL_BARRIER ("sh", "SH", 0xb, ARM_EXT_BARRIER),
+ UL_BARRIER ("ishst", "ISHST", 0xa, ARM_EXT_BARRIER),
+ UL_BARRIER ("shst", "SHST", 0xa, ARM_EXT_BARRIER),
+ UL_BARRIER ("ishld", "ISHLD", 0x9, ARM_EXT_V8),
+ UL_BARRIER ("un", "UN", 0x7, ARM_EXT_BARRIER),
+ UL_BARRIER ("nsh", "NSH", 0x7, ARM_EXT_BARRIER),
+ UL_BARRIER ("unst", "UNST", 0x6, ARM_EXT_BARRIER),
+ UL_BARRIER ("nshst", "NSHST", 0x6, ARM_EXT_BARRIER),
+ UL_BARRIER ("nshld", "NSHLD", 0x5, ARM_EXT_V8),
+ UL_BARRIER ("osh", "OSH", 0x3, ARM_EXT_BARRIER),
+ UL_BARRIER ("oshst", "OSHST", 0x2, ARM_EXT_BARRIER),
+ UL_BARRIER ("oshld", "OSHLD", 0x1, ARM_EXT_V8)
};
+#undef UL_BARRIER
+
/* Table of ARM-format instructions. */
/* Macros for gluing together operand strings. N.B. In all cases
TUF("pldw", 410f000, f830f000, 1, (ADDR), pld, t_pld),
+ /* AArchv8 instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v8
+
+ tCE("sevl", 320f005, _sevl, 0, (), noargs, t_hint),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT NULL
+ TUF("dcps1", 0, f78f8001, 0, (), noargs, noargs),
+ TUF("dcps2", 0, f78f8002, 0, (), noargs, noargs),
+ TUF("dcps3", 0, f78f8003, 0, (), noargs, noargs),
+
#undef ARM_VARIANT
#define ARM_VARIANT & fpu_fpa_ext_v1 /* Core FPA instruction set (V1). */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT NULL
cCE("wfs", e200110, 1, (RR), rd),
cCE("rfs", e300110, 1, (RR), rd),
}
}
- 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. */
- if (newimm == (unsigned int) FAIL
- && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
+ /* If the offset is negative, we should use encoding A2 for ADR. */
+ if ((temp & 0xfff0000) == 0x28f0000 && value < 0)
+ newimm = negate_data_op (&temp, value);
+ else
+ {
+ newimm = encode_arm_immediate (value);
+
+ /* If the instruction will fail, see if we can fix things up by
+ changing the opcode. */
+ if (newimm == (unsigned int) FAIL)
+ newimm = negate_data_op (&temp, value);
+ }
+
+ if (newimm == (unsigned int) FAIL)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid constant (%lx) after fixup"),
thumb_bl_common:
#ifdef OBJ_ELF
- if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4 &&
- fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4
+ && fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23;
#endif
1 of the base address. */
value = (value + 1) & ~ 1;
- if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
- {
- if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)))
- as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
- else if ((value & ~0x1ffffff)
- && ((value & ~0x1ffffff) != ~0x1ffffff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Thumb2 branch out of range"));
- }
+ if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
+ {
+ if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
+ else if ((value & ~0x1ffffff)
+ && ((value & ~0x1ffffff) != ~0x1ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Thumb2 branch out of range"));
+ }
if (fixP->fx_done || !seg->use_rela_p)
encode_thumb2_b_bl_offset (buf, value);
return (target_big_endian
? "elf32-bigarm-vxworks"
: "elf32-littlearm-vxworks");
+#elif defined (TE_NACL)
+ return (target_big_endian
+ ? "elf32-bigarm-nacl"
+ : "elf32-littlearm-nacl");
#else
if (target_big_endian)
return "elf32-bigarm";
ARM_ARCH_OPT ("armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP),
ARM_ARCH_OPT ("armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP),
ARM_ARCH_OPT ("armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8-a", ARM_ARCH_V8A, FPU_ARCH_VFP),
ARM_ARCH_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP),
ARM_ARCH_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP),
ARM_ARCH_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP),
#define ARM_EXT_OPT(N, V, AA) { N, sizeof (N) - 1, V, AA }
static const struct arm_option_extension_value_table arm_extensions[] =
{
+ ARM_EXT_OPT ("crypto", FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("fp", FPU_ARCH_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
ARM_EXT_OPT ("idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0),
ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)),
ARM_EXT_OPT ("iwmmxt",ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY),
ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY),
ARM_EXT_OPT ("mp", ARM_FEATURE (ARM_EXT_MP, 0),
ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)),
+ ARM_EXT_OPT ("simd", FPU_ARCH_NEON_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
ARM_EXT_OPT ("os", ARM_FEATURE (ARM_EXT_OS, 0),
ARM_FEATURE (ARM_EXT_V6M, 0)),
ARM_EXT_OPT ("sec", ARM_FEATURE (ARM_EXT_SEC, 0),
{"vfpv4-d16", FPU_ARCH_VFP_V4D16},
{"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16},
{"neon-vfpv4", FPU_ARCH_NEON_VFP_V4},
+ {"fp-armv8", FPU_ARCH_VFP_ARMV8},
+ {"neon-fp-armv8", FPU_ARCH_NEON_VFP_ARMV8},
+ {"crypto-neon-fp-armv8",
+ FPU_ARCH_CRYPTO_NEON_VFP_ARMV8},
{NULL, ARM_ARCH_NONE}
};
{11, ARM_ARCH_V6M},
{12, ARM_ARCH_V6SM},
{8, ARM_ARCH_V6T2},
- {10, ARM_ARCH_V7A},
+ {10, ARM_ARCH_V7A_IDIV_MP_SEC_VIRT},
{10, ARM_ARCH_V7R},
{10, ARM_ARCH_V7M},
+ {14, ARM_ARCH_V8A},
{0, ARM_ARCH_NONE}
};
int arch;
char profile;
int virt_sec = 0;
+ int fp16_optional = 0;
arm_feature_set flags;
arm_feature_set tmp;
const cpu_arch_ver_table *p;
ARM_CPU_HAS_FEATURE (flags, arm_arch_t2) ? 2 : 1);
/* Tag_VFP_arch. */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma))
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_armv8))
+ aeabi_set_attribute_int (Tag_VFP_arch, 7);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma))
aeabi_set_attribute_int (Tag_VFP_arch,
ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32)
? 5 : 6);
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32))
- aeabi_set_attribute_int (Tag_VFP_arch, 3);
+ {
+ fp16_optional = 1;
+ aeabi_set_attribute_int (Tag_VFP_arch, 3);
+ }
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v3xd))
- aeabi_set_attribute_int (Tag_VFP_arch, 4);
+ {
+ aeabi_set_attribute_int (Tag_VFP_arch, 4);
+ fp16_optional = 1;
+ }
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v2))
aeabi_set_attribute_int (Tag_VFP_arch, 2);
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1)
aeabi_set_attribute_int (Tag_WMMX_arch, 1);
/* Tag_Advanced_SIMD_arch (formerly Tag_NEON_arch). */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_v1))
- aeabi_set_attribute_int
- (Tag_Advanced_SIMD_arch, (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma)
- ? 2 : 1));
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_armv8))
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 3);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_v1))
+ {
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma))
+ {
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 2);
+ }
+ else
+ {
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 1);
+ fp16_optional = 1;
+ }
+ }
/* Tag_VFP_HP_extension (formerly Tag_NEON_FP16_arch). */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16))
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16) && fp16_optional)
aeabi_set_attribute_int (Tag_VFP_HP_extension, 1);
/* Tag_DIV_use.
in ARM state, or when Thumb integer divide instructions have been used,
but we have no architecture profile set, nor have we any ARM instructions.
+ For ARMv8 we set the tag to 0 as integer divide is implied by the base
+ architecture.
+
For new architectures we will have to check these tests. */
- gas_assert (arch <= TAG_CPU_ARCH_V7E_M);
- if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv)
- || (profile == '\0'
- && ARM_CPU_HAS_FEATURE (flags, arm_ext_div)
- && !ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any)))
+ gas_assert (arch <= TAG_CPU_ARCH_V8);
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v8))
+ aeabi_set_attribute_int (Tag_DIV_use, 0);
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv)
+ || (profile == '\0'
+ && ARM_CPU_HAS_FEATURE (flags, arm_ext_div)
+ && !ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any)))
aeabi_set_attribute_int (Tag_DIV_use, 2);
/* Tag_MP_extension_use. */