https://gcc.gnu.org/bugzilla/show_bug.cgi?id=82628
--- Comment #10 from Jakub Jelinek <jakub at gcc dot gnu.org> --- Testcase for determining the sbbb and adcb behavior for all operands: int main (void) { int cf, x, y; for (cf = 0; cf < 2; cf++) for (x = 0; x <= 255; x++) for (y = 0; y <= 255; y++) { unsigned char cfout, xout; asm volatile ("cmpb %2, %3; sbbb %4, %1; setc %0" : "=a" (cfout), "=q" (xout) : "q" ((unsigned char) cf), "q" ((unsigned char) 0), "q" ((unsigned char) y), "1" ((unsigned char) x)); if (cfout != ((unsigned char) (y + cf) > (unsigned char) x)) __builtin_printf ("cf %d x %x y %x cf %d %d\n", cf, x, y, cfout, ((unsigned char) (y + cf) > (unsigned char) x)); if (cfout != ((y + cf) > (unsigned char) x)) __builtin_abort (); } __builtin_printf ("=======\n"); for (cf = 0; cf < 2; cf++) for (x = -128; x <= 127; x++) for (y = -128; y <= 127; y++) { unsigned char ovout, xout; asm volatile ("cmpb %2, %3; sbbb %4, %1; seto %0" : "=a" (ovout), "=q" (xout) : "q" ((unsigned char) cf), "q" ((unsigned char) 0), "q" ((unsigned char) y), "1" ((unsigned char) x)); if ((ovout != ((signed char) xout < 0)) != ((signed char) (y + cf) > (signed char) x)) __builtin_printf ("cf %d x %x y %x ov %d %d %d\n", cf, x, y, ovout, (ovout != ((signed char) xout < 0)), ((signed char) (y + cf) > (signed char) x)); if ((ovout != ((signed char) xout < 0)) != ((y + cf) > (signed char) x)) __builtin_abort (); } __builtin_printf ("=======\n"); for (cf = 0; cf < 2; cf++) for (x = 0; x <= 255; x++) for (y = 0; y <= 255; y++) { unsigned char cfout, xout; asm volatile ("cmpb %2, %3; adcb %4, %1; setc %0" : "=a" (cfout), "=q" (xout) : "q" ((unsigned char) cf), "q" ((unsigned char) 0), "q" ((unsigned char) y), "1" ((unsigned char) x)); if (cfout != ((unsigned char) (x + y + cf) < (unsigned char) x)) __builtin_printf ("cf %d x %x y %x cf %d %d\n", cf, x, y, cfout, ((unsigned char) (x + y + cf) < (unsigned char) x)); if (cfout != ((unsigned char) (x + y + cf) < (y + cf))) __builtin_abort (); } return 0; } This shows that the current (define_insn "sub<mode>3_carry_ccgz" [(set (reg:CCGZ FLAGS_REG) (compare:CCGZ (match_operand:DWIH 1 "register_operand" "0") (plus:DWIH (ltu:DWIH (reg:CC FLAGS_REG) (const_int 0)) (match_operand:DWIH 2 "x86_64_general_operand" "rme")))) (clobber (match_scratch:DWIH 0 "=r"))] "" "sbb{<imodesuffix>}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "<MODE>")]) pattern when we want a comparison that checks the CF flag (i.e. GEU, LTU comparisons) is wrong for the case when operands[2] is the unsigned integer maximum and CF is set from earlier; so it should really be something like: (define_insn "sub<mode>3_carry_ccc" [(set (reg:CCC FLAGS_REG) (compare:CCC (zero_extend:<DWI> (match_operand:DWIH 1 "register_operand" "0")) (plus:<DWI> (ltu:<DWI> (reg:CC FLAGS_REG) (const_int 0)) (zero_extend:<DWI> (match_operand:DWIH 2 "x86_64_general_operand" "rme"))))) (clobber (match_scratch:DWIH 0 "=r"))] "" "sbb{<imodesuffix>}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "<MODE>")]) (see first loop in the testcase above). For pattern when we want a comparison that checks the SF and OF flags (i.e. GE, LT comparisons), we probably need a different mode instead of CCGZmode that says that just SF and OF are valid (or is any mode we have usable for this), and then do something like: (define_insn "sub<mode>3_carry_ccx" [(set (reg:CCX FLAGS_REG) (compare:CCX (sign_extend:<DWI> (match_operand:DWIH 1 "register_operand" "0")) (plus:<DWI> (ltu:<DWI> (reg:CC FLAGS_REG) (const_int 0)) (sign_extend:<DWI> (match_operand:DWIH 2 "x86_64_general_operand" "rme"))))) (clobber (match_scratch:DWIH 0 "=r"))] "" "sbb{<imodesuffix>}\t{%2, %0|%0, %2}" [(set_attr "type" "alu") (set_attr "mode" "<MODE>")]) as what we have for the signed comparisons using SF cmp OF fails if operands[2] is signed maximum and CF is set. Though, not really sure about this, because in the testcase above I'm using SF bit from the 8-bit result rather than 16-bit/32-bit result. And subborrow<mode> and addcarry<mode> would need changes too.