[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