Add a transformation for a nested lshift/rshift inside a plus that
compares for equality with the same operand of the plus. In other words:

((a OP b) + c EQ|NE c)

becomes, for OP = (lshift, rshift) and in a scenario without overflows:

a EQ|NE 0

This optimizes the following code, seen in perlbench:

long
frob(long x, long y, long z)
{
  long ret = (y << 2) + z;
  long cond = ret != z;
  if (cond == 0)
    ret = 0;
  return ret;
}

gcc/ChangeLog:

        * match.pd (`(cmp ((a OP b) + c) c`): New pattern.

gcc/testsuite/ChangeLog:

        * gcc.dg/torture/ifcond-plus-shift-czero.c: New test.

Signed-off-by: Daniel Barboza <[email protected]>
---

Changes from v1:
- Use an existing 'cmp' only pattern as base instead of adding a new
  pattern that includes the conditional
- v1 link: https://gcc.gnu.org/pipermail/gcc-patches/2026-January/704992.html


 gcc/match.pd                                  | 13 ++++++++++
 .../gcc.dg/torture/ifcond-plus-shift-czero.c  | 25 +++++++++++++++++++
 2 files changed, 38 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c

diff --git a/gcc/match.pd b/gcc/match.pd
index 08d0810e986..27ee5c2ea6b 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -2946,6 +2946,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        && (CONSTANT_CLASS_P (@1) || (single_use (@2) && single_use (@3))))
    (op @1 { build_zero_cst (TREE_TYPE (@1)); }))))
 
+/* Do the same but with lshift|rshift as @0.  */
+(for cmp (eq ne)
+ (for op (lshift rshift)
+  (simplify
+   (cmp:c (nop_convert?@3 (plus:c@2 (op @0 @4) (convert1? @1))) (convert2? @1))
+   (with { tree type0 = TREE_TYPE (@0);  }
+   (if (ANY_INTEGRAL_TYPE_P (type0)
+       && (TYPE_OVERFLOW_UNDEFINED (type0)
+           || TYPE_OVERFLOW_WRAPS (type0))
+       && tree_nop_conversion_p (TREE_TYPE (@3), TREE_TYPE (@2))
+       && tree_nop_conversion_p (TREE_TYPE (@3), TREE_TYPE (@1)))
+    (cmp @0 { build_zero_cst (type0); }))))))
+
 /* (&a + b) !=/== (&a[1] + c) -> (&a[0] - &a[1]) + b !=/== c */
 (for neeq (ne eq)
  (simplify
diff --git a/gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c 
b/gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c
new file mode 100644
index 00000000000..769412202c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifcond-plus-shift-czero.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-phiopt2" } */
+/* { dg-skip-if "" { *-*-* } { "-O0" "-fno-fat-lto-objects"  } { "" } } */
+
+long
+f1 (long x, long y, long z)
+{
+  long ret = (y << 2) + z;
+  long cond = ret != z;
+  if (cond == 0) 
+    ret = 0;
+  return ret;
+}
+
+long
+f2 (long x, long y, long z)
+{
+  long ret = (y >> 2) + z;
+  long cond = ret != z;
+  if (cond == 0) 
+    ret = 0;
+  return ret;
+}
+
+/* { dg-final { scan-tree-dump-times "== 0" 2 "phiopt2" } } */ 
-- 
2.43.0

Reply via email to