aboutsummaryrefslogtreecommitdiff
path: root/target/arm/translate.c
diff options
context:
space:
mode:
Diffstat (limited to 'target/arm/translate.c')
-rw-r--r--target/arm/translate.c271
1 files changed, 249 insertions, 22 deletions
diff --git a/target/arm/translate.c b/target/arm/translate.c
index aa6dcaa577..ba6ab7d287 100644
--- a/target/arm/translate.c
+++ b/target/arm/translate.c
@@ -25,6 +25,7 @@
#include "disas/disas.h"
#include "exec/exec-all.h"
#include "tcg-op.h"
+#include "tcg-op-gvec.h"
#include "qemu/log.h"
#include "qemu/bitops.h"
#include "arm_ldst.h"
@@ -75,6 +76,10 @@ static const char *regnames[] =
{ "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
"r8", "r9", "r10", "r11", "r12", "r13", "r14", "pc" };
+/* Function prototypes for gen_ functions calling Neon helpers. */
+typedef void NeonGenThreeOpEnvFn(TCGv_i32, TCGv_env, TCGv_i32,
+ TCGv_i32, TCGv_i32);
+
/* initialize TCG globals. */
void arm_translate_init(void)
{
@@ -5374,9 +5379,9 @@ static void gen_neon_narrow_op(int op, int u, int size,
#define NEON_3R_VPMAX 20
#define NEON_3R_VPMIN 21
#define NEON_3R_VQDMULH_VQRDMULH 22
-#define NEON_3R_VPADD 23
+#define NEON_3R_VPADD_VQRDMLAH 23
#define NEON_3R_SHA 24 /* SHA1C,SHA1P,SHA1M,SHA1SU0,SHA256H{2},SHA256SU1 */
-#define NEON_3R_VFM 25 /* VFMA, VFMS : float fused multiply-add */
+#define NEON_3R_VFM_VQRDMLSH 25 /* VFMA, VFMS, VQRDMLSH */
#define NEON_3R_FLOAT_ARITH 26 /* float VADD, VSUB, VPADD, VABD */
#define NEON_3R_FLOAT_MULTIPLY 27 /* float VMLA, VMLS, VMUL */
#define NEON_3R_FLOAT_CMP 28 /* float VCEQ, VCGE, VCGT */
@@ -5408,9 +5413,9 @@ static const uint8_t neon_3r_sizes[] = {
[NEON_3R_VPMAX] = 0x7,
[NEON_3R_VPMIN] = 0x7,
[NEON_3R_VQDMULH_VQRDMULH] = 0x6,
- [NEON_3R_VPADD] = 0x7,
+ [NEON_3R_VPADD_VQRDMLAH] = 0x7,
[NEON_3R_SHA] = 0xf, /* size field encodes op type */
- [NEON_3R_VFM] = 0x5, /* size bit 1 encodes op */
+ [NEON_3R_VFM_VQRDMLSH] = 0x7, /* For VFM, size bit 1 encodes op */
[NEON_3R_FLOAT_ARITH] = 0x5, /* size bit 1 encodes op */
[NEON_3R_FLOAT_MULTIPLY] = 0x5, /* size bit 1 encodes op */
[NEON_3R_FLOAT_CMP] = 0x5, /* size bit 1 encodes op */
@@ -5589,6 +5594,22 @@ static const uint8_t neon_2rm_sizes[] = {
[NEON_2RM_VCVT_UF] = 0x4,
};
+
+/* Expand v8.1 simd helper. */
+static int do_v81_helper(DisasContext *s, gen_helper_gvec_3_ptr *fn,
+ int q, int rd, int rn, int rm)
+{
+ if (arm_dc_feature(s, ARM_FEATURE_V8_RDM)) {
+ int opr_sz = (1 + q) * 8;
+ tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd),
+ vfp_reg_offset(1, rn),
+ vfp_reg_offset(1, rm), cpu_env,
+ opr_sz, opr_sz, 0, fn);
+ return 0;
+ }
+ return 1;
+}
+
/* Translate a NEON data processing instruction. Return nonzero if the
instruction is invalid.
We process data in a mixture of 32-bit and 64-bit chunks.
@@ -5641,12 +5662,13 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
if (q && ((rd | rn | rm) & 1)) {
return 1;
}
- /*
- * The SHA-1/SHA-256 3-register instructions require special treatment
- * here, as their size field is overloaded as an op type selector, and
- * they all consume their input in a single pass.
- */
- if (op == NEON_3R_SHA) {
+ switch (op) {
+ case NEON_3R_SHA:
+ /* The SHA-1/SHA-256 3-register instructions require special
+ * treatment here, as their size field is overloaded as an
+ * op type selector, and they all consume their input in a
+ * single pass.
+ */
if (!q) {
return 1;
}
@@ -5683,6 +5705,40 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
tcg_temp_free_ptr(ptr2);
tcg_temp_free_ptr(ptr3);
return 0;
+
+ case NEON_3R_VPADD_VQRDMLAH:
+ if (!u) {
+ break; /* VPADD */
+ }
+ /* VQRDMLAH */
+ switch (size) {
+ case 1:
+ return do_v81_helper(s, gen_helper_gvec_qrdmlah_s16,
+ q, rd, rn, rm);
+ case 2:
+ return do_v81_helper(s, gen_helper_gvec_qrdmlah_s32,
+ q, rd, rn, rm);
+ }
+ return 1;
+
+ case NEON_3R_VFM_VQRDMLSH:
+ if (!u) {
+ /* VFM, VFMS */
+ if (size == 1) {
+ return 1;
+ }
+ break;
+ }
+ /* VQRDMLSH */
+ switch (size) {
+ case 1:
+ return do_v81_helper(s, gen_helper_gvec_qrdmlsh_s16,
+ q, rd, rn, rm);
+ case 2:
+ return do_v81_helper(s, gen_helper_gvec_qrdmlsh_s32,
+ q, rd, rn, rm);
+ }
+ return 1;
}
if (size == 3 && op != NEON_3R_LOGIC) {
/* 64-bit element instructions. */
@@ -5768,11 +5824,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
rm = rtmp;
}
break;
- case NEON_3R_VPADD:
- if (u) {
- return 1;
- }
- /* Fall through */
+ case NEON_3R_VPADD_VQRDMLAH:
case NEON_3R_VPMAX:
case NEON_3R_VPMIN:
pairwise = 1;
@@ -5806,8 +5858,8 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
return 1;
}
break;
- case NEON_3R_VFM:
- if (!arm_dc_feature(s, ARM_FEATURE_VFP4) || u) {
+ case NEON_3R_VFM_VQRDMLSH:
+ if (!arm_dc_feature(s, ARM_FEATURE_VFP4)) {
return 1;
}
break;
@@ -6004,7 +6056,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
}
}
break;
- case NEON_3R_VPADD:
+ case NEON_3R_VPADD_VQRDMLAH:
switch (size) {
case 0: gen_helper_neon_padd_u8(tmp, tmp, tmp2); break;
case 1: gen_helper_neon_padd_u16(tmp, tmp, tmp2); break;
@@ -6103,7 +6155,7 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
}
}
break;
- case NEON_3R_VFM:
+ case NEON_3R_VFM_VQRDMLSH:
{
/* VFMA, VFMS: fused multiply-add */
TCGv_ptr fpstatus = get_fpstatus_ptr(1);
@@ -6937,11 +6989,45 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
}
neon_store_reg64(cpu_V0, rd + pass);
}
+ break;
+ case 14: /* VQRDMLAH scalar */
+ case 15: /* VQRDMLSH scalar */
+ {
+ NeonGenThreeOpEnvFn *fn;
+ if (!arm_dc_feature(s, ARM_FEATURE_V8_RDM)) {
+ return 1;
+ }
+ if (u && ((rd | rn) & 1)) {
+ return 1;
+ }
+ if (op == 14) {
+ if (size == 1) {
+ fn = gen_helper_neon_qrdmlah_s16;
+ } else {
+ fn = gen_helper_neon_qrdmlah_s32;
+ }
+ } else {
+ if (size == 1) {
+ fn = gen_helper_neon_qrdmlsh_s16;
+ } else {
+ fn = gen_helper_neon_qrdmlsh_s32;
+ }
+ }
+ tmp2 = neon_get_scalar(size, rm);
+ for (pass = 0; pass < (u ? 4 : 2); pass++) {
+ tmp = neon_load_reg(rn, pass);
+ tmp3 = neon_load_reg(rd, pass);
+ fn(tmp, cpu_env, tmp, tmp2, tmp3);
+ tcg_temp_free_i32(tmp3);
+ neon_store_reg(rd, pass, tmp);
+ }
+ tcg_temp_free_i32(tmp2);
+ }
break;
- default: /* 14 and 15 are RESERVED */
- return 1;
+ default:
+ g_assert_not_reached();
}
}
} else { /* size == 3 */
@@ -7594,6 +7680,123 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
return 0;
}
+/* Advanced SIMD three registers of the same length extension.
+ * 31 25 23 22 20 16 12 11 10 9 8 3 0
+ * +---------------+-----+---+-----+----+----+---+----+---+----+---------+----+
+ * | 1 1 1 1 1 1 0 | op1 | D | op2 | Vn | Vd | 1 | o3 | 0 | o4 | N Q M U | Vm |
+ * +---------------+-----+---+-----+----+----+---+----+---+----+---------+----+
+ */
+static int disas_neon_insn_3same_ext(DisasContext *s, uint32_t insn)
+{
+ gen_helper_gvec_3_ptr *fn_gvec_ptr;
+ int rd, rn, rm, rot, size, opr_sz;
+ TCGv_ptr fpst;
+ bool q;
+
+ q = extract32(insn, 6, 1);
+ VFP_DREG_D(rd, insn);
+ VFP_DREG_N(rn, insn);
+ VFP_DREG_M(rm, insn);
+ if ((rd | rn | rm) & q) {
+ return 1;
+ }
+
+ if ((insn & 0xfe200f10) == 0xfc200800) {
+ /* VCMLA -- 1111 110R R.1S .... .... 1000 ...0 .... */
+ size = extract32(insn, 20, 1);
+ rot = extract32(insn, 23, 2);
+ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)
+ || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) {
+ return 1;
+ }
+ fn_gvec_ptr = size ? gen_helper_gvec_fcmlas : gen_helper_gvec_fcmlah;
+ } else if ((insn & 0xfea00f10) == 0xfc800800) {
+ /* VCADD -- 1111 110R 1.0S .... .... 1000 ...0 .... */
+ size = extract32(insn, 20, 1);
+ rot = extract32(insn, 24, 1);
+ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)
+ || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) {
+ return 1;
+ }
+ fn_gvec_ptr = size ? gen_helper_gvec_fcadds : gen_helper_gvec_fcaddh;
+ } else {
+ return 1;
+ }
+
+ if (s->fp_excp_el) {
+ gen_exception_insn(s, 4, EXCP_UDEF,
+ syn_fp_access_trap(1, 0xe, false), s->fp_excp_el);
+ return 0;
+ }
+ if (!s->vfp_enabled) {
+ return 1;
+ }
+
+ opr_sz = (1 + q) * 8;
+ fpst = get_fpstatus_ptr(1);
+ tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd),
+ vfp_reg_offset(1, rn),
+ vfp_reg_offset(1, rm), fpst,
+ opr_sz, opr_sz, rot, fn_gvec_ptr);
+ tcg_temp_free_ptr(fpst);
+ return 0;
+}
+
+/* Advanced SIMD two registers and a scalar extension.
+ * 31 24 23 22 20 16 12 11 10 9 8 3 0
+ * +-----------------+----+---+----+----+----+---+----+---+----+---------+----+
+ * | 1 1 1 1 1 1 1 0 | o1 | D | o2 | Vn | Vd | 1 | o3 | 0 | o4 | N Q M U | Vm |
+ * +-----------------+----+---+----+----+----+---+----+---+----+---------+----+
+ *
+ */
+
+static int disas_neon_insn_2reg_scalar_ext(DisasContext *s, uint32_t insn)
+{
+ int rd, rn, rm, rot, size, opr_sz;
+ TCGv_ptr fpst;
+ bool q;
+
+ q = extract32(insn, 6, 1);
+ VFP_DREG_D(rd, insn);
+ VFP_DREG_N(rn, insn);
+ VFP_DREG_M(rm, insn);
+ if ((rd | rn) & q) {
+ return 1;
+ }
+
+ if ((insn & 0xff000f10) == 0xfe000800) {
+ /* VCMLA (indexed) -- 1111 1110 S.RR .... .... 1000 ...0 .... */
+ rot = extract32(insn, 20, 2);
+ size = extract32(insn, 23, 1);
+ if (!arm_dc_feature(s, ARM_FEATURE_V8_FCMA)
+ || (!size && !arm_dc_feature(s, ARM_FEATURE_V8_FP16))) {
+ return 1;
+ }
+ } else {
+ return 1;
+ }
+
+ if (s->fp_excp_el) {
+ gen_exception_insn(s, 4, EXCP_UDEF,
+ syn_fp_access_trap(1, 0xe, false), s->fp_excp_el);
+ return 0;
+ }
+ if (!s->vfp_enabled) {
+ return 1;
+ }
+
+ opr_sz = (1 + q) * 8;
+ fpst = get_fpstatus_ptr(1);
+ tcg_gen_gvec_3_ptr(vfp_reg_offset(1, rd),
+ vfp_reg_offset(1, rn),
+ vfp_reg_offset(1, rm), fpst,
+ opr_sz, opr_sz, rot,
+ size ? gen_helper_gvec_fcmlas_idx
+ : gen_helper_gvec_fcmlah_idx);
+ tcg_temp_free_ptr(fpst);
+ return 0;
+}
+
static int disas_coproc_insn(DisasContext *s, uint32_t insn)
{
int cpnum, is64, crn, crm, opc1, opc2, isread, rt, rt2;
@@ -8338,6 +8541,18 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
}
}
}
+ } else if ((insn & 0x0e000a00) == 0x0c000800
+ && arm_dc_feature(s, ARM_FEATURE_V8)) {
+ if (disas_neon_insn_3same_ext(s, insn)) {
+ goto illegal_op;
+ }
+ return;
+ } else if ((insn & 0x0f000a00) == 0x0e000800
+ && arm_dc_feature(s, ARM_FEATURE_V8)) {
+ if (disas_neon_insn_2reg_scalar_ext(s, insn)) {
+ goto illegal_op;
+ }
+ return;
} else if ((insn & 0x0fe00000) == 0x0c400000) {
/* Coprocessor double register transfer. */
ARCH(5TE);
@@ -10559,7 +10774,19 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
default_exception_el(s));
break;
}
- if (((insn >> 24) & 3) == 3) {
+ if ((insn & 0xfe000a00) == 0xfc000800
+ && arm_dc_feature(s, ARM_FEATURE_V8)) {
+ /* The Thumb2 and ARM encodings are identical. */
+ if (disas_neon_insn_3same_ext(s, insn)) {
+ goto illegal_op;
+ }
+ } else if ((insn & 0xff000a00) == 0xfe000800
+ && arm_dc_feature(s, ARM_FEATURE_V8)) {
+ /* The Thumb2 and ARM encodings are identical. */
+ if (disas_neon_insn_2reg_scalar_ext(s, insn)) {
+ goto illegal_op;
+ }
+ } else if (((insn >> 24) & 3) == 3) {
/* Translate into the equivalent ARM encoding. */
insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4) | (1 << 28);
if (disas_neon_data_insn(s, insn)) {