On 12/03/2026 09:54, Muhammad Kamran wrote:
The pattern expands uaddc sum into:
cmp openand[4], #1
adcs operand[0], operand[2], operand[3]
cset operand[1], cs
Carry chained uaddc expansion results in a rount trip CC -> register -> CC.
Remove that round-trip as CSET-like materialization does not clobber flags,
so CC already carries the same bit.
Thanks, this is already looking pretty good, but a few nits below.
gcc/ChangeLog:
* config/aarch64/aarch64.md
(uaddc<mode>5): Expansion pattern for uaddc<m>5
(*aarch64_setc_from_carry_combine_cc_c_<mode>) Post-combine no-op split
pattern for carry chain elimination in CC_Cmode
(*aarch64_setc_from_carry_combine_cc_adc_<mode> Post-combine no-op
split for carry chain elimination in CC_ADCmode
gcc/testsuite/ChangeLog:
* gcc.target/aarch64/uaddcm5.c: test chained uaddc expansion
ChangeLog entries are sentences, so should start with a capital letter
and end with a full stop.
---
gcc/config/aarch64/aarch64.md | 60 ++++++++++++++++++++++
gcc/testsuite/gcc.target/aarch64/uaddcm5.c | 26 ++++++++++
2 files changed, 86 insertions(+)
create mode 100644 gcc/testsuite/gcc.target/aarch64/uaddcm5.c
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index df5f72ab94c..e875bcaec28 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -3390,6 +3390,66 @@ (define_insn "*add<mode>3_carryinC"
[(set_attr "type" "adc_reg")]
)
I think it would help to have a comment here describing these operands;
it's more necessary because you haven't added pro-forma RTL for the expand.
+(define_expand "uaddc<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_add<mode>3_compareC (operands[0], operands[2],
+ operands[3]));
+ rtx cc = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+ rtx pat = gen_rtx_LTU (<MODE>mode, cc, const0_rtx);
+ emit_insn (gen_rtx_SET (operands[1], pat));
aarch64 defines a function 'emit_set_insn' which can be used to simplify
this:
emit_set_insn (operands[1], pat);
+ }
I think you can also handle a const that is non-zero here, since the
most efficient way to set the C flag in the CPSR is to compare a
register (any register will do, though unfortunately Xzr isn't valid)
against 0, since C=1 => unsigned higher or same.
There are also optimizations that can be made if you know that the RHS
of the addition is a constant; but there are two cases to consider:
1) y = ~0 (all bits one). In this case the result will be X and the
resulting carry flag will be 1. You no-longer need an ADD in this case.
2) All other values of y. In those cases you can just add 1 to y and
use a simple add-with-carryout operation.
Note that for both these cases you'll need to change the operand type
for the pattern to accept an immediate value (nonmemory_operand). If
that value isn't then supported by the instruction you choose, you'll
need to force it into a register before using it.
+ else
+ {
+ rtx cin = force_reg (<MODE>mode, operands[4]);
+ rtx cc = gen_rtx_REG (CC_Cmode, CC_REGNUM);
+ rtx adc_cc = gen_rtx_REG (CC_ADCmode, CC_REGNUM);
+ rtx cmp = gen_rtx_COMPARE (CC_Cmode,
+ gen_rtx_PLUS (<MODE>mode, cin, constm1_rtx),
+ cin);
+ emit_insn (gen_rtx_SET (cc, cmp));
+ emit_insn (gen_add<mode>3_carryinC (operands[0], operands[2],
+ operands[3]));
+ rtx pat = gen_rtx_GEU (<MODE>mode, adc_cc, const0_rtx);
+ emit_insn (gen_rtx_SET (operands[1], pat));
+ }
+ DONE;
+})
+
+;; The uaddc expansion results in a rount 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_carry_combine_cc_c_<mode>"
+ [(set (reg:CC_C CC_REGNUM)
+ (compare:CC_C
+ (neg:GPI
+ (geu:GPI (reg:CC_C CC_REGNUM) (const_int 0)))
+ (ltu:GPI (reg:CC_C CC_REGNUM) (const_int 0))))]
+ ""
+ "#"
+ "&& 1"
This can just be the empty string "".
+ [(const_int 0)]
+ "emit_note (NOTE_INSN_DELETED); DONE;")
+
+(define_insn_and_split "*aarch64_setc_from_carry_combine_cc_adc_<mode>"
+ [(set (reg:CC_C CC_REGNUM)
+ (compare:CC_C
+ (neg:GPI
+ (ltu:GPI (reg:CC_ADC CC_REGNUM) (const_int 0)))
+ (geu:GPI (reg:CC_ADC CC_REGNUM) (const_int 0))))]
+ ""
+ "#"
+ "&& 1"
+ [(const_int 0)]
+ "emit_note (NOTE_INSN_DELETED); DONE;")
+
(define_expand "add<mode>3_carryinV"
[(parallel
[(set (reg:CC_V CC_REGNUM)
diff --git a/gcc/testsuite/gcc.target/aarch64/uaddcm5.c
b/gcc/testsuite/gcc.target/aarch64/uaddcm5.c
new file mode 100644
index 00000000000..39a3508b066
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/uaddcm5.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+static unsigned long
+uaddc (unsigned long x, unsigned long y, unsigned long carry_in, unsigned long
*carry_out)
+{
+ unsigned long r;
+ unsigned long c1 = __builtin_add_overflow (x, y, &r);
+ unsigned long c2 = __builtin_add_overflow (r, carry_in, &r);
+ *carry_out = c1 + c2;
+ return r;
+}
+
+void
+foo (unsigned long *p, unsigned long *q)
+{
+ unsigned long c;
+ p[0] = uaddc (p[0], q[0], 0, &c);
+ p[1] = uaddc (p[1], q[1], c, &c);
+ p[2] = uaddc (p[2], q[2], c, &c);
+ p[3] = uaddc (p[3], q[3], c, &c);
+}
+
+
+/* { dg-final { scan-assembler-times "adcs\\t\[xw\]\[0-9\]+, \[xw\]\[0-9\]+,
\[xw\]\[0-9\]+" 3 } } */
+/* { dg-final { scan-assembler-not "cset\\t\[xw\]\[0-9\]+, cs+.*" } } */
\ No newline at end of file
Please make sure there is a newline character at the end of files.
Also, it would be nice to see some tests for __builtin_addc() and some
cases that check the constant folding cases I've described.
R.