From: Paolo Bonzini <[email protected]> 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 <[email protected]> (cherry picked from commit e38d0afade7c134cd4d675f54d26c394cc3cc31f) Signed-off-by: Michael Tokarev <[email protected]> diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 12e7f5f943..64d3fa7db9 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -3253,8 +3253,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); @@ -3264,16 +3265,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: @@ -3288,6 +3304,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 @@ -3406,29 +3429,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); } /* @@ -3449,7 +3457,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) { @@ -3460,7 +3468,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(); @@ -3501,7 +3508,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) { @@ -3512,7 +3519,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(); -- 2.47.3
