/* Codes needing carry flag. */
case GEU: /* CF=0 */
case LTU: /* CF=1 */
+ rtx geu;
/* Detect overflow checks. They need just the carry flag. */
if (GET_CODE (op0) == PLUS
&& (rtx_equal_p (op1, XEXP (op0, 0))
|| rtx_equal_p (op1, XEXP (op0, 1))))
return CCCmode;
+ /* Similarly for *setcc_qi_addqi3_cconly_overflow_1_* patterns.
+ Match LTU of op0
+ (neg:QI (geu:QI (reg:CC_CCC FLAGS_REG) (const_int 0)))
+ and op1
+ (ltu:QI (reg:CC_CCC FLAGS_REG) (const_int 0))
+ where CC_CCC is either CC or CCC. */
+ else if (code == LTU
+ && GET_CODE (op0) == NEG
+ && GET_CODE (geu = XEXP (op0, 0)) == GEU
+ && REG_P (XEXP (geu, 0))
+ && (GET_MODE (XEXP (geu, 0)) == CCCmode
+ || GET_MODE (XEXP (geu, 0)) == CCmode)
+ && REGNO (XEXP (geu, 0)) == FLAGS_REG
+ && XEXP (geu, 1) == const0_rtx
+ && GET_CODE (op1) == LTU
+ && REG_P (XEXP (op1, 0))
+ && GET_MODE (XEXP (op1, 0)) == GET_MODE (XEXP (geu, 0))
+ && REGNO (XEXP (op1, 0)) == FLAGS_REG
+ && XEXP (op1, 1) == const0_rtx)
+ return CCCmode;
else
return CCmode;
case GTU: /* CF=0 & ZF=0 */
return false;
case COMPARE:
- if (GET_CODE (XEXP (x, 0)) == ZERO_EXTRACT
- && XEXP (XEXP (x, 0), 1) == const1_rtx
- && CONST_INT_P (XEXP (XEXP (x, 0), 2))
- && XEXP (x, 1) == const0_rtx)
+ rtx op0, op1;
+ op0 = XEXP (x, 0);
+ op1 = XEXP (x, 1);
+ if (GET_CODE (op0) == ZERO_EXTRACT
+ && XEXP (op0, 1) == const1_rtx
+ && CONST_INT_P (XEXP (op0, 2))
+ && op1 == const0_rtx)
{
/* This kind of construct is implemented using test[bwl].
Treat it as if we had an AND. */
- mode = GET_MODE (XEXP (XEXP (x, 0), 0));
+ mode = GET_MODE (XEXP (op0, 0));
*total = (cost->add
- + rtx_cost (XEXP (XEXP (x, 0), 0), mode, outer_code,
+ + rtx_cost (XEXP (op0, 0), mode, outer_code,
opno, speed)
+ rtx_cost (const1_rtx, mode, outer_code, opno, speed));
return true;
}
- if (GET_CODE (XEXP (x, 0)) == PLUS
- && rtx_equal_p (XEXP (XEXP (x, 0), 0), XEXP (x, 1)))
+ if (GET_CODE (op0) == PLUS && rtx_equal_p (XEXP (op0, 0), op1))
{
/* This is an overflow detection, count it as a normal compare. */
- *total = rtx_cost (XEXP (x, 0), GET_MODE (XEXP (x, 0)),
- COMPARE, 0, speed);
+ *total = rtx_cost (op0, GET_MODE (op0), COMPARE, 0, speed);
+ return true;
+ }
+
+ rtx geu;
+ /* Match x
+ (compare:CCC (neg:QI (geu:QI (reg:CC_CCC FLAGS_REG) (const_int 0)))
+ (ltu:QI (reg:CC_CCC FLAGS_REG) (const_int 0))) */
+ if (mode == CCCmode
+ && GET_CODE (op0) == NEG
+ && GET_CODE (geu = XEXP (op0, 0)) == GEU
+ && REG_P (XEXP (geu, 0))
+ && (GET_MODE (XEXP (geu, 0)) == CCCmode
+ || GET_MODE (XEXP (geu, 0)) == CCmode)
+ && REGNO (XEXP (geu, 0)) == FLAGS_REG
+ && XEXP (geu, 1) == const0_rtx
+ && GET_CODE (op1) == LTU
+ && REG_P (XEXP (op1, 0))
+ && GET_MODE (XEXP (op1, 0)) == GET_MODE (XEXP (geu, 0))
+ && REGNO (XEXP (op1, 0)) == FLAGS_REG
+ && XEXP (op1, 1) == const0_rtx)
+ {
+ /* This is *setcc_qi_addqi3_cconly_overflow_1_* patterns, a nop. */
+ *total = 0;
return true;
}
/* The embedded comparison operand is completely free. */
- if (!general_operand (XEXP (x, 0), GET_MODE (XEXP (x, 0)))
- && XEXP (x, 1) == const0_rtx)
+ if (!general_operand (op0, GET_MODE (op0)) && op1 == const0_rtx)
*total = 0;
return false;
--- /dev/null
+/* PR target/97387 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fomit-frame-pointer" } */
+/* { dg-final { scan-assembler-times "\taddl\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tadcl\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tsubl\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tsbbl\t" 3 } } */
+/* { dg-final { scan-assembler-not "\tset\[bc]\t" } } */
+/* { dg-final { scan-assembler-not "\taddb\t" } } */
+
+#include <x86intrin.h>
+
+void
+foo (unsigned int a[4], unsigned int b[4])
+{
+ unsigned char carry = 0;
+ carry = _addcarry_u32 (carry, a[0], b[0], &a[0]);
+ carry = _addcarry_u32 (carry, a[1], b[1], &a[1]);
+ carry = _addcarry_u32 (carry, a[2], b[2], &a[2]);
+ _addcarry_u32 (carry, a[3], b[3], &a[3]);
+}
+
+void
+bar (unsigned int a[4], unsigned int b[4])
+{
+ unsigned char carry = 0;
+ carry = _subborrow_u32 (carry, a[0], b[0], &a[0]);
+ carry = _subborrow_u32 (carry, a[1], b[1], &a[1]);
+ carry = _subborrow_u32 (carry, a[2], b[2], &a[2]);
+ _subborrow_u32 (carry, a[3], b[3], &a[3]);
+}
--- /dev/null
+/* PR target/97387 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -fomit-frame-pointer" } */
+/* { dg-final { scan-assembler-times "\taddq\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tadcq\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tsubq\t" 1 } } */
+/* { dg-final { scan-assembler-times "\tsbbq\t" 3 } } */
+/* { dg-final { scan-assembler-not "\tset\[bc]\t" } } */
+/* { dg-final { scan-assembler-not "\taddb\t" } } */
+
+#include <x86intrin.h>
+
+void
+foo (unsigned long long a[4], unsigned long long b[4])
+{
+ unsigned char carry = 0;
+ carry = _addcarry_u64 (carry, a[0], b[0], &a[0]);
+ carry = _addcarry_u64 (carry, a[1], b[1], &a[1]);
+ carry = _addcarry_u64 (carry, a[2], b[2], &a[2]);
+ _addcarry_u64 (carry, a[3], b[3], &a[3]);
+}
+
+void
+bar (unsigned long long a[4], unsigned long long b[4])
+{
+ unsigned char carry = 0;
+ carry = _subborrow_u64 (carry, a[0], b[0], &a[0]);
+ carry = _subborrow_u64 (carry, a[1], b[1], &a[1]);
+ carry = _subborrow_u64 (carry, a[2], b[2], &a[2]);
+ _subborrow_u64 (carry, a[3], b[3], &a[3]);
+}