static const arm_feature_set crc_ext_armv8 =
ARM_FEATURE_COPROC (CRC_EXT_ARMV8);
static const arm_feature_set fpu_neon_ext_v8_1 =
- ARM_FEATURE_COPROC (FPU_NEON_EXT_ARMV8 | FPU_NEON_EXT_RDMA);
+ ARM_FEATURE_COPROC (FPU_NEON_EXT_RDMA);
static int mfloat_abi_opt = -1;
/* Record user cpu selection for object attributes. */
struct reloc_entry
{
- char * name;
+ const char * name;
bfd_reloc_code_real_type reloc;
};
_("cannot use register index with PC-relative addressing")
#define BAD_PC_WRITEBACK \
_("cannot use writeback with PC-relative addressing")
-#define BAD_RANGE _("branch out of range")
+#define BAD_RANGE _("branch out of range")
+#define BAD_FP16 _("selected processor does not support fp16 instruction")
#define UNPRED_REG(R) _("using " R " results in unpredictable behaviour")
static struct hash_control * arm_ops_hsh;
return c->value;
}
+/* Record a use of the given feature. */
+static void
+record_feature_use (const arm_feature_set *feature)
+{
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, *feature);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *feature);
+}
+
/* If the given feature available in the selected CPU, mark it as used.
Returns TRUE iff feature is available. */
static bfd_boolean
/* 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);
+ record_feature_use (feature);
return TRUE;
}
#define rotate_left(v, n) (v << (n & 31) | v >> ((32 - n) & 31))
+/* If the current inst is scalar ARMv8.2 fp16 instruction, do special encoding.
+
+ The only binary encoding difference is the Coprocessor number. Coprocessor
+ 9 is used for half-precision calculations or conversions. The format of the
+ instruction is the same as the equivalent Coprocessor 10 instuction that
+ exists for Single-Precision operation. */
+
+static void
+do_scalar_fp16_v82_encode (void)
+{
+ if (inst.cond != COND_ALWAYS)
+ as_warn (_("ARMv8.2 scalar fp16 instruction cannot be conditional,"
+ " the behaviour is UNPREDICTABLE"));
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16),
+ _(BAD_FP16));
+
+ inst.instruction = (inst.instruction & 0xfffff0ff) | 0x900;
+ mark_feature_used (&arm_ext_fp16);
+}
+
/* If VAL can be encoded in the immediate field of an ARM instruction,
return the encoded form. Otherwise, return FAIL. */
X(2, (S, R), SINGLE), \
X(2, (R, S), SINGLE), \
X(2, (F, R), SINGLE), \
- X(2, (R, F), SINGLE)
+ X(2, (R, F), SINGLE), \
+/* Half float shape supported so far. */\
+ X (2, (H, D), MIXED), \
+ X (2, (D, H), MIXED), \
+ X (2, (H, F), MIXED), \
+ X (2, (F, H), MIXED), \
+ X (2, (H, H), HALF), \
+ X (2, (H, R), HALF), \
+ X (2, (R, H), HALF), \
+ X (2, (H, I), HALF), \
+ X (3, (H, H, H), HALF), \
+ X (3, (H, F, I), MIXED), \
+ X (3, (F, H, I), MIXED)
#define S2(A,B) NS_##A##B
#define S3(A,B,C) NS_##A##B##C
enum neon_shape_class
{
+ SC_HALF,
SC_SINGLE,
SC_DOUBLE,
SC_QUAD,
enum neon_shape_el
{
+ SE_H,
SE_F,
SE_D,
SE_Q,
/* Register widths of above. */
static unsigned neon_shape_el_size[] =
{
+ 16,
32,
64,
128,
#define N_SUF_32 (N_SU_32 | N_F32)
#define N_I_ALL (N_I8 | N_I16 | N_I32 | N_I64)
#define N_IF_32 (N_I8 | N_I16 | N_I32 | N_F32)
+#define N_F_ALL (N_F16 | N_F32 | N_F64)
/* Pass this as the first type argument to neon_check_type to ignore types
altogether. */
switch (neon_shape_tab[shape].el[j])
{
+ /* If a .f16, .16, .u16, .s16 type specifier is given over
+ a VFP single precision register operand, it's essentially
+ means only half of the register is used.
+
+ If the type specifier is given after the mnemonics, the
+ information is stored in inst.vectype. If the type specifier
+ is given after register operand, the information is stored
+ in inst.operands[].vectype.
+
+ When there is only one type specifier, and all the register
+ operands are the same type of hardware register, the type
+ specifier applies to all register operands.
+
+ If no type specifier is given, the shape is inferred from
+ operand information.
+
+ for example:
+ vadd.f16 s0, s1, s2: NS_HHH
+ vabs.f16 s0, s1: NS_HH
+ vmov.f16 s0, r1: NS_HR
+ vmov.f16 r0, s1: NS_RH
+ vcvt.f16 r0, s1: NS_RH
+ vcvt.f16.s32 s2, s2, #29: NS_HFI
+ vcvt.f16.s32 s2, s2: NS_HF
+ */
+ case SE_H:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].issingle
+ && !inst.operands[j].isquad
+ && ((inst.vectype.elems == 1
+ && inst.vectype.el[0].size == 16)
+ || (inst.vectype.elems > 1
+ && inst.vectype.el[j].size == 16)
+ || (inst.vectype.elems == 0
+ && inst.operands[j].vectype.type != NT_invtype
+ && inst.operands[j].vectype.size == 16))))
+ matches = 0;
+ break;
+
case SE_F:
if (!(inst.operands[j].isreg
&& inst.operands[j].isvec
&& inst.operands[j].issingle
- && !inst.operands[j].isquad))
+ && !inst.operands[j].isquad
+ && ((inst.vectype.elems == 1 && inst.vectype.el[0].size == 32)
+ || (inst.vectype.elems > 1 && inst.vectype.el[j].size == 32)
+ || (inst.vectype.elems == 0
+ && (inst.operands[j].vectype.size == 32
+ || inst.operands[j].vectype.type == NT_invtype)))))
matches = 0;
break;
*type = NT_untyped;
else if ((mask & (N_P8 | N_P16 | N_P64)) != 0)
*type = NT_poly;
- else if ((mask & (N_F16 | N_F32 | N_F64)) != 0)
+ else if ((mask & (N_F_ALL)) != 0)
*type = NT_float;
else
return FAIL;
else
match = g_size;
+ /* FP16 will use a single precision register. */
+ if (regwidth == 32 && match == 16)
+ {
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16))
+ match = regwidth;
+ else
+ {
+ inst.error = _(BAD_FP16);
+ return badtype;
+ }
+ }
+
if (regwidth != match)
{
first_error (_("operand size must match register width"));
{
int is_add = (inst.instruction & 0x0fffffff) == N_MNEM_vadd;
- if (rs == NS_FFF)
+ if (rs == NS_FFF || rs == NS_HHH)
{
if (is_add)
do_vfp_nsyn_opcode ("fadds");
else
do_vfp_nsyn_opcode ("fsubs");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
}
else
{
switch (args)
{
case 2:
- rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
- et = neon_check_type (2, rs,
- N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_NULL);
+ et = neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY | N_VFP);
break;
case 3:
- rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
- et = neon_check_type (3, rs,
- N_EQK | N_VFP, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ rs = neon_select_shape (NS_HHH, NS_FFF, NS_DDD, NS_NULL);
+ et = neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
+ N_F_ALL | N_KEY | N_VFP);
break;
default:
{
int is_mla = (inst.instruction & 0x0fffffff) == N_MNEM_vmla;
- if (rs == NS_FFF)
+ if (rs == NS_FFF || rs == NS_HHH)
{
if (is_mla)
do_vfp_nsyn_opcode ("fmacs");
else
do_vfp_nsyn_opcode ("fnmacs");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
}
else
{
{
int is_fma = (inst.instruction & 0x0fffffff) == N_MNEM_vfma;
- if (rs == NS_FFF)
+ if (rs == NS_FFF || rs == NS_HHH)
{
if (is_fma)
do_vfp_nsyn_opcode ("ffmas");
else
do_vfp_nsyn_opcode ("ffnmas");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
}
else
{
static void
do_vfp_nsyn_mul (enum neon_shape rs)
{
- if (rs == NS_FFF)
- do_vfp_nsyn_opcode ("fmuls");
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ do_vfp_nsyn_opcode ("fmuls");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
else
do_vfp_nsyn_opcode ("fmuld");
}
do_vfp_nsyn_abs_neg (enum neon_shape rs)
{
int is_neg = (inst.instruction & 0x80) != 0;
- neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_VFP | N_KEY);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_VFP | N_KEY);
- if (rs == NS_FF)
+ if (rs == NS_FF || rs == NS_HH)
{
if (is_neg)
do_vfp_nsyn_opcode ("fnegs");
else
do_vfp_nsyn_opcode ("fabss");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
}
else
{
static void
do_vfp_nsyn_sqrt (void)
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
- neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ enum neon_shape rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_NULL);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY | N_VFP);
+
+ if (rs == NS_FF || rs == NS_HH)
+ {
+ do_vfp_nsyn_opcode ("fsqrts");
- if (rs == NS_FF)
- do_vfp_nsyn_opcode ("fsqrts");
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
+ }
else
do_vfp_nsyn_opcode ("fsqrtd");
}
static void
do_vfp_nsyn_div (void)
{
- enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
+ enum neon_shape rs = neon_select_shape (NS_HHH, NS_FFF, NS_DDD, NS_NULL);
neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
- N_F32 | N_F64 | N_KEY | N_VFP);
+ N_F_ALL | N_KEY | N_VFP);
- if (rs == NS_FFF)
- do_vfp_nsyn_opcode ("fdivs");
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ do_vfp_nsyn_opcode ("fdivs");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
else
do_vfp_nsyn_opcode ("fdivd");
}
static void
do_vfp_nsyn_nmul (void)
{
- enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
+ enum neon_shape rs = neon_select_shape (NS_HHH, NS_FFF, NS_DDD, NS_NULL);
neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
- N_F32 | N_F64 | N_KEY | N_VFP);
+ N_F_ALL | N_KEY | N_VFP);
- if (rs == NS_FFF)
+ if (rs == NS_FFF || rs == NS_HHH)
{
NEON_ENCODE (SINGLE, inst);
do_vfp_sp_dyadic ();
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
}
else
{
do_vfp_dp_rd_rn_rm ();
}
do_vfp_cond_or_thumb ();
+
}
static void
do_vfp_nsyn_cmp (void)
{
+ enum neon_shape rs;
if (inst.operands[1].isreg)
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
- neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_NULL);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY | N_VFP);
- if (rs == NS_FF)
+ if (rs == NS_FF || rs == NS_HH)
{
NEON_ENCODE (SINGLE, inst);
do_vfp_sp_monadic ();
}
else
{
- enum neon_shape rs = neon_select_shape (NS_FI, NS_DI, NS_NULL);
- neon_check_type (2, rs, N_F32 | N_F64 | N_KEY | N_VFP, N_EQK);
+ rs = neon_select_shape (NS_HI, NS_FI, NS_DI, NS_NULL);
+ neon_check_type (2, rs, N_F_ALL | N_KEY | N_VFP, N_EQK);
switch (inst.instruction & 0x0fffffff)
{
abort ();
}
- if (rs == NS_FI)
+ if (rs == NS_FI || rs == NS_HI)
{
NEON_ENCODE (SINGLE, inst);
do_vfp_sp_compare_z ();
}
}
do_vfp_cond_or_thumb ();
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HI || rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
}
static void
}
}
+static void
+do_neon_qrdmlah (void)
+{
+ /* Check we're on the correct architecture. */
+ if (!mark_feature_used (&fpu_neon_ext_armv8))
+ inst.error =
+ _("instruction form not available on this architecture.");
+ else if (!mark_feature_used (&fpu_neon_ext_v8_1))
+ {
+ as_warn (_("this instruction implies use of ARMv8.1 AdvSIMD."));
+ record_feature_use (&fpu_neon_ext_v8_1);
+ }
+
+ if (inst.operands[2].isscalar)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, neon_quad (rs));
+ }
+ else
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ /* The U bit (rounding) comes from bit mask. */
+ neon_three_same (neon_quad (rs), 0, et.size);
+ }
+}
+
static void
do_neon_fcmp_absolute (void)
{
/* Half-precision conversions. */ \
CVT_VAR (f32_f16, N_F32, N_F16, whole_reg, NULL, NULL, NULL) \
CVT_VAR (f16_f32, N_F16, N_F32, whole_reg, NULL, NULL, NULL) \
+ /* New VCVT instructions introduced by ARMv8.2 fp16 extension. \
+ Compared with single/double precision variants, only the co-processor \
+ field is different, so the encoding flow is reused here. */ \
+ CVT_VAR (f16_s32, N_F16 | N_KEY, N_S32, N_VFP, "fsltos", "fsitos", NULL) \
+ CVT_VAR (f16_u32, N_F16 | N_KEY, N_U32, N_VFP, "fultos", "fuitos", NULL) \
+ CVT_VAR (u32_f16, N_U32, N_F16 | N_KEY, N_VFP, "ftouls", "ftouis", "ftouizs")\
+ CVT_VAR (s32_f16, N_S32, N_F16 | N_KEY, N_VFP, "ftosls", "ftosis", "ftosizs")\
/* VFP instructions. */ \
CVT_VAR (f32_f64, N_F32, N_F64, N_VFP, NULL, "fcvtsd", NULL) \
CVT_VAR (f64_f32, N_F64, N_F32, N_VFP, NULL, "fcvtds", NULL) \
{
const char *opname = 0;
- if (rs == NS_DDI || rs == NS_QQI || rs == NS_FFI)
+ if (rs == NS_DDI || rs == NS_QQI || rs == NS_FFI
+ || rs == NS_FHI || rs == NS_HFI)
{
/* Conversions with immediate bitshift. */
const char *enc[] =
if (opname)
do_vfp_nsyn_opcode (opname);
+
+ /* ARMv8.2 fp16 VCVT instruction. */
+ if (flavour == neon_cvt_flavour_s32_f16
+ || flavour == neon_cvt_flavour_u32_f16
+ || flavour == neon_cvt_flavour_f16_u32
+ || flavour == neon_cvt_flavour_f16_s32)
+ do_scalar_fp16_v82_encode ();
}
static void
do_vfp_nsyn_cvtz (void)
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_NULL);
+ enum neon_shape rs = neon_select_shape (NS_FH, NS_FF, NS_FD, NS_NULL);
enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
const char *enc[] =
{
constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
_(BAD_FPU));
+ if (flavour == neon_cvt_flavour_s32_f16
+ || flavour == neon_cvt_flavour_u32_f16)
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16),
+ _(BAD_FP16));
+
set_it_insn_type (OUTSIDE_IT_INSN);
switch (flavour)
sz = 0;
op = 1;
break;
+ case neon_cvt_flavour_s32_f16:
+ sz = 0;
+ op = 1;
+ break;
case neon_cvt_flavour_u32_f64:
sz = 1;
op = 0;
sz = 0;
op = 0;
break;
+ case neon_cvt_flavour_u32_f16:
+ sz = 0;
+ op = 0;
+ break;
default:
first_error (_("invalid instruction shape"));
return;
encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
encode_arm_vfp_reg (inst.operands[1].reg, sz == 1 ? VFP_REG_Dm : VFP_REG_Sm);
inst.instruction |= sz << 8;
+
+ /* ARMv8.2 fp16 VCVT instruction. */
+ if (flavour == neon_cvt_flavour_s32_f16
+ ||flavour == neon_cvt_flavour_u32_f16)
+ do_scalar_fp16_v82_encode ();
inst.instruction |= op << 7;
inst.instruction |= rm << 16;
inst.instruction |= 0xf0000000;
do_neon_cvt_1 (enum neon_cvt_mode mode)
{
enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_FFI, NS_DD, NS_QQ,
- NS_FD, NS_DF, NS_FF, NS_QD, NS_DQ, NS_NULL);
+ NS_FD, NS_DF, NS_FF, NS_QD, NS_DQ,
+ NS_FH, NS_HF, NS_FHI, NS_HFI,
+ NS_NULL);
enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
/* PR11109: Handle round-to-zero for VCVT conversions. */
return;
}
+ /* ARMv8.2 fp16 VCVT conversions. */
+ if (mode == neon_cvt_mode_z
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16)
+ && (flavour == neon_cvt_flavour_s32_f16
+ || flavour == neon_cvt_flavour_u32_f16)
+ && (rs == NS_FH))
+ {
+ do_vfp_nsyn_cvtz ();
+ do_scalar_fp16_v82_encode ();
+ return;
+ }
+
/* VFP rather than Neon conversions. */
if (flavour >= neon_cvt_flavour_first_fp)
{
static void
do_neon_cvttb_1 (bfd_boolean t)
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_DF, NS_NULL);
+ enum neon_shape rs = neon_select_shape (NS_HF, NS_HD, NS_FH, NS_FF, NS_FD,
+ NS_DF, NS_DH, NS_NULL);
if (rs == NS_NULL)
return;
do_neon_mov (void)
{
enum neon_shape rs = neon_select_shape (NS_RRFF, NS_FFRR, NS_DRR, NS_RRD,
- NS_QQ, NS_DD, NS_QI, NS_DI, NS_SR, NS_RS, NS_FF, NS_FI, NS_RF, NS_FR,
- NS_NULL);
+ NS_QQ, NS_DD, NS_QI, NS_DI, NS_SR,
+ NS_RS, NS_FF, NS_FI, NS_RF, NS_FR,
+ NS_HR, NS_RH, NS_HI, NS_NULL);
struct neon_type_el et;
const char *ldconst = 0;
do_vfp_nsyn_opcode ("fcpys");
break;
+ case NS_HI:
case NS_FI: /* case 10 (fconsts). */
ldconst = "fconsts";
encode_fconstd:
{
inst.operands[1].imm = neon_qfloat_bits (inst.operands[1].imm);
do_vfp_nsyn_opcode (ldconst);
+
+ /* ARMv8.2 fp16 vmov.f16 instruction. */
+ if (rs == NS_HI)
+ do_scalar_fp16_v82_encode ();
}
else
first_error (_("immediate out of range"));
break;
+ case NS_RH:
case NS_RF: /* case 12 (fmrs). */
do_vfp_nsyn_opcode ("fmrs");
+ /* ARMv8.2 fp16 vmov.f16 instruction. */
+ if (rs == NS_RH)
+ do_scalar_fp16_v82_encode ();
break;
+ case NS_HR:
case NS_FR: /* case 13 (fmsr). */
do_vfp_nsyn_opcode ("fmsr");
+ /* ARMv8.2 fp16 vmov.f16 instruction. */
+ if (rs == NS_HR)
+ do_scalar_fp16_v82_encode ();
break;
/* The encoders for the fmrrs and fmsrr instructions expect three operands
et.size - imm);
}
+static void
+do_neon_movhf (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_HH, NS_NULL);
+ constraint (rs != NS_HH, _("invalid suffix"));
+
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
+
+ do_vfp_sp_monadic ();
+
+ inst.is_neon = 1;
+ inst.instruction |= 0xf0000000;
+}
+
static void
do_neon_movl (void)
{
do_vfp_nsyn_opcode ("flds");
else
do_vfp_nsyn_opcode ("fsts");
+
+ /* ARMv8.2 vldr.16/vstr.16 instruction. */
+ if (inst.vectype.el[0].size == 16)
+ do_scalar_fp16_v82_encode ();
}
else
{
NEON_ENCODE (FPV8, inst);
- if (rs == NS_FFF)
- do_vfp_sp_dyadic ();
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ do_vfp_sp_dyadic ();
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
else
do_vfp_dp_rd_rn_rm ();
static void
do_vrint_1 (enum neon_cvt_mode mode)
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_QQ, NS_NULL);
+ enum neon_shape rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_QQ, NS_NULL);
struct neon_type_el et;
if (rs == NS_NULL)
constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
_(BAD_FPU));
- et = neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ et = neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY
+ | N_VFP);
if (et.type != NT_invtype)
{
/* VFP encodings. */
set_it_insn_type (OUTSIDE_IT_INSN);
NEON_ENCODE (FPV8, inst);
- if (rs == NS_FF)
+ if (rs == NS_FF || rs == NS_HH)
do_vfp_sp_monadic ();
else
do_vfp_dp_rd_rm ();
inst.instruction |= (rs == NS_DD) << 8;
do_vfp_cond_or_thumb ();
+
+ /* ARMv8.2 fp16 vrint instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
}
else
{
NCE(vmov, 0, 1, (VMOV), neon_mov),
NCE(vmovq, 0, 1, (VMOV), neon_mov),
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_fp16
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_fp16
+ /* New instructions added from v8.2, allowing the extraction and insertion of
+ the upper 16 bits of a 32-bit vector register. */
+ NCE (vmovx, eb00a40, 2, (RVS, RVS), neon_movhf),
+ NCE (vins, eb00ac0, 2, (RVS, RVS), neon_movhf),
+
#undef THUMB_VARIANT
#define THUMB_VARIANT & fpu_neon_ext_v1
#undef ARM_VARIANT
NUF(vrsqrts, 0200f10, 3, (RNDQ, oRNDQ, RNDQ), neon_step),
NUF(vrsqrtsq, 0200f10, 3, (RNQ, oRNQ, RNQ), neon_step),
/* ARM v8.1 extension. */
- nUF(vqrdmlah, _vqrdmlah, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qdmulh),
- nUF(vqrdmlahq, _vqrdmlah, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qdmulh),
- nUF(vqrdmlsh, _vqrdmlsh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qdmulh),
- nUF(vqrdmlshq, _vqrdmlsh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qdmulh),
+ nUF (vqrdmlah, _vqrdmlah, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qrdmlah),
+ nUF (vqrdmlahq, _vqrdmlah, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qrdmlah),
+ nUF (vqrdmlsh, _vqrdmlsh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qrdmlah),
+ nUF (vqrdmlshq, _vqrdmlsh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qrdmlah),
/* Two address, int/float. Types S8 S16 S32 F32. */
NUF(vabsq, 1b10300, 2, (RNQ, RNQ), neon_abs_neg),
case BFD_RELOC_ARM_CP_OFF_IMM:
case BFD_RELOC_ARM_T32_CP_OFF_IMM:
- if (value < -1023 || value > 1023 || (value & 3))
+ if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM)
+ newval = md_chars_to_number (buf, INSN_SIZE);
+ else
+ newval = get_thumb32_insn (buf);
+ if ((newval & 0x0f200f00) == 0x0d000900)
+ {
+ /* This is a fp16 vstr/vldr. The immediate offset in the mnemonic
+ has permitted values that are multiples of 2, in the range 0
+ to 510. */
+ if (value < -510 || value > 510 || (value & 1))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("co-processor offset out of range"));
+ }
+ else if (value < -1023 || value > 1023 || (value & 3))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("co-processor offset out of range"));
cp_off_common:
else
{
newval &= 0xff7fff00;
+ if ((newval & 0x0f200f00) == 0x0d000900)
+ {
+ /* This is a fp16 vstr/vldr.
+
+ It requires the immediate offset in the instruction is shifted
+ left by 1 to be a half-word offset.
+
+ Here, left shift by 1 first, and later right shift by 2
+ should get the right offset. */
+ value <<= 1;
+ }
newval |= (value >> 2) | (sign ? INDEX_UP : 0);
}
if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
default:
{
- char * type;
+ const char * type;
switch (fixp->fx_r_type)
{
struct arm_option_table
{
- char *option; /* Option name to match. */
- char *help; /* Help information. */
+ const char *option; /* Option name to match. */
+ const char *help; /* Help information. */
int *var; /* Variable to change. */
int value; /* What to change it to. */
- char *deprecated; /* If non-null, print this message. */
+ const char *deprecated; /* If non-null, print this message. */
};
struct arm_option_table arm_opts[] =
struct arm_legacy_option_table
{
- char *option; /* Option name to match. */
+ const char *option; /* Option name to match. */
const arm_feature_set **var; /* Variable to change. */
const arm_feature_set value; /* What to change it to. */
- char *deprecated; /* If non-null, print this message. */
+ const char *deprecated; /* If non-null, print this message. */
};
const struct arm_legacy_option_table arm_legacy_opts[] =
struct arm_cpu_option_table
{
- char *name;
+ const char *name;
size_t name_len;
const arm_feature_set value;
/* For some CPUs we assume an FPU unless the user explicitly sets
struct arm_arch_option_table
{
- char *name;
+ const char *name;
size_t name_len;
const arm_feature_set value;
const arm_feature_set default_fpu;
/* ISA extensions in the co-processor and main instruction set space. */
struct arm_option_extension_value_table
{
- char *name;
+ const char *name;
size_t name_len;
const arm_feature_set merge_value;
const arm_feature_set clear_value;
ARM_EXT_OPT ("mp", ARM_FEATURE_CORE_LOW (ARM_EXT_MP),
ARM_FEATURE_CORE_LOW (ARM_EXT_MP),
ARM_FEATURE_CORE_LOW (ARM_EXT_V7A | ARM_EXT_V7R)),
- ARM_EXT_OPT ("simd", FPU_ARCH_NEON_VFP_ARMV8,
- ARM_FEATURE_COPROC (FPU_NEON_ARMV8),
- ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
ARM_EXT_OPT ("os", ARM_FEATURE_CORE_LOW (ARM_EXT_OS),
ARM_FEATURE_CORE_LOW (ARM_EXT_OS),
ARM_FEATURE_CORE_LOW (ARM_EXT_V6M)),
ARM_EXT_OPT ("pan", ARM_FEATURE_CORE_HIGH (ARM_EXT2_PAN),
ARM_FEATURE (ARM_EXT_V8, ARM_EXT2_PAN, 0),
ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("rdma", FPU_ARCH_NEON_VFP_ARMV8_1,
+ ARM_FEATURE_COPROC (FPU_NEON_ARMV8 | FPU_NEON_EXT_RDMA),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
ARM_EXT_OPT ("sec", ARM_FEATURE_CORE_LOW (ARM_EXT_SEC),
ARM_FEATURE_CORE_LOW (ARM_EXT_SEC),
ARM_FEATURE_CORE_LOW (ARM_EXT_V6K | ARM_EXT_V7A)),
+ ARM_EXT_OPT ("simd", FPU_ARCH_NEON_VFP_ARMV8,
+ ARM_FEATURE_COPROC (FPU_NEON_ARMV8),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
ARM_EXT_OPT ("virt", ARM_FEATURE_CORE_LOW (ARM_EXT_VIRT | ARM_EXT_ADIV
| ARM_EXT_DIV),
ARM_FEATURE_CORE_LOW (ARM_EXT_VIRT),
ARM_FEATURE_CORE_LOW (ARM_EXT_V7A)),
- ARM_EXT_OPT ("rdma", FPU_ARCH_NEON_VFP_ARMV8,
- ARM_FEATURE_COPROC (FPU_NEON_ARMV8 | FPU_NEON_EXT_RDMA),
- ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
ARM_EXT_OPT ("xscale",ARM_FEATURE_COPROC (ARM_CEXT_XSCALE),
ARM_FEATURE_COPROC (ARM_CEXT_XSCALE), ARM_ANY),
{ NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE, ARM_ARCH_NONE }
/* ISA floating-point and Advanced SIMD extensions. */
struct arm_option_fpu_value_table
{
- char *name;
+ const char *name;
const arm_feature_set value;
};
struct arm_option_value_table
{
- char *name;
+ const char *name;
long value;
};
struct arm_long_option_table
{
- char * option; /* Substring to match. */
- char * help; /* Help information. */
+ const char * option; /* Substring to match. */
+ const char * help; /* Help information. */
int (* func) (char * subopt); /* Function to decode sub-option. */
- char * deprecated; /* If non-null, print this message. */
+ const char * deprecated; /* If non-null, print this message. */
};
static bfd_boolean