Muhammad Kamran <[email protected]> writes:
> 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.
>
> 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
> ---
>  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")]
>  )
>  
> +(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));
> +    }
> +  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"
> +  [(const_int 0)]
> +  "emit_note (NOTE_INSN_DELETED); DONE;")

If we want this kind of simplification to be target-specific,
I think we should add a dedicated mechanism for it, rather than using
define_insn_and_split.  There are two reasons, one kind of subjective
and one more practical:

(1) The instruction above is never really intended to be an instruction
    and is not needed to preserve RTL semantics (since it's a nop in
    RTL terms as well as machine terms).  It also adds overhead by
    taking up insn codes and requiring entries in recog_data etc.

    In other words, it seems like we're using define_insn_and_split
    because it's already there, rather than because it's what we would
    choose if we were starting from scratch.

(2) As a simplification rule, the above is really:

      (compare:CC_C (neg:M (geu:M X (const_int 0)))
                    (ltu:M X (const_int 0)))

      -> X

    Expressing this as an instruction pattern is less powerful than
    using open-coded simplify-rtx.cc rules, again for two reasons:

    (a) The symbolic rule above works for all X, including the rhs of a
        (reg:CC_C CC_REGNUM) definition.  The define_insn_and_split only
        works for (reg:CC_C CC_REGNUM) itself.

    (b) A simplify-rtx.cc rule would expose the "nopness" straight away,
        enabling follow-on optimisations in the same RTL pass.
        The instruction pattern only exposes the nopness at the next
        split point.

FWIW, I remember writing a patch once to add simplification rules to md
files, with the aim of using them to simplify UNSPECs.  Can't remember
whether I ever posted it though, and it would have been on an Arm
machine that I no longer have access to.

That said, the above rule seems like it would work for all CC modes on
all STORE_FLAG_VALUE==1 targets, unless I'm missing something.  The rule
below is an inversion by means of a mode change, which could be expressed
using a target hook to say that CC_ADCmode and CC_Cmode are mutual inverses
for all operations that are defined on them (that is, LTU and GEU).

Thanks,
Richard

> +
> +(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

Reply via email to