aboutsummaryrefslogtreecommitdiff
path: root/gas/config/tc-arm.c
diff options
context:
space:
mode:
authorRenlin Li <renlin.li@arm.com>2016-02-24 17:59:35 +0000
committerRenlin Li <renlin.li@arm.com>2016-02-24 18:09:02 +0000
commit9db2f6b4262ec9152c2c6be90e2ea913423e6d3d (patch)
tree91121b6fc44e1aa9f51f82e3815bdca1ca25b980 /gas/config/tc-arm.c
parent657f9cde9d531c9929bef9e02a8064101d568f50 (diff)
downloadgdb-9db2f6b4262ec9152c2c6be90e2ea913423e6d3d.zip
gdb-9db2f6b4262ec9152c2c6be90e2ea913423e6d3d.tar.gz
gdb-9db2f6b4262ec9152c2c6be90e2ea913423e6d3d.tar.bz2
[GAS][ARM][3/3]Add armv8.2 fp16 scalar instruction support. Based on SE_H instruction shape.
gas/ 2016-02-24 Renlin Li <renlin.li@arm.com> * config/tc-arm.c (BAD_FP16): New error message macro. (do_scalar_fp16_v82_encode): Change the coproc field to 9 for armv8.2 fp16 scalar instructions. (neon_check_type): Allow different size from key. (do_vfp_nsyn_add_sub): Add support SE_H shape support. (try_vfp_nsyn): Likewise. (do_vfp_nsyn_mla_mls): Likewise. (do_vfp_nsyn_fma_fms): Likewise. (do_vfp_nsyn_ldm_stm): Likewise (do_vfp_nsyn_sqrt): Likewise (do_vfp_nsyn_div): Likewise (do_vfp_nsyn_nmul): Likewise. (do_vfp_nsyn_cmp): Likewise. (do_neon_shll): Likewise. (do_vfp_nsyn_cvt_fpv8): Likewise. (do_neon_cvttb_2): Likewise. (do_neon_mov): Likewise. (do_neon_rshift_round_imm): Likewise. (do_neon_ldr_str): Likewise. (do_vfp_nsyn_fpv8): Likewise. (do_vmaxnm): Likewise. (do_vrint_1): Likewise. (insns): New entry for vins, vmovx. (md_apply_fix): Left shift 1 bit for fp16 vldr/vstr. * testsuite/gas/arm/armv8-2-fp16-scalar-thumb.d: New. * testsuite/gas/arm/armv8-2-fp16-scalar.d: New. * testsuite/gas/arm/armv8-2-fp16-scalar.s: New. * testsuite/gas/arm/armv8-2-fp16-scalar-bad.s: New * testsuite/gas/arm/armv8-2-fp16-scalar-bad.d: New * testsuite/gas/arm/armv8-2-fp16-scalar-bad.l: New
Diffstat (limited to 'gas/config/tc-arm.c')
-rw-r--r--gas/config/tc-arm.c275
1 files changed, 236 insertions, 39 deletions
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index 2a7f649..6cc7edc 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -784,7 +784,8 @@ struct asm_opcode
_("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;
@@ -7263,6 +7264,26 @@ parse_operands (char *str, const unsigned int *pattern, bfd_boolean thumb)
#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. */
@@ -13896,6 +13917,18 @@ neon_check_type (unsigned els, enum neon_shape ns, ...)
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"));
@@ -13987,12 +14020,16 @@ do_vfp_nsyn_add_sub (enum neon_shape rs)
{
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
{
@@ -14015,15 +14052,14 @@ try_vfp_nsyn (int args, void (*pfn) (enum neon_shape))
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:
@@ -14045,12 +14081,16 @@ do_vfp_nsyn_mla_mls (enum neon_shape rs)
{
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
{
@@ -14066,12 +14106,16 @@ do_vfp_nsyn_fma_fms (enum neon_shape rs)
{
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
{
@@ -14085,8 +14129,14 @@ do_vfp_nsyn_fma_fms (enum neon_shape rs)
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");
}
@@ -14095,14 +14145,18 @@ static void
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
{
@@ -14139,11 +14193,17 @@ do_vfp_nsyn_ldm_stm (int is_dbmode)
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)
- do_vfp_nsyn_opcode ("fsqrts");
+ if (rs == NS_FF || rs == NS_HH)
+ {
+ do_vfp_nsyn_opcode ("fsqrts");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
+ }
else
do_vfp_nsyn_opcode ("fsqrtd");
}
@@ -14151,12 +14211,18 @@ do_vfp_nsyn_sqrt (void)
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");
}
@@ -14164,14 +14230,18 @@ do_vfp_nsyn_div (void)
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
{
@@ -14179,17 +14249,19 @@ do_vfp_nsyn_nmul (void)
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 ();
@@ -14202,8 +14274,8 @@ do_vfp_nsyn_cmp (void)
}
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)
{
@@ -14217,7 +14289,7 @@ do_vfp_nsyn_cmp (void)
abort ();
}
- if (rs == NS_FI)
+ if (rs == NS_FI || rs == NS_HI)
{
NEON_ENCODE (SINGLE, inst);
do_vfp_sp_compare_z ();
@@ -14229,6 +14301,10 @@ do_vfp_nsyn_cmp (void)
}
}
do_vfp_cond_or_thumb ();
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HI || rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
}
static void
@@ -15194,6 +15270,13 @@ do_neon_shll (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) \
@@ -15306,6 +15389,13 @@ do_vfp_nsyn_cvt (enum neon_shape rs, enum neon_cvt_flavour flavour)
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
@@ -15339,6 +15429,11 @@ do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour flavour,
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)
@@ -15351,6 +15446,10 @@ do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour 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;
@@ -15359,6 +15458,10 @@ do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour flavour,
sz = 0;
op = 0;
break;
+ case neon_cvt_flavour_u32_f16:
+ sz = 0;
+ op = 0;
+ break;
default:
first_error (_("invalid instruction shape"));
return;
@@ -15377,6 +15480,11 @@ do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour flavour,
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;
@@ -15405,6 +15513,18 @@ do_neon_cvt_1 (enum neon_cvt_mode mode)
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)
{
@@ -15970,8 +16090,9 @@ static void
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;
@@ -16149,6 +16270,7 @@ do_neon_mov (void)
do_vfp_nsyn_opcode ("fcpys");
break;
+ case NS_HI:
case NS_FI: /* case 10 (fconsts). */
ldconst = "fconsts";
encode_fconstd:
@@ -16156,17 +16278,29 @@ do_neon_mov (void)
{
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
@@ -16223,6 +16357,21 @@ do_neon_rshift_round_imm (void)
}
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)
{
struct neon_type_el et = neon_check_type (2, NS_QD,
@@ -16398,6 +16547,10 @@ do_neon_ldr_str (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
{
@@ -16755,8 +16908,14 @@ do_vfp_nsyn_fpv8 (enum neon_shape rs)
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 ();
@@ -16792,7 +16951,7 @@ do_vmaxnm (void)
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)
@@ -16804,7 +16963,8 @@ do_vrint_1 (enum neon_cvt_mode mode)
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. */
@@ -16813,7 +16973,7 @@ do_vrint_1 (enum neon_cvt_mode mode)
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 ();
@@ -16832,6 +16992,10 @@ do_vrint_1 (enum neon_cvt_mode mode)
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
{
@@ -20036,6 +20200,15 @@ static const struct asm_opcode insns[] =
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
@@ -23156,7 +23329,20 @@ md_apply_fix (fixS * fixP,
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:
@@ -23173,6 +23359,17 @@ md_apply_fix (fixS * fixP,
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