summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2026-05-28 17:02:52 +0200
committerMichael Tokarev <mjt@tls.msk.ru>2026-06-19 08:37:21 +0300
commit77e8e0a3ae012f97349e1a6435d7d73ab25c402c (patch)
treec8ca9f80f3e2ab1b55aca90c7f074312cd83de18
parent41d5a45d24ed3e18605d3f6569d9446dad3ebf65 (diff)
downloadqemu-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.inc64
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();