On 12/03/2026 09:54, Muhammad Kamran wrote:
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.
Most of my comments on patch2 apply here as well, but we just need to be
a bit careful with the C flag within the expansion, since for
subtraction on Arm architectures, an input value of non-zero needs to
clear the C before the subsequent subtract. The easiest way to do that
is to use
CMN scratch, #0
You might need to have a special pattern for that.
R.
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+.*" } } */