diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2023-01-31 09:48:03 +0100 |
---|---|---|
committer | Paolo Bonzini <pbonzini@redhat.com> | 2023-02-11 09:07:25 +0100 |
commit | 60c7dd22e1383754d5f150bc9f7c2785c662a7b6 (patch) | |
tree | e0ef4db1c17ebc8850fd3000b98a2abea655070c /target | |
parent | 99282098dc74c2055bde5652bde6cf0067d0c370 (diff) | |
download | qemu-60c7dd22e1383754d5f150bc9f7c2785c662a7b6.zip qemu-60c7dd22e1383754d5f150bc9f7c2785c662a7b6.tar.gz qemu-60c7dd22e1383754d5f150bc9f7c2785c662a7b6.tar.bz2 |
target/i386: fix ADOX followed by ADCX
When ADCX is followed by ADOX or vice versa, the second instruction's
carry comes from EFLAGS and the condition codes use the CC_OP_ADCOX
operation. Retrieving the carry from EFLAGS is handled by this bit
of gen_ADCOX:
tcg_gen_extract_tl(carry_in, cpu_cc_src,
ctz32(cc_op == CC_OP_ADCX ? CC_C : CC_O), 1);
Unfortunately, in this case cc_op has been overwritten by the previous
"if" statement to CC_OP_ADCOX. This works by chance when the first
instruction is ADCX; however, if the first instruction is ADOX,
ADCX will incorrectly take its carry from OF instead of CF.
Fix by moving the computation of the new cc_op at the end of the function.
The included exhaustive test case fails without this patch and passes
afterwards.
Because ADCX/ADOX need not be invoked through the VEX prefix, this
regression bisects to commit 16fc5726a6e2 ("target/i386: reimplement
0x0f 0x38, add AVX", 2022-10-18). However, the mistake happened a
little earlier, when BMI instructions were rewritten using the new
decoder framework.
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1471
Reported-by: Paul Jolly <https://gitlab.com/myitcv>
Fixes: 1d0b926150e5 ("target/i386: move scalar 0F 38 and 0F 3A instruction to new decoder", 2022-10-18)
Cc: qemu-stable@nongnu.org
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'target')
-rw-r--r-- | target/i386/tcg/emit.c.inc | 20 |
1 files changed, 11 insertions, 9 deletions
diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 4d7702c..0d7c6e8 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1015,6 +1015,7 @@ VSIB_AVX(VPGATHERQ, vpgatherq) static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) { + int opposite_cc_op; TCGv carry_in = NULL; TCGv carry_out = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2); TCGv zero; @@ -1022,14 +1023,8 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) if (cc_op == s->cc_op || s->cc_op == CC_OP_ADCOX) { /* Re-use the carry-out from a previous round. */ carry_in = carry_out; - cc_op = s->cc_op; - } else if (s->cc_op == CC_OP_ADCX || s->cc_op == CC_OP_ADOX) { - /* Merge with the carry-out from the opposite instruction. */ - cc_op = CC_OP_ADCOX; - } - - /* If we don't have a carry-in, get it out of EFLAGS. */ - if (!carry_in) { + } else { + /* We don't have a carry-in, get it out of EFLAGS. */ if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) { gen_compute_eflags(s); } @@ -1053,7 +1048,14 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op) tcg_gen_add2_tl(s->T0, carry_out, s->T0, carry_out, s->T1, zero); break; } - set_cc_op(s, cc_op); + + opposite_cc_op = cc_op == CC_OP_ADCX ? CC_OP_ADOX : CC_OP_ADCX; + if (s->cc_op == CC_OP_ADCOX || s->cc_op == opposite_cc_op) { + /* Merge with the carry-out from the opposite instruction. */ + set_cc_op(s, CC_OP_ADCOX); + } else { + set_cc_op(s, cc_op); + } } static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode) |