thebeanogamer / rpms / qemu-kvm

Forked from rpms/qemu-kvm 5 months ago
Clone
ed5979
From 54d3e58aabf9716f9a07aeb7044d7b7997e28123 Mon Sep 17 00:00:00 2001
ed5979
From: Paolo Bonzini <pbonzini@redhat.com>
ed5979
Date: Tue, 31 Jan 2023 09:48:03 +0100
ed5979
Subject: [PATCH 5/8] target/i386: fix ADOX followed by ADCX
ed5979
ed5979
RH-Author: Paolo Bonzini <pbonzini@redhat.com>
ed5979
RH-MergeRequest: 154: target/i386: fix bugs in emulation of BMI instructions
ed5979
RH-Bugzilla: 2173590
ed5979
RH-Acked-by: Emanuele Giuseppe Esposito <eesposit@redhat.com>
ed5979
RH-Acked-by: Vitaly Kuznetsov <vkuznets@redhat.com>
ed5979
RH-Acked-by: Bandan Das <None>
ed5979
RH-Commit: [5/7] 64dbe4e602f08e4a88fdeacee5a8993ca4383563 (bonzini/rhel-qemu-kvm)
ed5979
ed5979
Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2173590
ed5979
Upstream-Status: merged
ed5979
ed5979
When ADCX is followed by ADOX or vice versa, the second instruction's
ed5979
carry comes from EFLAGS and the condition codes use the CC_OP_ADCOX
ed5979
operation.  Retrieving the carry from EFLAGS is handled by this bit
ed5979
of gen_ADCOX:
ed5979
ed5979
        tcg_gen_extract_tl(carry_in, cpu_cc_src,
ed5979
            ctz32(cc_op == CC_OP_ADCX ? CC_C : CC_O), 1);
ed5979
ed5979
Unfortunately, in this case cc_op has been overwritten by the previous
ed5979
"if" statement to CC_OP_ADCOX.  This works by chance when the first
ed5979
instruction is ADCX; however, if the first instruction is ADOX,
ed5979
ADCX will incorrectly take its carry from OF instead of CF.
ed5979
ed5979
Fix by moving the computation of the new cc_op at the end of the function.
ed5979
The included exhaustive test case fails without this patch and passes
ed5979
afterwards.
ed5979
ed5979
Because ADCX/ADOX need not be invoked through the VEX prefix, this
ed5979
regression bisects to commit 16fc5726a6e2 ("target/i386: reimplement
ed5979
0x0f 0x38, add AVX", 2022-10-18).  However, the mistake happened a
ed5979
little earlier, when BMI instructions were rewritten using the new
ed5979
decoder framework.
ed5979
ed5979
Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1471
ed5979
Reported-by: Paul Jolly <https://gitlab.com/myitcv>
ed5979
Fixes: 1d0b926150e5 ("target/i386: move scalar 0F 38 and 0F 3A instruction to new decoder", 2022-10-18)
ed5979
Cc: qemu-stable@nongnu.org
ed5979
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
ed5979
(cherry picked from commit 60c7dd22e1383754d5f150bc9f7c2785c662a7b6)
ed5979
---
ed5979
 target/i386/tcg/emit.c.inc       | 20 +++++----
ed5979
 tests/tcg/i386/Makefile.target   |  6 ++-
ed5979
 tests/tcg/i386/test-i386-adcox.c | 75 ++++++++++++++++++++++++++++++++
ed5979
 3 files changed, 91 insertions(+), 10 deletions(-)
ed5979
 create mode 100644 tests/tcg/i386/test-i386-adcox.c
ed5979
ed5979
diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc
ed5979
index 4d7702c106..0d7c6e80ae 100644
ed5979
--- a/target/i386/tcg/emit.c.inc
ed5979
+++ b/target/i386/tcg/emit.c.inc
ed5979
@@ -1015,6 +1015,7 @@ VSIB_AVX(VPGATHERQ, vpgatherq)
ed5979
 
ed5979
 static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
ed5979
 {
ed5979
+    int opposite_cc_op;
ed5979
     TCGv carry_in = NULL;
ed5979
     TCGv carry_out = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2);
ed5979
     TCGv zero;
ed5979
@@ -1022,14 +1023,8 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
ed5979
     if (cc_op == s->cc_op || s->cc_op == CC_OP_ADCOX) {
ed5979
         /* Re-use the carry-out from a previous round.  */
ed5979
         carry_in = carry_out;
ed5979
-        cc_op = s->cc_op;
ed5979
-    } else if (s->cc_op == CC_OP_ADCX || s->cc_op == CC_OP_ADOX) {
ed5979
-        /* Merge with the carry-out from the opposite instruction.  */
ed5979
-        cc_op = CC_OP_ADCOX;
ed5979
-    }
ed5979
-
ed5979
-    /* If we don't have a carry-in, get it out of EFLAGS.  */
ed5979
-    if (!carry_in) {
ed5979
+    } else {
ed5979
+        /* We don't have a carry-in, get it out of EFLAGS.  */
ed5979
         if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) {
ed5979
             gen_compute_eflags(s);
ed5979
         }
ed5979
@@ -1053,7 +1048,14 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
ed5979
         tcg_gen_add2_tl(s->T0, carry_out, s->T0, carry_out, s->T1, zero);
ed5979
         break;
ed5979
     }
ed5979
-    set_cc_op(s, cc_op);
ed5979
+
ed5979
+    opposite_cc_op = cc_op == CC_OP_ADCX ? CC_OP_ADOX : CC_OP_ADCX;
ed5979
+    if (s->cc_op == CC_OP_ADCOX || s->cc_op == opposite_cc_op) {
ed5979
+        /* Merge with the carry-out from the opposite instruction.  */
ed5979
+        set_cc_op(s, CC_OP_ADCOX);
ed5979
+    } else {
ed5979
+        set_cc_op(s, cc_op);
ed5979
+    }
ed5979
 }
ed5979
 
ed5979
 static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
ed5979
diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target
ed5979
index 81831cafbc..bafd8c2180 100644
ed5979
--- a/tests/tcg/i386/Makefile.target
ed5979
+++ b/tests/tcg/i386/Makefile.target
ed5979
@@ -14,7 +14,7 @@ config-cc.mak: Makefile
ed5979
 I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c))
ed5979
 ALL_X86_TESTS=$(I386_SRCS:.c=)
ed5979
 SKIP_I386_TESTS=test-i386-ssse3 test-avx test-3dnow test-mmx
ed5979
-X86_64_TESTS:=$(filter test-i386-bmi2 $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
ed5979
+X86_64_TESTS:=$(filter test-i386-adcox test-i386-bmi2 $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
ed5979
 
ed5979
 test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse
ed5979
 run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max
ed5979
@@ -28,6 +28,10 @@ test-i386-bmi2: CFLAGS=-O2
ed5979
 run-test-i386-bmi2: QEMU_OPTS += -cpu max
ed5979
 run-plugin-test-i386-bmi2-%: QEMU_OPTS += -cpu max
ed5979
 
ed5979
+test-i386-adcox: CFLAGS=-O2
ed5979
+run-test-i386-adcox: QEMU_OPTS += -cpu max
ed5979
+run-plugin-test-i386-adcox-%: QEMU_OPTS += -cpu max
ed5979
+
ed5979
 #
ed5979
 # hello-i386 is a barebones app
ed5979
 #
ed5979
diff --git a/tests/tcg/i386/test-i386-adcox.c b/tests/tcg/i386/test-i386-adcox.c
ed5979
new file mode 100644
ed5979
index 0000000000..16169efff8
ed5979
--- /dev/null
ed5979
+++ b/tests/tcg/i386/test-i386-adcox.c
ed5979
@@ -0,0 +1,75 @@
ed5979
+/* See if various BMI2 instructions give expected results */
ed5979
+#include <assert.h>
ed5979
+#include <stdint.h>
ed5979
+#include <stdio.h>
ed5979
+
ed5979
+#define CC_C 1
ed5979
+#define CC_O (1 << 11)
ed5979
+
ed5979
+#ifdef __x86_64__
ed5979
+#define REG uint64_t
ed5979
+#else
ed5979
+#define REG uint32_t
ed5979
+#endif
ed5979
+
ed5979
+void test_adox_adcx(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_operand)
ed5979
+{
ed5979
+    REG flags;
ed5979
+    REG out_adcx, out_adox;
ed5979
+
ed5979
+    asm("pushf; pop %0" : "=r"(flags));
ed5979
+    flags &= ~(CC_C | CC_O);
ed5979
+    flags |= (in_c ? CC_C : 0);
ed5979
+    flags |= (in_o ? CC_O : 0);
ed5979
+
ed5979
+    out_adcx = adcx_operand;
ed5979
+    out_adox = adox_operand;
ed5979
+    asm("push %0; popf;"
ed5979
+        "adox %3, %2;"
ed5979
+        "adcx %3, %1;"
ed5979
+        "pushf; pop %0"
ed5979
+        : "+r" (flags), "+r" (out_adcx), "+r" (out_adox)
ed5979
+        : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox));
ed5979
+
ed5979
+    assert(out_adcx == in_c + adcx_operand - 1);
ed5979
+    assert(out_adox == in_o + adox_operand - 1);
ed5979
+    assert(!!(flags & CC_C) == (in_c || adcx_operand));
ed5979
+    assert(!!(flags & CC_O) == (in_o || adox_operand));
ed5979
+}
ed5979
+
ed5979
+void test_adcx_adox(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_operand)
ed5979
+{
ed5979
+    REG flags;
ed5979
+    REG out_adcx, out_adox;
ed5979
+
ed5979
+    asm("pushf; pop %0" : "=r"(flags));
ed5979
+    flags &= ~(CC_C | CC_O);
ed5979
+    flags |= (in_c ? CC_C : 0);
ed5979
+    flags |= (in_o ? CC_O : 0);
ed5979
+
ed5979
+    out_adcx = adcx_operand;
ed5979
+    out_adox = adox_operand;
ed5979
+    asm("push %0; popf;"
ed5979
+        "adcx %3, %1;"
ed5979
+        "adox %3, %2;"
ed5979
+        "pushf; pop %0"
ed5979
+        : "+r" (flags), "+r" (out_adcx), "+r" (out_adox)
ed5979
+        : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox));
ed5979
+
ed5979
+    assert(out_adcx == in_c + adcx_operand - 1);
ed5979
+    assert(out_adox == in_o + adox_operand - 1);
ed5979
+    assert(!!(flags & CC_C) == (in_c || adcx_operand));
ed5979
+    assert(!!(flags & CC_O) == (in_o || adox_operand));
ed5979
+}
ed5979
+
ed5979
+int main(int argc, char *argv[]) {
ed5979
+    /* try all combinations of input CF, input OF, CF from op1+op2,  OF from op2+op1 */
ed5979
+    int i;
ed5979
+    for (i = 0; i <= 15; i++) {
ed5979
+        printf("%d\n", i);
ed5979
+        test_adcx_adox(!!(i & 1), !!(i & 2), !!(i & 4), !!(i & 8));
ed5979
+        test_adox_adcx(!!(i & 1), !!(i & 2), !!(i & 4), !!(i & 8));
ed5979
+    }
ed5979
+    return 0;
ed5979
+}
ed5979
+
ed5979
-- 
ed5979
2.39.1
ed5979