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 | 5f0e74c93d9c8376e0490dec5c6cf5873b56a261 (patch) | |
| tree | b15ce623ef20e6a24a061c59c540d4c5596b3071 | |
| parent | 6e7d0ddfc2e969c742ef310cceda406e0fa4e4ba (diff) | |
| download | qemu-5f0e74c93d9c8376e0490dec5c6cf5873b56a261.tar.gz qemu-5f0e74c93d9c8376e0490dec5c6cf5873b56a261.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 a22673e4b4..af1cea92be 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3237,8 +3237,9 @@ static void gen_PUSHF(DisasContext *s, X86DecodedInsn *decode) gen_push_v(s, s->T0); } -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); @@ -3248,16 +3249,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: @@ -3272,6 +3288,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 @@ -3390,29 +3413,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); } /* @@ -3433,7 +3441,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) { @@ -3444,7 +3452,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(); @@ -3485,7 +3492,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) { @@ -3496,7 +3503,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(); |
