target/arm: Use gvec for NEON VDUP

Also introduces neon_element_offset to find the env offset
of a specific element within a neon register.

Backports commit 32f91fb71f4c32113ec8c2af5f74f14abe6c7162 from qemu
This commit is contained in:
Richard Henderson 2018-11-10 09:51:38 -05:00 committed by Lioncash
parent 3d5f040608
commit c1251a19e1
No known key found for this signature in database
GPG key ID: 4E3C3CC1031BA9C7

View file

@ -1655,6 +1655,25 @@ neon_reg_offset (int reg, int n)
return vfp_reg_offset(0, sreg);
}
/* Return the offset of a 2**SIZE piece of a NEON register, at index ELE,
* where 0 is the least significant end of the register.
*/
static inline long
neon_element_offset(int reg, int element, TCGMemOp size)
{
int element_size = 1 << size;
int ofs = element * element_size;
#ifdef HOST_WORDS_BIGENDIAN
/* Calculate the offset assuming fully little-endian,
* then XOR to account for the order of the 8-byte units.
*/
if (element_size < 8) {
ofs ^= 8 - element_size;
}
#endif
return neon_reg_offset(reg, 0) + ofs;
}
static TCGv_i32 neon_load_reg(DisasContext *s, int reg, int pass)
{
TCGContext *tcg_ctx = s->uc->tcg_ctx;
@ -3552,17 +3571,10 @@ static int disas_vfp_insn(DisasContext *s, uint32_t insn)
tmp = load_reg(s, rd);
if (insn & (1 << 23)) {
/* VDUP */
if (size == 0) {
gen_neon_dup_u8(s, tmp, 0);
} else if (size == 1) {
gen_neon_dup_low16(s, tmp);
}
for (n = 0; n <= pass * 2; n++) {
tmp2 = tcg_temp_new_i32(tcg_ctx);
tcg_gen_mov_i32(tcg_ctx, tmp2, tmp);
neon_store_reg(s, rn, n, tmp2);
}
neon_store_reg(s, rn, n, tmp);
int vec_size = pass ? 16 : 8;
tcg_gen_gvec_dup_i32(tcg_ctx, size, neon_reg_offset(rn, 0),
vec_size, vec_size, tmp);
tcg_temp_free_i32(tcg_ctx, tmp);
} else {
/* VMOV */
switch (size) {
@ -7921,28 +7933,25 @@ static int disas_neon_data_insn(DisasContext *s, uint32_t insn)
tcg_temp_free_i32(tcg_ctx, tmp);
} else if ((insn & 0x380) == 0) {
/* VDUP */
int element;
TCGMemOp size;
if ((insn & (7 << 16)) == 0 || (q && (rd & 1))) {
return 1;
}
if (insn & (1 << 19)) {
tmp = neon_load_reg(s, rm, 1);
} else {
tmp = neon_load_reg(s, rm, 0);
}
if (insn & (1 << 16)) {
gen_neon_dup_u8(s, tmp, ((insn >> 17) & 3) * 8);
size = MO_8;
element = (insn >> 17) & 7;
} else if (insn & (1 << 17)) {
if ((insn >> 18) & 1)
gen_neon_dup_high16(s, tmp);
else
gen_neon_dup_low16(s, tmp);
size = MO_16;
element = (insn >> 18) & 3;
} else {
size = MO_32;
element = (insn >> 19) & 1;
}
for (pass = 0; pass < (q ? 4 : 2); pass++) {
tmp2 = tcg_temp_new_i32(tcg_ctx);
tcg_gen_mov_i32(tcg_ctx, tmp2, tmp);
neon_store_reg(s, rd, pass, tmp2);
}
tcg_temp_free_i32(tcg_ctx, tmp);
tcg_gen_gvec_dup_mem(tcg_ctx, size, neon_reg_offset(rd, 0),
neon_element_offset(rm, element, size),
q ? 16 : 8, q ? 16 : 8);
} else {
return 1;
}