]> git.proxmox.com Git - pve-qemu.git/blob - debian/patches/extra/0027-target-i386-fix-ADOX-followed-by-ADCX.patch
d/rules: drop virtiofsd switch
[pve-qemu.git] / debian / patches / extra / 0027-target-i386-fix-ADOX-followed-by-ADCX.patch
1 From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2 From: Paolo Bonzini <pbonzini@redhat.com>
3 Date: Tue, 31 Jan 2023 09:48:03 +0100
4 Subject: [PATCH] target/i386: fix ADOX followed by ADCX
5
6 When ADCX is followed by ADOX or vice versa, the second instruction's
7 carry comes from EFLAGS and the condition codes use the CC_OP_ADCOX
8 operation. Retrieving the carry from EFLAGS is handled by this bit
9 of gen_ADCOX:
10
11 tcg_gen_extract_tl(carry_in, cpu_cc_src,
12 ctz32(cc_op == CC_OP_ADCX ? CC_C : CC_O), 1);
13
14 Unfortunately, in this case cc_op has been overwritten by the previous
15 "if" statement to CC_OP_ADCOX. This works by chance when the first
16 instruction is ADCX; however, if the first instruction is ADOX,
17 ADCX will incorrectly take its carry from OF instead of CF.
18
19 Fix by moving the computation of the new cc_op at the end of the function.
20 The included exhaustive test case fails without this patch and passes
21 afterwards.
22
23 Because ADCX/ADOX need not be invoked through the VEX prefix, this
24 regression bisects to commit 16fc5726a6e2 ("target/i386: reimplement
25 0x0f 0x38, add AVX", 2022-10-18). However, the mistake happened a
26 little earlier, when BMI instructions were rewritten using the new
27 decoder framework.
28
29 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1471
30 Reported-by: Paul Jolly <https://gitlab.com/myitcv>
31 Fixes: 1d0b926150e5 ("target/i386: move scalar 0F 38 and 0F 3A instruction to new decoder", 2022-10-18)
32 Cc: qemu-stable@nongnu.org
33 Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
34 (cherry-picked from commit 60c7dd22e1383754d5f150bc9f7c2785c662a7b6)
35 Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
36 ---
37 target/i386/tcg/emit.c.inc | 20 +++++----
38 tests/tcg/i386/Makefile.target | 6 ++-
39 tests/tcg/i386/test-i386-adcox.c | 75 ++++++++++++++++++++++++++++++++
40 3 files changed, 91 insertions(+), 10 deletions(-)
41 create mode 100644 tests/tcg/i386/test-i386-adcox.c
42
43 diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc
44 index 4d7702c106..0d7c6e80ae 100644
45 --- a/target/i386/tcg/emit.c.inc
46 +++ b/target/i386/tcg/emit.c.inc
47 @@ -1015,6 +1015,7 @@ VSIB_AVX(VPGATHERQ, vpgatherq)
48
49 static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
50 {
51 + int opposite_cc_op;
52 TCGv carry_in = NULL;
53 TCGv carry_out = (cc_op == CC_OP_ADCX ? cpu_cc_dst : cpu_cc_src2);
54 TCGv zero;
55 @@ -1022,14 +1023,8 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
56 if (cc_op == s->cc_op || s->cc_op == CC_OP_ADCOX) {
57 /* Re-use the carry-out from a previous round. */
58 carry_in = carry_out;
59 - cc_op = s->cc_op;
60 - } else if (s->cc_op == CC_OP_ADCX || s->cc_op == CC_OP_ADOX) {
61 - /* Merge with the carry-out from the opposite instruction. */
62 - cc_op = CC_OP_ADCOX;
63 - }
64 -
65 - /* If we don't have a carry-in, get it out of EFLAGS. */
66 - if (!carry_in) {
67 + } else {
68 + /* We don't have a carry-in, get it out of EFLAGS. */
69 if (s->cc_op != CC_OP_ADCX && s->cc_op != CC_OP_ADOX) {
70 gen_compute_eflags(s);
71 }
72 @@ -1053,7 +1048,14 @@ static void gen_ADCOX(DisasContext *s, CPUX86State *env, MemOp ot, int cc_op)
73 tcg_gen_add2_tl(s->T0, carry_out, s->T0, carry_out, s->T1, zero);
74 break;
75 }
76 - set_cc_op(s, cc_op);
77 +
78 + opposite_cc_op = cc_op == CC_OP_ADCX ? CC_OP_ADOX : CC_OP_ADCX;
79 + if (s->cc_op == CC_OP_ADCOX || s->cc_op == opposite_cc_op) {
80 + /* Merge with the carry-out from the opposite instruction. */
81 + set_cc_op(s, CC_OP_ADCOX);
82 + } else {
83 + set_cc_op(s, cc_op);
84 + }
85 }
86
87 static void gen_ADCX(DisasContext *s, CPUX86State *env, X86DecodedInsn *decode)
88 diff --git a/tests/tcg/i386/Makefile.target b/tests/tcg/i386/Makefile.target
89 index 81831cafbc..bafd8c2180 100644
90 --- a/tests/tcg/i386/Makefile.target
91 +++ b/tests/tcg/i386/Makefile.target
92 @@ -14,7 +14,7 @@ config-cc.mak: Makefile
93 I386_SRCS=$(notdir $(wildcard $(I386_SRC)/*.c))
94 ALL_X86_TESTS=$(I386_SRCS:.c=)
95 SKIP_I386_TESTS=test-i386-ssse3 test-avx test-3dnow test-mmx
96 -X86_64_TESTS:=$(filter test-i386-bmi2 $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
97 +X86_64_TESTS:=$(filter test-i386-adcox test-i386-bmi2 $(SKIP_I386_TESTS), $(ALL_X86_TESTS))
98
99 test-i386-sse-exceptions: CFLAGS += -msse4.1 -mfpmath=sse
100 run-test-i386-sse-exceptions: QEMU_OPTS += -cpu max
101 @@ -28,6 +28,10 @@ test-i386-bmi2: CFLAGS=-O2
102 run-test-i386-bmi2: QEMU_OPTS += -cpu max
103 run-plugin-test-i386-bmi2-%: QEMU_OPTS += -cpu max
104
105 +test-i386-adcox: CFLAGS=-O2
106 +run-test-i386-adcox: QEMU_OPTS += -cpu max
107 +run-plugin-test-i386-adcox-%: QEMU_OPTS += -cpu max
108 +
109 #
110 # hello-i386 is a barebones app
111 #
112 diff --git a/tests/tcg/i386/test-i386-adcox.c b/tests/tcg/i386/test-i386-adcox.c
113 new file mode 100644
114 index 0000000000..16169efff8
115 --- /dev/null
116 +++ b/tests/tcg/i386/test-i386-adcox.c
117 @@ -0,0 +1,75 @@
118 +/* See if various BMI2 instructions give expected results */
119 +#include <assert.h>
120 +#include <stdint.h>
121 +#include <stdio.h>
122 +
123 +#define CC_C 1
124 +#define CC_O (1 << 11)
125 +
126 +#ifdef __x86_64__
127 +#define REG uint64_t
128 +#else
129 +#define REG uint32_t
130 +#endif
131 +
132 +void test_adox_adcx(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_operand)
133 +{
134 + REG flags;
135 + REG out_adcx, out_adox;
136 +
137 + asm("pushf; pop %0" : "=r"(flags));
138 + flags &= ~(CC_C | CC_O);
139 + flags |= (in_c ? CC_C : 0);
140 + flags |= (in_o ? CC_O : 0);
141 +
142 + out_adcx = adcx_operand;
143 + out_adox = adox_operand;
144 + asm("push %0; popf;"
145 + "adox %3, %2;"
146 + "adcx %3, %1;"
147 + "pushf; pop %0"
148 + : "+r" (flags), "+r" (out_adcx), "+r" (out_adox)
149 + : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox));
150 +
151 + assert(out_adcx == in_c + adcx_operand - 1);
152 + assert(out_adox == in_o + adox_operand - 1);
153 + assert(!!(flags & CC_C) == (in_c || adcx_operand));
154 + assert(!!(flags & CC_O) == (in_o || adox_operand));
155 +}
156 +
157 +void test_adcx_adox(uint32_t in_c, uint32_t in_o, REG adcx_operand, REG adox_operand)
158 +{
159 + REG flags;
160 + REG out_adcx, out_adox;
161 +
162 + asm("pushf; pop %0" : "=r"(flags));
163 + flags &= ~(CC_C | CC_O);
164 + flags |= (in_c ? CC_C : 0);
165 + flags |= (in_o ? CC_O : 0);
166 +
167 + out_adcx = adcx_operand;
168 + out_adox = adox_operand;
169 + asm("push %0; popf;"
170 + "adcx %3, %1;"
171 + "adox %3, %2;"
172 + "pushf; pop %0"
173 + : "+r" (flags), "+r" (out_adcx), "+r" (out_adox)
174 + : "r" ((REG)-1), "0" (flags), "1" (out_adcx), "2" (out_adox));
175 +
176 + assert(out_adcx == in_c + adcx_operand - 1);
177 + assert(out_adox == in_o + adox_operand - 1);
178 + assert(!!(flags & CC_C) == (in_c || adcx_operand));
179 + assert(!!(flags & CC_O) == (in_o || adox_operand));
180 +}
181 +
182 +int main(int argc, char *argv[]) {
183 + /* try all combinations of input CF, input OF, CF from op1+op2, OF from op2+op1 */
184 + int i;
185 + for (i = 0; i <= 15; i++) {
186 + printf("%d\n", i);
187 + test_adcx_adox(!!(i & 1), !!(i & 2), !!(i & 4), !!(i & 8));
188 + test_adox_adcx(!!(i & 1), !!(i & 2), !!(i & 4), !!(i & 8));
189 + }
190 + return 0;
191 +}
192 +