The pattern expands usubc into:
negs openand[4]
sbcs operand[0], operand[2], operand[3]
cset operand[1], cc

Similar to the chained uaddc, chained usubc expansions results in a round
trip CC -> register -> CC. Remove that round-trip as CSET-like
materialization does not clobber flags, so CC already carries the same bit.

gcc/ChangeLog:

        * config/aarch64/aarch64.md
        (usubc<mode>5): Expansion pattern for usubc<m>5
        (*aarch64_setc_from_borrow_<mode>) Post-combine no-op split
        pattern for carry chain elimination

gcc/testsuite/ChangeLog:

        * gcc.target/aarch64/usubcm5.c: test chained usubc expansion
---
 gcc/config/aarch64/aarch64.md              | 41 ++++++++++++++++++++++
 gcc/testsuite/gcc.target/aarch64/usubcm5.c | 27 ++++++++++++++
 2 files changed, 68 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/usubcm5.c

diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index e875bcaec28..9ffe019582c 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -4092,6 +4092,47 @@ (define_insn "*subsi3_carryin_alt_uxtw"
   [(set_attr "type" "adc_reg")]
 )
 
+(define_expand "usubc<mode>5"
+  [(match_operand:GPI 0 "register_operand")
+   (match_operand:GPI 1 "register_operand")
+   (match_operand:GPI 2 "register_operand")
+   (match_operand:GPI 3 "register_operand")
+   (match_operand:GPI 4 "nonmemory_operand")]
+  ""
+{
+  if (operands[4] == const0_rtx)
+    {
+      emit_insn (gen_sub<mode>3_compare1 (operands[0], operands[2],
+           operands[3]));
+    }
+  else
+    {
+      rtx cin = force_reg (<MODE>mode, operands[4]);
+      rtx tmp = gen_reg_rtx (<MODE>mode);
+      emit_insn (gen_sub<mode>3_compare1 (tmp, const0_rtx, cin));
+      emit_insn (gen_usub<mode>3_carryinC (operands[0], operands[2],
+            operands[3]));
+    }
+  rtx cc = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+  rtx pat = gen_rtx_GEU (<MODE>mode, cc, const0_rtx);
+  emit_insn (gen_rtx_SET (operands[1], pat));
+  DONE;
+})
+
+;; Similar to the chained uaddc, chained usubc expansions results in a round
+;; trip CC -> register -> CC.  Remove that round-trip as CSET-like
+;; materialization does not clobber flags, so CC already carries the same bit.
+(define_insn_and_split "*aarch64_setc_from_borrow_<mode>"
+  [(set (reg:CC CC_REGNUM)
+       (compare:CC
+         (const_int 0)
+         (match_operand:GPI 0 "aarch64_borrow_operation" "")))]
+  ""
+  "#"
+  "&& 1"
+  [(const_int 0)]
+  "emit_note (NOTE_INSN_DELETED); DONE;")
+
 (define_expand "usub<GPI:mode>3_carryinC"
   [(parallel
      [(set (reg:CC CC_REGNUM)
diff --git a/gcc/testsuite/gcc.target/aarch64/usubcm5.c 
b/gcc/testsuite/gcc.target/aarch64/usubcm5.c
new file mode 100644
index 00000000000..e2d1cbc42c0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/usubcm5.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+static unsigned long
+usubc (unsigned long x, unsigned long y, unsigned long carry_in, unsigned long 
*carry_out)
+{
+  unsigned long r;
+  unsigned long c1 = __builtin_sub_overflow (x, y, &r);
+  unsigned long c2 = __builtin_sub_overflow (r, carry_in, &r);
+  *carry_out = c1 + c2;
+  return r;
+}
+
+void
+__attribute__((__noinline__))
+bar (unsigned long *p, unsigned long *q)
+{
+  unsigned long c;
+  p[0] = usubc (p[0], q[0], 0, &c);
+  p[1] = usubc (p[1], q[1], c, &c);
+  p[2] = usubc (p[2], q[2], c, &c);
+  p[3] = usubc (p[3], q[3], c, &c);
+}
+
+
+/* { dg-final { scan-assembler-times "sbcs\\t\[xw\]\[0-9\]+, \[xw\]\[0-9\]+, 
\[xw\]\[0-9\]+" 3 } } */
+/* { dg-final { scan-assembler-not "cset\\t\[xw\]\[0-9\]+, cc+.*" } } */
-- 
2.43.0

Reply via email to