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.