target/arm: Convert Neon 'load/store single structure' to decodetree
Convert the Neon "load/store single structure to one lane" insns to decodetree. As this is the last set of insns in the neon load/store group, we can remove the whole disas_neon_ls_insn() function. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-id: 20200430181003.21682-14-peter.maydell@linaro.org
This commit is contained in:
parent
3698747c48
commit
123ce4e3da
@ -39,3 +39,14 @@ VLDST_multiple 1111 0100 0 . l:1 0 rn:4 .... itype:4 size:2 align:2 rm:4 \
|
||||
|
||||
VLD_all_lanes 1111 0100 1 . 1 0 rn:4 .... 11 n:2 size:2 t:1 a:1 rm:4 \
|
||||
vd=%vd_dp
|
||||
|
||||
# Neon load/store single structure to one lane
|
||||
%imm1_5_p1 5:1 !function=plus1
|
||||
%imm1_6_p1 6:1 !function=plus1
|
||||
|
||||
VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 00 n:2 reg_idx:3 align:1 rm:4 \
|
||||
vd=%vd_dp size=0 stride=1
|
||||
VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 01 n:2 reg_idx:2 align:2 rm:4 \
|
||||
vd=%vd_dp size=1 stride=%imm1_5_p1
|
||||
VLDST_single 1111 0100 1 . l:1 0 rn:4 .... 10 n:2 reg_idx:1 align:3 rm:4 \
|
||||
vd=%vd_dp size=2 stride=%imm1_6_p1
|
||||
|
@ -26,6 +26,11 @@
|
||||
* It might be possible to convert it to a standalone .c file eventually.
|
||||
*/
|
||||
|
||||
static inline int plus1(DisasContext *s, int x)
|
||||
{
|
||||
return x + 1;
|
||||
}
|
||||
|
||||
/* Include the generated Neon decoder */
|
||||
#include "decode-neon-dp.inc.c"
|
||||
#include "decode-neon-ls.inc.c"
|
||||
@ -471,3 +476,87 @@ static bool trans_VLD_all_lanes(DisasContext *s, arg_VLD_all_lanes *a)
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool trans_VLDST_single(DisasContext *s, arg_VLDST_single *a)
|
||||
{
|
||||
/* Neon load/store single structure to one lane */
|
||||
int reg;
|
||||
int nregs = a->n + 1;
|
||||
int vd = a->vd;
|
||||
TCGv_i32 addr, tmp;
|
||||
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* UNDEF accesses to D16-D31 if they don't exist */
|
||||
if (!dc_isar_feature(aa32_simd_r32, s) && (a->vd & 0x10)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Catch the UNDEF cases. This is unavoidably a bit messy. */
|
||||
switch (nregs) {
|
||||
case 1:
|
||||
if (((a->align & (1 << a->size)) != 0) ||
|
||||
(a->size == 2 && ((a->align & 3) == 1 || (a->align & 3) == 2))) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if ((a->align & 1) != 0) {
|
||||
return false;
|
||||
}
|
||||
/* fall through */
|
||||
case 2:
|
||||
if (a->size == 2 && (a->align & 2) != 0) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if ((a->size == 2) && ((a->align & 3) == 3)) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if ((vd + a->stride * (nregs - 1)) > 31) {
|
||||
/*
|
||||
* Attempts to write off the end of the register file are
|
||||
* UNPREDICTABLE; we choose to UNDEF because otherwise we would
|
||||
* access off the end of the array that holds the register data.
|
||||
*/
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vfp_access_check(s)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
tmp = tcg_temp_new_i32();
|
||||
addr = tcg_temp_new_i32();
|
||||
load_reg_var(s, addr, a->rn);
|
||||
/*
|
||||
* TODO: if we implemented alignment exceptions, we should check
|
||||
* addr against the alignment encoded in a->align here.
|
||||
*/
|
||||
for (reg = 0; reg < nregs; reg++) {
|
||||
if (a->l) {
|
||||
gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s),
|
||||
s->be_data | a->size);
|
||||
neon_store_element(vd, a->reg_idx, a->size, tmp);
|
||||
} else { /* Store */
|
||||
neon_load_element(tmp, vd, a->reg_idx, a->size);
|
||||
gen_aa32_st_i32(s, tmp, addr, get_mem_index(s),
|
||||
s->be_data | a->size);
|
||||
}
|
||||
vd += a->stride;
|
||||
tcg_gen_addi_i32(addr, addr, 1 << a->size);
|
||||
}
|
||||
tcg_temp_free_i32(addr);
|
||||
tcg_temp_free_i32(tmp);
|
||||
|
||||
gen_neon_ldst_base_update(s, a->rm, a->rn, (1 << a->size) * nregs);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -3213,140 +3213,6 @@ static void gen_neon_trn_u16(TCGv_i32 t0, TCGv_i32 t1)
|
||||
tcg_temp_free_i32(rd);
|
||||
}
|
||||
|
||||
|
||||
/* Translate a NEON load/store element instruction. Return nonzero if the
|
||||
instruction is invalid. */
|
||||
static int disas_neon_ls_insn(DisasContext *s, uint32_t insn)
|
||||
{
|
||||
int rd, rn, rm;
|
||||
int nregs;
|
||||
int stride;
|
||||
int size;
|
||||
int reg;
|
||||
int load;
|
||||
TCGv_i32 addr;
|
||||
TCGv_i32 tmp;
|
||||
|
||||
if (!arm_dc_feature(s, ARM_FEATURE_NEON)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FIXME: this access check should not take precedence over UNDEF
|
||||
* for invalid encodings; we will generate incorrect syndrome information
|
||||
* for attempts to execute invalid vfp/neon encodings with FP disabled.
|
||||
*/
|
||||
if (s->fp_excp_el) {
|
||||
gen_exception_insn(s, s->pc_curr, EXCP_UDEF,
|
||||
syn_simd_access_trap(1, 0xe, false), s->fp_excp_el);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!s->vfp_enabled)
|
||||
return 1;
|
||||
VFP_DREG_D(rd, insn);
|
||||
rn = (insn >> 16) & 0xf;
|
||||
rm = insn & 0xf;
|
||||
load = (insn & (1 << 21)) != 0;
|
||||
if ((insn & (1 << 23)) == 0) {
|
||||
/* Load store all elements -- handled already by decodetree */
|
||||
return 1;
|
||||
} else {
|
||||
size = (insn >> 10) & 3;
|
||||
if (size == 3) {
|
||||
/* Load single element to all lanes -- handled by decodetree */
|
||||
return 1;
|
||||
} else {
|
||||
/* Single element. */
|
||||
int idx = (insn >> 4) & 0xf;
|
||||
int reg_idx;
|
||||
switch (size) {
|
||||
case 0:
|
||||
reg_idx = (insn >> 5) & 7;
|
||||
stride = 1;
|
||||
break;
|
||||
case 1:
|
||||
reg_idx = (insn >> 6) & 3;
|
||||
stride = (insn & (1 << 5)) ? 2 : 1;
|
||||
break;
|
||||
case 2:
|
||||
reg_idx = (insn >> 7) & 1;
|
||||
stride = (insn & (1 << 6)) ? 2 : 1;
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
nregs = ((insn >> 8) & 3) + 1;
|
||||
/* Catch the UNDEF cases. This is unavoidably a bit messy. */
|
||||
switch (nregs) {
|
||||
case 1:
|
||||
if (((idx & (1 << size)) != 0) ||
|
||||
(size == 2 && ((idx & 3) == 1 || (idx & 3) == 2))) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
if ((idx & 1) != 0) {
|
||||
return 1;
|
||||
}
|
||||
/* fall through */
|
||||
case 2:
|
||||
if (size == 2 && (idx & 2) != 0) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if ((size == 2) && ((idx & 3) == 3)) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
if ((rd + stride * (nregs - 1)) > 31) {
|
||||
/* Attempts to write off the end of the register file
|
||||
* are UNPREDICTABLE; we choose to UNDEF because otherwise
|
||||
* the neon_load_reg() would write off the end of the array.
|
||||
*/
|
||||
return 1;
|
||||
}
|
||||
tmp = tcg_temp_new_i32();
|
||||
addr = tcg_temp_new_i32();
|
||||
load_reg_var(s, addr, rn);
|
||||
for (reg = 0; reg < nregs; reg++) {
|
||||
if (load) {
|
||||
gen_aa32_ld_i32(s, tmp, addr, get_mem_index(s),
|
||||
s->be_data | size);
|
||||
neon_store_element(rd, reg_idx, size, tmp);
|
||||
} else { /* Store */
|
||||
neon_load_element(tmp, rd, reg_idx, size);
|
||||
gen_aa32_st_i32(s, tmp, addr, get_mem_index(s),
|
||||
s->be_data | size);
|
||||
}
|
||||
rd += stride;
|
||||
tcg_gen_addi_i32(addr, addr, 1 << size);
|
||||
}
|
||||
tcg_temp_free_i32(addr);
|
||||
tcg_temp_free_i32(tmp);
|
||||
stride = nregs * (1 << size);
|
||||
}
|
||||
}
|
||||
if (rm != 15) {
|
||||
TCGv_i32 base;
|
||||
|
||||
base = load_reg(s, rn);
|
||||
if (rm == 13) {
|
||||
tcg_gen_addi_i32(base, base, stride);
|
||||
} else {
|
||||
TCGv_i32 index;
|
||||
index = load_reg(s, rm);
|
||||
tcg_gen_add_i32(base, base, index);
|
||||
tcg_temp_free_i32(index);
|
||||
}
|
||||
store_reg(s, rn, base);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void gen_neon_narrow(int size, TCGv_i32 dest, TCGv_i64 src)
|
||||
{
|
||||
switch (size) {
|
||||
@ -10596,13 +10462,6 @@ static void disas_arm_insn(DisasContext *s, unsigned int insn)
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((insn & 0x0f100000) == 0x04000000) {
|
||||
/* NEON load/store. */
|
||||
if (disas_neon_ls_insn(s, insn)) {
|
||||
goto illegal_op;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ((insn & 0x0e000f00) == 0x0c000100) {
|
||||
if (arm_dc_feature(s, ARM_FEATURE_IWMMXT)) {
|
||||
/* iWMMXt register transfer. */
|
||||
@ -10807,12 +10666,6 @@ static void disas_thumb2_insn(DisasContext *s, uint32_t insn)
|
||||
}
|
||||
break;
|
||||
case 12:
|
||||
if ((insn & 0x01100000) == 0x01000000) {
|
||||
if (disas_neon_ls_insn(s, insn)) {
|
||||
goto illegal_op;
|
||||
}
|
||||
break;
|
||||
}
|
||||
goto illegal_op;
|
||||
default:
|
||||
illegal_op:
|
||||
|
Loading…
Reference in New Issue
Block a user