[PATCH 2/2] match.pd: fold the promoted spelling of abs-difference [PR50856]

Dominic P <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
A > B ? A - B : B - A folds to abs (A - B) (PR50856), but only when the
comparison and the subtraction see the same operands.  In the ubiquitous
C spelling over sub-int types,

  int f (unsigned char a, unsigned char b) { return a > b ? a-b : b-a; }

the front end narrows the promoted comparison back to unsigned char
while the subtractions stay in int, so the compare tests A' and B' and
the arms compute (int) A' - (int) B': different operands, and the fold
never matches.  The conditional survives to RTL as compare-and-select.

Match the widened arms explicitly: a strictly widening conversion to
the signed result type preserves the comparison's order (it is a zero-
or sign-extension, so it preserves values), and the widened difference
cannot overflow since both operands fit in the narrower precision.  A
same-width conversion is excluded: reinterpretation changes the order.

This is the scalar shape of every byte sum-of-absolute-differences
loop.  With the fold, such a loop vectorises through ABS_EXPR and the
vectoriser's abd patterns: on aarch64 the loop body becomes
the uabd family (uabdl2/uabal/uadalp, by accumulator shape) where it
was compare, two usubl pairs and a select.

Bootstrapped on x86_64-pc-linux-gnu and regtested with gcc.dg/dg.exp
and gcc.dg/tree-ssa/tree-ssa.exp: no unexpected results.  Without the
fold the new test's ABS_EXPR scans fail (0 instead of 4).

Assisted-by: Claude Fable 5 (Anthropic)

gcc/ChangeLog:

	PR tree-optimization/50856
	* match.pd (A' > B' ? (T) A' - (T) B' : (T) B' - (T) A'): New
	pattern, and its lt/le negated-abs counterpart.

gcc/testsuite/ChangeLog:

	PR tree-optimization/50856
	* gcc.dg/tree-ssa/absdiff-widen-1.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
 gcc/match.pd                                  | 32 +++++++++++++++++++
 .../gcc.dg/tree-ssa/absdiff-widen-1.c         | 20 ++++++++++++
 2 files changed, 52 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/absdiff-widen-1.c

diff --git a/gcc/match.pd b/gcc/match.pd
index 04ddb82c76e..ec5293cca47 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -7627,6 +7627,38 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
 	    || (target_supports_op_p (type, ABS_EXPR, optab_vector)
 		&& target_supports_op_p (type, NEGATE_EXPR, optab_vector))))
     (negate (abs @0)))))
+ /* The same with the comparison done on the narrow values whose widening
+    feeds the subtraction:
+      A' > B' ? (T) A' - (T) B' : (T) B' - (T) A'  ->  abs ((T) A' - (T) B')
+    which is the promoted-byte spelling of abs-difference (the C SAD
+    idiom: the front end narrows the promoted comparison back to the
+    original type, so the compare and the subtraction see different
+    operands).  A strictly widening conversion to the signed result type
+    preserves the comparison's order -- zero- or sign-extension keeps
+    values and hence order -- and the widened difference cannot overflow,
+    since both values fit in the narrower precision.  */
+ (for cmp (gt ge)
+  (simplify
+   (cnd (cmp @1 @2) (minus@0 (convert @1) (convert @2))
+		    (minus (convert @2) (convert @1)))
+   (if (!HONOR_SIGNED_ZEROS (type)
+	&& !TYPE_UNSIGNED (type)
+	&& INTEGRAL_TYPE_P (type)
+	&& !TYPE_OVERFLOW_WRAPS (type)
+	&& INTEGRAL_TYPE_P (TREE_TYPE (@1))
+	&& TYPE_PRECISION (TREE_TYPE (@1)) < TYPE_PRECISION (type))
+    (abs @0))))
+ (for cmp (lt le)
+  (simplify
+   (cnd (cmp @1 @2) (minus@0 (convert @1) (convert @2))
+		    (minus (convert @2) (convert @1)))
+   (if (!HONOR_SIGNED_ZEROS (type)
+	&& !TYPE_UNSIGNED (type)
+	&& INTEGRAL_TYPE_P (type)
+	&& !TYPE_OVERFLOW_WRAPS (type)
+	&& INTEGRAL_TYPE_P (TREE_TYPE (@1))
+	&& TYPE_PRECISION (TREE_TYPE (@1)) < TYPE_PRECISION (type))
+    (negate (abs @0)))))
 )
 
 /* X >=/> 0 ? Y + X : Y - X
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/absdiff-widen-1.c b/gcc/testsuite/gcc.dg/tree-ssa/absdiff-widen-1.c
new file mode 100644
index 00000000000..18b03c203d9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/absdiff-widen-1.c
@@ -0,0 +1,20 @@
+/* The promoted abs-difference idiom: the front end narrows the promoted
+   comparison back to the original type, so the compare and the subtraction
+   see different operands and the plain PR50856 fold cannot match.  A
+   strictly widening conversion preserves the comparison's order and the
+   widened difference cannot overflow, so this is abs (A - B) too.  This is
+   the C spelling of byte SAD loops.  */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+int adu (unsigned char a, unsigned char b) { return a > b ? a - b : b - a; }
+int ads (signed char a, signed char b) { return a > b ? a - b : b - a; }
+int adr (unsigned char a, unsigned char b) { return a < b ? b - a : a - b; }
+int adn (unsigned char a, unsigned char b) { return a < b ? a - b : b - a; }
+
+/* Same-width reinterpretation must not fold: the conversion changes the
+   comparison's order.  */
+int nm1 (unsigned a, unsigned b) { return a > b ? (int)a - (int)b : (int)b - (int)a; }
+
+/* { dg-final { scan-tree-dump-times "ABS_EXPR" 4 "optimized" } } */
+/* { dg-final { scan-tree-dump-times "= -" 1 "optimized" } } */
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.