@samitouri / QOSamiQemu / commits / 03ce28d453

target/i386: emulate: stop torturing cc_src into carrying SF/PF

x86_flags.c encodes the flags into cc_dst and cc_src with algorithms essentially derived from Bochs; the exact details have changed but cc_dst is Bochs result and cc_src is very close to Bochs auxbits. However, using only two words is unnecessarily limiting because it splits SF/PF between the two words even though *ZF* is the real nuisance (ZF=1 implies SF=PF=0) and the one that commands usage of PD/SD delta bits. Within TCG, the CCMP instruction would have a similar need of efficiently encoding an arithmetic result or an EFLAGS value; it is not implemented, but there are plans (see commit message for 5dcdbd07125, "target/i386: tcg: use cout to commonize add/adc/sub/sbb cases", 2025-04-17) to use an algorithm very similar to target/i386/emulate's, but with *three* words. Then SF and PF live together in harmony, because SF can be encoded with either parity and PF does not use the high bit where SF is stored; by placing them in a third word their computation is isolated from ZF's and everything becomes simpler. In fact I'm not even sure why Bochs did it like that, and did not just give SF/PF their own home in a third word as well; the developers believe that the extra store is too expensive. I am not really sure about that, but as far as QEMU is concerned, emulation proceeds one instruction at a time so using SRC2 should actually be faster, not just easier. To convert from the output of arithmetic operations, PD and SD disappear and DST simply has to be stored in two places; to convert to RFLAGS, SF/PF are easily computed from SRC2 as if PD=SD=0; conversion to LFLAGS encodes parity in bit 0 and mixes in SF as an even-parity value with the right sign bit. Unlike TCG, there is a single meaning for all operand lengths, which corresponds to either CCMPL or CCMPQ depending on sizeof(target_ulong). So the carry-out value still needs to be split---with AF in bit 3 and CF/PO in the higher bits of the target_ulong-sized env->cc_src. This is an acceptable tradeoff for the interpreter, in order to optimize lflags_to_rflags. Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>

Paolo Bonzini committed Jul 22, 2026 at 11:11 UTC 03ce28d45362dfaab99dd3a6ac842c617c548530
1 file changed +25 -40
target/i386/emulate/x86_flags.c
+25 -40
@@ -30,28 +30,24 @@
30
31
32 /*
33 - * The algorithms here are similar to those in Bochs. After an ALU
34 - * operation, CC_DST can be used to compute ZF, SF and PF, whereas
35 - * CC_SRC is used to compute AF, CF and OF. In reality, SF and PF are the
36 - * XOR of the value computed from CC_DST and the value found in bits 7 and 2
37 - * of CC_SRC; this way the same logic can be used to compute the flags
38 - * both before and after an ALU operation.
33 + * The emulator always encodes flags in the same way as CC_OP_CCMPB + MO_TL.
34 + * While for arithmetic operations ZF/SF/PF are computed from the same value,
35 + * ZF=1 may be inconsistent with PF/SF for arbitrary RFLAGS values so CC_SRC2
36 + * is used for SF and PF. CC_SRC holds a carry-out vector that is used to
37 + * compute AF, CF and OF.
38 *
39 * Compared to the TCG CC_OP codes, this avoids conditionals when converting
40 * to and from the RFLAGS representation.
41 + *
42 + * The underlying ideas ultimately descend from Bochs, but with significant
43 + * simplifications obtained by storing flags in three words rather than two.
44 */
45
46 #define LF_SIGN_BIT (TARGET_LONG_BITS - 1)
47
46 -#define LF_BIT_PD (2) /* lazy Parity Delta, same bit as PF */
47 -#define LF_BIT_AF (3) /* lazy Adjust flag */
48 -#define LF_BIT_SD (7) /* lazy Sign Flag Delta, same bit as SF */
48 #define LF_BIT_CF (TARGET_LONG_BITS - 1) /* lazy Carry Flag */
49 #define LF_BIT_PO (TARGET_LONG_BITS - 2) /* lazy Partial Overflow = CF ^ OF */
50
52 -#define LF_MASK_PD ((target_ulong)0x01 << LF_BIT_PD)
53 -#define LF_MASK_AF ((target_ulong)0x01 << LF_BIT_AF)
54 -#define LF_MASK_SD ((target_ulong)0x01 << LF_BIT_SD)
51 #define LF_MASK_CF ((target_ulong)0x01 << LF_BIT_CF)
52 #define LF_MASK_PO ((target_ulong)0x01 << LF_BIT_PO)
53
@@ -59,19 +55,15 @@
55 /* OSZAPC */
56 /* ******************* */
57
62 -/* use carries to fill in AF, PO and CF, while ensuring PD and SD are clear.
63 - * for full-word operations just clear PD and SD; for smaller operand
64 - * sizes only keep AF in the low byte and shift the carries left to
65 - * place PO and CF in the top two bits.
58 +/*
59 + * For arithmetic operations ZF/SF/PF are consistent so DST == SRC2.
60 + * For operations that are not full-word, keep AF in the low byte and shift
61 + * the carries left to place PO and CF in the top two bits.
62 */
63 #define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \
68 - env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \
69 - target_ulong temp = (lf_carries); \
70 - if ((size) == TARGET_LONG_BITS) { \
71 - temp = temp & ~(LF_MASK_PD | LF_MASK_SD); \
72 - } else { \
73 - temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \
74 - } \
64 + env->cc_dst = env->cc_src2 = (target_ulong)(int##size##_t)(lf_result); \
65 + target_ulong temp = (lf_carries) & MAKE_64BIT_MASK(0, size); \
66 + temp |= temp << (TARGET_LONG_BITS - (size)); \
67 env->cc_src = temp; \
68 }
69
@@ -93,13 +85,9 @@
85 /* same as setting OSZAPC, but preserve CF and flip PO if the old value of CF
86 * did not match the high bit of lf_carries. */
87 #define SET_FLAGS_OSZAP_SIZE(size, lf_carries, lf_result) { \
96 - env->cc_dst = (target_ulong)(int##size##_t)(lf_result); \
97 - target_ulong temp = (lf_carries); \
98 - if ((size) == TARGET_LONG_BITS) { \
99 - temp = (temp & ~(LF_MASK_PD | LF_MASK_SD)); \
100 - } else { \
101 - temp = (temp & LF_MASK_AF) | (temp << (TARGET_LONG_BITS - (size))); \
102 - } \
88 + env->cc_dst = env->cc_src2 = (target_ulong)(int##size##_t)(lf_result); \
89 + target_ulong temp = (lf_carries) & MAKE_64BIT_MASK(0, size); \
90 + temp |= temp << (TARGET_LONG_BITS - (size)); \
91 target_ulong cf_changed = ((target_long)(env->cc_src ^ temp)) < 0; \
92 env->cc_src = temp ^ (cf_changed * (LF_MASK_PO | LF_MASK_CF)); \
93 }
@@ -255,7 +243,7 @@ void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2,
243
244 static inline uint32_t get_PF(CPUX86State *env)
245 {
258 - return ((parity8(env->cc_dst) - 1) ^ env->cc_src) & CC_P;
246 + return (parity8(env->cc_src2) - 1) & CC_P;
247 }
248
249 static inline uint32_t get_OF(CPUX86State *env)
@@ -283,8 +271,7 @@ static inline uint32_t get_ZF(CPUX86State *env)
271
272 static inline uint32_t get_SF(CPUX86State *env)
273 {
286 - return ((env->cc_dst >> (LF_SIGN_BIT - LF_BIT_SD)) ^
287 - env->cc_src) & CC_S;
274 + return (target_long)env->cc_src2 < 0 ? CC_S : 0;
275 }
276
277 void lflags_to_rflags(CPUX86State *env)
@@ -304,16 +291,14 @@ void rflags_to_lflags(CPUX86State *env)
291 {
292 target_ulong cf_af, cf_xor_of;
293
307 - /* Leave the low byte zero so that parity is always even... */
308 - env->cc_dst = !(env->eflags & CC_Z) << 8;
309 -
310 - /* ... and therefore cc_src always uses opposite polarity. */
311 - env->cc_src = CC_P;
312 - env->cc_src ^= env->eflags & (CC_S | CC_P);
294 + /* compute DST and SRC2 that reconstruct ZF/SF/PF. */
295 + env->cc_dst = ~env->eflags & CC_Z; /* DST = 0 if ZF=1 */
296 + env->cc_src2 = ~env->eflags & CC_P; /* odd parity if PF=0 */
297 + env->cc_src2 ^= -!!(env->eflags & CC_S);
298
299 /* rotate right by one to move CF and AF into the carry-out positions */
300 cf_af = env->eflags & (CC_C | CC_A);
316 - env->cc_src |= ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1)));
301 + env->cc_src = ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1)));
302
303 cf_xor_of = ((env->eflags & (CC_C | CC_O)) + (CC_O - CC_C)) & CC_O;
304 env->cc_src |= -cf_xor_of & LF_MASK_PO;