diff options
Diffstat (limited to 'tcg/arm/tcg-target.c')
-rw-r--r-- | tcg/arm/tcg-target.c | 37 |
1 files changed, 32 insertions, 5 deletions
diff --git a/tcg/arm/tcg-target.c b/tcg/arm/tcg-target.c index 6b55762b65..525994c9c1 100644 --- a/tcg/arm/tcg-target.c +++ b/tcg/arm/tcg-target.c @@ -179,11 +179,36 @@ static int target_parse_constraint(TCGArgConstraint *ct, const char **pct_str) return 0; } +static inline uint32_t rotl(uint32_t val, int n) +{ + return (val << n) | (val >> (32 - n)); +} + +/* ARM immediates for ALU instructions are made of an unsigned 8-bit + right-rotated by an even amount between 0 and 30. */ +static inline int encode_imm(uint32_t imm) +{ + /* simple case, only lower bits */ + if ((imm & ~0xff) == 0) + return 0; + /* then try a simple even shift */ + shift = ctz32(imm) & ~1; + if (((imm >> shift) & ~0xff) == 0) + return 32 - shift; + /* now try harder with rotations */ + if ((rotl(imm, 2) & ~0xff) == 0) + return 2; + if ((rotl(imm, 4) & ~0xff) == 0) + return 4; + if ((rotl(imm, 6) & ~0xff) == 0) + return 6; + /* imm can't be encoded */ + return -1; +} static inline int check_fit_imm(uint32_t imm) { - /* XXX: use rotation */ - return (imm & ~0xff) == 0; + return encode_imm(imm) >= 0; } /* Test if a constant matches the constraint. @@ -1407,10 +1432,12 @@ static inline void tcg_out_op(TCGContext *s, int opc, c = ARITH_EOR; /* Fall through. */ gen_arith: - if (const_args[2]) + if (const_args[2]) { + int rot; + rot = encode_imm(args[2]); tcg_out_dat_imm(s, COND_AL, c, - args[0], args[1], args[2]); - else + args[0], args[1], rotl(args[2], rot) | (rot << 7)); + } else tcg_out_dat_reg(s, COND_AL, c, args[0], args[1], args[2], SHIFT_IMM_LSL(0)); break; |