On Wed, Aug 19, 2026 at 1:38 PM Eikansh Gupta
<[email protected]> wrote:
>
> Recognize the absolute-difference idiom whose selector compares the
> operands directly (a > b ? a - b : b - a), optionally widened, as
> +/- abs (A - B).
>
> The same-width case is not folded under -fsanitize=signed-integer-overflow
> so the rewrite does not move the overflow the sanitizer reports.
>
> The transformation A </<= B ? A - B : B - A -> -abs (A - B) is only for
> widened operation. For same precision operands it can introduce new
> undefined behaviour for case a=0 and b=INT_MIN. (b-a) is defined for
> given values. But -abs(a-b) overflows.
>
> Transforming to abs(b-a) does not solve it either as for a=INT_MIN and
> b=0, (b-a) overflows.
>
>         PR tree-optimization/50856
>
> gcc/ChangeLog:
>
>         * match.pd (A CMP B ? A - B : B - A -> +/- abs (A - B)): New
>         patterns.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/pr50856.c: New test.
>         * g++.target/aarch64/pr50856.C: New test.
>
> Signed-off-by: Eikansh Gupta <[email protected]>
> ---
>  gcc/match.pd                               | 36 +++++++++++++++++++
>  gcc/testsuite/g++.target/aarch64/pr50856.C | 26 ++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/pr50856.c    | 41 ++++++++++++++++++++++
>  3 files changed, 103 insertions(+)
>  create mode 100644 gcc/testsuite/g++.target/aarch64/pr50856.C
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr50856.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index a7cec25dbad..0e9e52a3387 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -7154,6 +7154,42 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>        (convert (negate (absu:utype @0))))
>        (negate (abs @0)))))
>   )
> + /* A >/>= B ? A - B : B - A -> abs (A - B)
> +    with the operands optionally widened first.  */
> + (for cmp (gt ge)
> +  (simplify
> +   (cnd (cmp @0 @1)
> +       (minus@4 (convert1?@2 @0) (convert2?@3 @1))

I think you want either both converts or none, thus use (convert? ...)
in both cases.

> +       (minus @3 @2))
> +   (if (ANY_INTEGRAL_TYPE_P (type)
> +       && !TYPE_UNSIGNED (type)

In principle for a > b ? (unsigned)a - (unsigned)b : (unsigned)b - (unsigned)a
you could still match this to ABSU (a - b)?

> +       && types_match (TREE_TYPE (@0), TREE_TYPE (@1))

The compare (cmp @0 @1) should already guarantee this.

> +       && (element_precision (@0) < element_precision (type)
> +           /* The below checks are for same width operands.  */

what about truncatiions?

> +           || (types_match (TREE_TYPE (@0), type)
> +               && TYPE_OVERFLOW_UNDEFINED (type)
> +               /* Hoisting A - B out of the selector could move the
> +                  overflow the sanitizer reports.  */
> +               && !sanitize_flags_p (SANITIZE_SI_OVERFLOW)))
> +       && (!VECTOR_TYPE_P (type)
> +           || target_supports_op_p (type, ABS_EXPR, optab_vector)))
> +    (abs @4))))
> + /* A </<= B ? A - B : B - A -> -abs (A - B).  Widened operands only.

why not abs (B - A)?

I'd like to somehow merge both variants and gate not supported cases
on cmp == LT_EXPR || cmp == LE_EXPR.

> +    This pattern is only for widened operands as for same precision operands
> +    there can be a new undefined behaviour for case a=0 and b=INT_MIN.  */
> + (for cmp (lt le)
> +  (simplify
> +   (cnd (cmp @0 @1)
> +       (minus@4 (convert1?@2 @0) (convert2?@3 @1))
> +       (minus @3 @2))
> +   (if (ANY_INTEGRAL_TYPE_P (type)
> +       && !TYPE_UNSIGNED (type)
> +       && types_match (TREE_TYPE (@0), TREE_TYPE (@1))
> +       && element_precision (@0) < element_precision (type)
> +       && (!VECTOR_TYPE_P (type)
> +           || (target_supports_op_p (type, ABS_EXPR, optab_vector)
> +               && target_supports_op_p (type, NEGATE_EXPR, optab_vector))))
> +    (negate (abs @4)))))
>  )
>
>  /* -(type)!A -> (type)A - 1.  */
> diff --git a/gcc/testsuite/g++.target/aarch64/pr50856.C 
> b/gcc/testsuite/g++.target/aarch64/pr50856.C
> new file mode 100644
> index 00000000000..f10f16f3834
> --- /dev/null
> +++ b/gcc/testsuite/g++.target/aarch64/pr50856.C
> @@ -0,0 +1,26 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +/* vector a CMP b ? a - b : b - a -> abs(a - b).  Folds when the
> +   target has a vector abs.  */
> +
> +typedef short v8hi __attribute__ ((vector_size (16)));
> +typedef int v4si __attribute__ ((vector_size (16)));
> +
> +v8hi abd_h (v8hi a, v8hi b)
> +{
> +  return a > b ? a - b : b - a;
> +}
> +
> +v4si abd_s (v4si a, v4si b)
> +{
> +  return a >= b ? a - b : b - a;
> +}
> +
> +/* Same-width -abs must not fold.  */
> +v8hi nabd_h (v8hi a, v8hi b)
> +{
> +  return a < b ? a - b : b - a;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ABS_EXPR" 2 "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr50856.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/pr50856.c
> new file mode 100644
> index 00000000000..3c0513efbbd
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr50856.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +/* a CMP b ? a - b : b - a -> +/- abs (a - b).  */
> +
> +int gt_w (unsigned char a, unsigned char b)
> +{
> +  int x = a, y = b;
> +  return a > b ? x - y : y - x;
> +}
> +
> +int ge_w (unsigned char a, unsigned char b)
> +{
> +  int x = a, y = b;
> +  return a >= b ? x - y : y - x;
> +}
> +
> +int lt_w (unsigned char a, unsigned char b)
> +{
> +  int x = a, y = b;
> +  return a < b ? x - y : y - x;
> +}
> +
> +int le_w (unsigned char a, unsigned char b)
> +{
> +  int x = a, y = b;
> +  return a <= b ? x - y : y - x;
> +}
> +
> +int gt_i (int a, int b)
> +{
> +  return a > b ? a - b : b - a;
> +}
> +
> +/* Same-width -abs must not fold.  */
> +int lt_i (int a, int b)
> +{
> +  return a < b ? a - b : b - a;
> +}
> +
> +/* { dg-final { scan-tree-dump-times "ABS_EXPR" 5 "optimized" } } */
> --
> 2.34.1
>

Reply via email to