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
>