tcg/arm: Simplify usage of encode_imm

We have already computed the rotated value of the imm8
portion of the complete imm12 encoding.  No sense leaving
the combination of rot + rotation to the caller.

Create an encode_imm12_nofail helper that performs an assert.

This removes the final use of the local "rotl" function,
which duplicated our generic "rol32" function.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
This commit is contained in:
Richard Henderson 2021-08-09 12:27:08 -10:00
parent 31d160adc9
commit 90606715dc
1 changed files with 77 additions and 64 deletions

View File

@ -312,10 +312,10 @@ static bool reloc_pc8(tcg_insn_unit *src_rw, const tcg_insn_unit *target)
{ {
const tcg_insn_unit *src_rx = tcg_splitwx_to_rx(src_rw); const tcg_insn_unit *src_rx = tcg_splitwx_to_rx(src_rw);
ptrdiff_t offset = tcg_ptr_byte_diff(target, src_rx) - 8; ptrdiff_t offset = tcg_ptr_byte_diff(target, src_rx) - 8;
int rot = encode_imm(offset); int imm12 = encode_imm(offset);
if (rot >= 0) { if (imm12 >= 0) {
*src_rw = deposit32(*src_rw, 0, 12, rol32(offset, rot) | (rot << 7)); *src_rw = deposit32(*src_rw, 0, 12, imm12);
return true; return true;
} }
return false; return false;
@ -369,33 +369,52 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type,
(ALL_GENERAL_REGS & ~((1 << TCG_REG_R0) | (1 << TCG_REG_R1))) (ALL_GENERAL_REGS & ~((1 << TCG_REG_R0) | (1 << TCG_REG_R1)))
#endif #endif
static inline uint32_t rotl(uint32_t val, int n) /*
{ * ARM immediates for ALU instructions are made of an unsigned 8-bit
return (val << n) | (val >> (32 - n)); * right-rotated by an even amount between 0 and 30.
} *
* Return < 0 if @imm cannot be encoded, else the entire imm12 field.
/* ARM immediates for ALU instructions are made of an unsigned 8-bit */
right-rotated by an even amount between 0 and 30. */
static int encode_imm(uint32_t imm) static int encode_imm(uint32_t imm)
{ {
int shift; uint32_t rot, imm8;
/* simple case, only lower bits */ /* Simple case, no rotation required. */
if ((imm & ~0xff) == 0) if ((imm & ~0xff) == 0) {
return 0; return imm;
/* then try a simple even shift */ }
shift = ctz32(imm) & ~1;
if (((imm >> shift) & ~0xff) == 0) /* Next, try a simple even shift. */
return 32 - shift; rot = ctz32(imm) & ~1;
/* now try harder with rotations */ imm8 = imm >> rot;
if ((rotl(imm, 2) & ~0xff) == 0) rot = 32 - rot;
return 2; if ((imm8 & ~0xff) == 0) {
if ((rotl(imm, 4) & ~0xff) == 0) goto found;
return 4; }
if ((rotl(imm, 6) & ~0xff) == 0)
return 6; /*
/* imm can't be encoded */ * Finally, try harder with rotations.
* The ctz test above will have taken care of rotates >= 8.
*/
for (rot = 2; rot < 8; rot += 2) {
imm8 = rol32(imm, rot);
if ((imm8 & ~0xff) == 0) {
goto found;
}
}
/* Fail: imm cannot be encoded. */
return -1; return -1;
found:
/* Note that rot is even, and we discard bit 0 by shifting by 7. */
return rot << 7 | imm8;
}
static int encode_imm_nofail(uint32_t imm)
{
int ret = encode_imm(imm);
tcg_debug_assert(ret >= 0);
return ret;
} }
static inline int check_fit_imm(uint32_t imm) static inline int check_fit_imm(uint32_t imm)
@ -782,20 +801,18 @@ static void tcg_out_movi_pool(TCGContext *s, int cond, int rd, uint32_t arg)
static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg) static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg)
{ {
int rot, diff, opc, sh1, sh2; int imm12, diff, opc, sh1, sh2;
uint32_t tt0, tt1, tt2; uint32_t tt0, tt1, tt2;
/* Check a single MOV/MVN before anything else. */ /* Check a single MOV/MVN before anything else. */
rot = encode_imm(arg); imm12 = encode_imm(arg);
if (rot >= 0) { if (imm12 >= 0) {
tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, tcg_out_dat_imm(s, cond, ARITH_MOV, rd, 0, imm12);
rotl(arg, rot) | (rot << 7));
return; return;
} }
rot = encode_imm(~arg); imm12 = encode_imm(~arg);
if (rot >= 0) { if (imm12 >= 0) {
tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, tcg_out_dat_imm(s, cond, ARITH_MVN, rd, 0, imm12);
rotl(~arg, rot) | (rot << 7));
return; return;
} }
@ -803,17 +820,15 @@ static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg)
or within the TB, which is immediately before the code block. */ or within the TB, which is immediately before the code block. */
diff = tcg_pcrel_diff(s, (void *)arg) - 8; diff = tcg_pcrel_diff(s, (void *)arg) - 8;
if (diff >= 0) { if (diff >= 0) {
rot = encode_imm(diff); imm12 = encode_imm(diff);
if (rot >= 0) { if (imm12 >= 0) {
tcg_out_dat_imm(s, cond, ARITH_ADD, rd, TCG_REG_PC, tcg_out_dat_imm(s, cond, ARITH_ADD, rd, TCG_REG_PC, imm12);
rotl(diff, rot) | (rot << 7));
return; return;
} }
} else { } else {
rot = encode_imm(-diff); imm12 = encode_imm(-diff);
if (rot >= 0) { if (imm12 >= 0) {
tcg_out_dat_imm(s, cond, ARITH_SUB, rd, TCG_REG_PC, tcg_out_dat_imm(s, cond, ARITH_SUB, rd, TCG_REG_PC, imm12);
rotl(-diff, rot) | (rot << 7));
return; return;
} }
} }
@ -845,6 +860,8 @@ static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg)
sh2 = ctz32(tt1) & ~1; sh2 = ctz32(tt1) & ~1;
tt2 = tt1 & ~(0xff << sh2); tt2 = tt1 & ~(0xff << sh2);
if (tt2 == 0) { if (tt2 == 0) {
int rot;
rot = ((32 - sh1) << 7) & 0xf00; rot = ((32 - sh1) << 7) & 0xf00;
tcg_out_dat_imm(s, cond, opc, rd, 0, ((tt0 >> sh1) & 0xff) | rot); tcg_out_dat_imm(s, cond, opc, rd, 0, ((tt0 >> sh1) & 0xff) | rot);
rot = ((32 - sh2) << 7) & 0xf00; rot = ((32 - sh2) << 7) & 0xf00;
@ -857,37 +874,35 @@ static void tcg_out_movi32(TCGContext *s, int cond, int rd, uint32_t arg)
tcg_out_movi_pool(s, cond, rd, arg); tcg_out_movi_pool(s, cond, rd, arg);
} }
/*
* Emit either the reg,imm or reg,reg form of a data-processing insn.
* rhs must satisfy the "rI" constraint.
*/
static inline void tcg_out_dat_rI(TCGContext *s, int cond, int opc, TCGArg dst, static inline void tcg_out_dat_rI(TCGContext *s, int cond, int opc, TCGArg dst,
TCGArg lhs, TCGArg rhs, int rhs_is_const) TCGArg lhs, TCGArg rhs, int rhs_is_const)
{ {
/* Emit either the reg,imm or reg,reg form of a data-processing insn.
* rhs must satisfy the "rI" constraint.
*/
if (rhs_is_const) { if (rhs_is_const) {
int rot = encode_imm(rhs); tcg_out_dat_imm(s, cond, opc, dst, lhs, encode_imm_nofail(rhs));
tcg_debug_assert(rot >= 0);
tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7));
} else { } else {
tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0));
} }
} }
/*
* Emit either the reg,imm or reg,reg form of a data-processing insn.
* rhs must satisfy the "rIK" constraint.
*/
static void tcg_out_dat_rIK(TCGContext *s, int cond, int opc, int opinv, static void tcg_out_dat_rIK(TCGContext *s, int cond, int opc, int opinv,
TCGReg dst, TCGReg lhs, TCGArg rhs, TCGReg dst, TCGReg lhs, TCGArg rhs,
bool rhs_is_const) bool rhs_is_const)
{ {
/* Emit either the reg,imm or reg,reg form of a data-processing insn.
* rhs must satisfy the "rIK" constraint.
*/
if (rhs_is_const) { if (rhs_is_const) {
int rot = encode_imm(rhs); int imm12 = encode_imm(rhs);
if (rot < 0) { if (imm12 < 0) {
rhs = ~rhs; imm12 = encode_imm_nofail(~rhs);
rot = encode_imm(rhs);
tcg_debug_assert(rot >= 0);
opc = opinv; opc = opinv;
} }
tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12);
} else { } else {
tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0));
} }
@ -901,14 +916,12 @@ static void tcg_out_dat_rIN(TCGContext *s, int cond, int opc, int opneg,
* rhs must satisfy the "rIN" constraint. * rhs must satisfy the "rIN" constraint.
*/ */
if (rhs_is_const) { if (rhs_is_const) {
int rot = encode_imm(rhs); int imm12 = encode_imm(rhs);
if (rot < 0) { if (imm12 < 0) {
rhs = -rhs; imm12 = encode_imm_nofail(-rhs);
rot = encode_imm(rhs);
tcg_debug_assert(rot >= 0);
opc = opneg; opc = opneg;
} }
tcg_out_dat_imm(s, cond, opc, dst, lhs, rotl(rhs, rot) | (rot << 7)); tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12);
} else { } else {
tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0));
} }