diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2026-05-28 17:02:52 +0200 |
|---|---|---|
| committer | Michael Tokarev <mjt@tls.msk.ru> | 2026-06-19 08:37:21 +0300 |
| commit | 77e8e0a3ae012f97349e1a6435d7d73ab25c402c (patch) | |
| tree | c8ca9f80f3e2ab1b55aca90c7f074312cd83de18 | |
| parent | 41d5a45d24ed3e18605d3f6569d9446dad3ebf65 (diff) | |
| download | qemu-77e8e0a3ae012f97349e1a6435d7d73ab25c402c.tar.gz qemu-77e8e0a3ae012f97349e1a6435d7d73ab25c402c.zip | |
target/i386: apply mod to immediate count of an RCL/RCR operation
RCR and RCL instructions with a count of 9 are the same as if the
count was 0, but they generated incorrect code because the can_be_zero
flag is false. This causes 0 to underflow into -1 at
tcg_gen_subi_tl(count, count, 1).
Fix by absorbing the modulo computation into gen_shift_count(), now
renamed gen_shift_count_1(), so that it can handle both reductions.
Resolves: https://gitlab.com/qemu-project/qemu/-/work_items/3452
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
(cherry picked from commit e38d0afade7c134cd4d675f54d26c394cc3cc31f)
Signed-off-by: Michael Tokarev <mjt@tls.msk.ru>
| -rw-r--r-- | target/i386/tcg/emit.c.inc | 64 |
1 files changed, 35 insertions, 29 deletions
diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index ce636b6c56..8eca66b696 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3244,8 +3244,9 @@ static void gen_PUSHF(DisasContext *s, X86DecodedInsn *decode) assume_cc_op(s, CC_OP_EFLAGS); } -static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, - bool *can_be_zero, TCGv *count, int unit) +static MemOp gen_shift_count_1(DisasContext *s, X86DecodedInsn *decode, + bool *can_be_zero, TCGv *count, int unit, + int mod) { MemOp ot = decode->op[0].ot; int mask = (ot <= MO_32 ? 0x1f : 0x3f); @@ -3255,16 +3256,31 @@ static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, case X86_OP_INT: *count = tcg_temp_new(); tcg_gen_andi_tl(*count, cpu_regs[R_ECX], mask); + + if (mod < mask) { + TCGv temp = tcg_temp_new(); + assert(mod * 4 >= mask); + if (mod * 2 < mask) { + tcg_gen_subi_tl(temp, *count, mod * 2); + tcg_gen_movcond_tl(TCG_COND_GE, *count, temp, tcg_constant_tl(0), temp, *count); + } + tcg_gen_subi_tl(temp, *count, mod); + tcg_gen_movcond_tl(TCG_COND_GE, *count, temp, tcg_constant_tl(0), temp, *count); + } *can_be_zero = true; break; case X86_OP_IMM: - if ((decode->immediate & mask) == 0) { + decode->immediate &= mask; + if (mod < mask) { + decode->immediate %= mod; + } + if (decode->immediate == 0) { *count = NULL; break; } *count = tcg_temp_new(); - tcg_gen_movi_tl(*count, decode->immediate & mask); + tcg_gen_movi_tl(*count, decode->immediate); break; case X86_OP_SKIP: @@ -3279,6 +3295,13 @@ static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, return ot; } +static MemOp gen_shift_count(DisasContext *s, X86DecodedInsn *decode, + bool *can_be_zero, TCGv *count, int unit) +{ + return gen_shift_count_1(s, decode, can_be_zero, count, unit, + INT_MAX); +} + /* * Compute existing flags in decode->cc_src, for gen_* functions that wants * to set the cc_op set to CC_OP_ADCOX. In particular, this allows rotate @@ -3397,29 +3420,14 @@ static void gen_rot_overflow(X86DecodedInsn *decode, TCGv result, TCGv old, /* * RCx operations are invariant modulo 8*operand_size+1. For 8 and 16-bit operands, * this is less than 0x1f (the mask applied by gen_shift_count) so reduce further. + * FIXME: are flags updated if the count is nonzero, but a multiple of (8 << op) + 1? */ -static void gen_rotc_mod(MemOp ot, TCGv count) +static MemOp gen_rotc_count(DisasContext *s, X86DecodedInsn *decode, + bool *can_be_zero, TCGv *count, int unit) { - TCGv temp; - - switch (ot) { - case MO_8: - temp = tcg_temp_new(); - tcg_gen_subi_tl(temp, count, 18); - tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); - tcg_gen_subi_tl(temp, count, 9); - tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); - break; - - case MO_16: - temp = tcg_temp_new(); - tcg_gen_subi_tl(temp, count, 17); - tcg_gen_movcond_tl(TCG_COND_GE, count, temp, tcg_constant_tl(0), temp, count); - break; - - default: - break; - } + MemOp ot = decode->op[0].ot; + return gen_shift_count_1(s, decode, can_be_zero, count, unit, + (8 << ot) + 1); } /* @@ -3440,7 +3448,7 @@ static void gen_RCL(DisasContext *s, X86DecodedInsn *decode) bool have_1bit_cin, can_be_zero; TCGv count; TCGLabel *zero_label = NULL; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + MemOp ot = gen_rotc_count(s, decode, &can_be_zero, &count, decode->op[2].unit); TCGv low, high, low_count; if (!count) { @@ -3451,7 +3459,6 @@ static void gen_RCL(DisasContext *s, X86DecodedInsn *decode) high = tcg_temp_new(); low_count = tcg_temp_new(); - gen_rotc_mod(ot, count); have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero); if (can_be_zero) { zero_label = gen_new_label(); @@ -3492,7 +3499,7 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) bool have_1bit_cin, can_be_zero; TCGv count; TCGLabel *zero_label = NULL; - MemOp ot = gen_shift_count(s, decode, &can_be_zero, &count, decode->op[2].unit); + MemOp ot = gen_rotc_count(s, decode, &can_be_zero, &count, decode->op[2].unit); TCGv low, high, high_count; if (!count) { @@ -3503,7 +3510,6 @@ static void gen_RCR(DisasContext *s, X86DecodedInsn *decode) high = tcg_temp_new(); high_count = tcg_temp_new(); - gen_rotc_mod(ot, count); have_1bit_cin = gen_eflags_adcox(s, decode, true, can_be_zero); if (can_be_zero) { zero_label = gen_new_label(); |
