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) ? x : y

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

a !=/== 0 ? x : y

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 (`((a OP b) + c eq|ne c) ? x : y`): New pattern.

gcc/testsuite/ChangeLog:

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

Signed-off-by: Daniel Barboza <[email protected]>
---
 gcc/match.pd                                  | 17 +++++++++++++
 .../gcc.dg/torture/ifcond-plus-shift-czero.c  | 25 +++++++++++++++++++
 2 files changed, 42 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 ccdc1129e23..fdc18b1ef41 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -2824,6 +2824,23 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
        (le (minus (convert:etype @0) { lo; }) { hi; })
        (gt (minus (convert:etype @0) { lo; }) { hi; })))))))))
 
+/* Reduces ((A OP B) + C EQ|NE C) ? X : Y to
+   A EQ|NE 0 ? x : y, for OP = (lshift, rshift).  */
+(for cmp (eq ne)
+ (for op (lshift rshift)
+  (simplify
+   (cond (cmp:c (plus:c (op @1 @2) @0) @0) @3 @4)
+    (if (ANY_INTEGRAL_TYPE_P (TREE_TYPE (@1))
+        && TYPE_OVERFLOW_UNDEFINED (TREE_TYPE (@1)))
+     (with
+      {
+       fold_overflow_warning ("assuming signed overflow does not occur "
+                             "when changing (X << imm) + C1 cmp C1 to "
+                             "X << imm cmp 0 and X << imm cmp 0 "
+                             "to X cmp 0",
+                             WARN_STRICT_OVERFLOW_COMPARISON);  }
+     (cond (cmp @1 { build_zero_cst (type); }) @3 @4))))))
+
 /* X + Z < Y + Z is the same as X < Y when there is no overflow.  */
 (for op (lt le ge gt)
  (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