Follow-up to r17-3550, extending movement_possibility_1 to variable shift
counts as suggested in
https://gcc.gnu.org/pipermail/gcc-patches/2026-August/728576.html

Querying ranger turns out not to be safe here.  A contextual range need not
hold at the location the statement moves to, and a global range can be
derived from a __builtin_unreachable guard that has since been removed after
bounding only the name's uses (tree-vrp.cc, remove_unreachable): an earlier
version of this patch used the global range and hoisted a shift above such a
guard, miscompiling function k in the new test.  Instead accept a count
whose defining statement is a BIT_AND_EXPR with a constant mask below the
precision of the shifted type.  This covers s &= 7 directly, and s % 8 where
earlier passes rewrite it into that form.

Bootstrapped on x86_64-pc-linux-gnu; regression tested there with
gcc.dg/dg.exp and gcc.dg/tree-ssa/tree-ssa.exp, with no unexpected results.

Assisted-by: Claude Opus 5 (Anthropic)

gcc/ChangeLog:

        * tree-ssa-loop-im.cc (movement_possibility_1): Allow a shift
        or rotate by an SSA name whose defining statement masks the
        count below the precision of the shifted type.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-ssa/loop-im-shift-2.c: New test.

Signed-off-by: Dominic P <[email protected]>
---
 .../gcc.dg/tree-ssa/loop-im-shift-2.c         | 47 +++++++++++++++++++
 gcc/tree-ssa-loop-im.cc                       | 35 ++++++++++----
 2 files changed, 73 insertions(+), 9 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/loop-im-shift-2.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/loop-im-shift-2.c 
b/gcc/testsuite/gcc.dg/tree-ssa/loop-im-shift-2.c
new file mode 100644
index 00000000000..6555ef92ef4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/loop-im-shift-2.c
@@ -0,0 +1,47 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-lim2-details" } */
+
+/* The count is masked into range, so the whole chain may be hoisted.  */
+
+void f (int *p, int n, int a, unsigned int s, int c, int *q)
+{
+  s &= 7;
+  for (int i = 0; i < n; i++)
+    if (p[i] > c)
+      q[i] = ((a << s) * 5) / 7;
+}
+
+/* An unconstrained count keeps the chain in the loop.  */
+
+void g (int *p, int n, int a, unsigned int s, int c, int *q)
+{
+  for (int i = 0; i < n; i++)
+    if (p[i] > c)
+      q[i] = ((a << s) * 5) / 7;
+}
+
+/* A mask that does not bound the count below the precision.  */
+
+void h (int *p, int n, int a, unsigned int s, int c, int *q)
+{
+  s &= 63;
+  for (int i = 0; i < n; i++)
+    if (p[i] > c)
+      q[i] = ((a << s) * 5) / 7;
+}
+
+/* The global range of s comes from a guard the hoist would bypass.  */
+
+void k (int *p, int n, int a, unsigned int s, int c, int *q)
+{
+  for (int i = 0; i < n; i++)
+    if (p[i] > c)
+      {
+       if (s > 7) __builtin_unreachable ();
+       q[i] = ((a << s) * 5) / 7;
+      }
+}
+
+/* Only f's chain moves: the shift, the multiply and the divide.  */
+/* { dg-final { scan-tree-dump-times "Moving statement" 3 "lim2" } } */
+/* { dg-final { scan-tree-dump-times "Moving statement \[^\\r\\n\]*<<" 1 
"lim2" } } */
diff --git a/gcc/tree-ssa-loop-im.cc b/gcc/tree-ssa-loop-im.cc
index 0dcf5982ec2..a77241c8253 100644
--- a/gcc/tree-ssa-loop-im.cc
+++ b/gcc/tree-ssa-loop-im.cc
@@ -419,15 +419,32 @@ movement_possibility_1 (gimple *stmt)
       /* For shifts and rotates and possibly out-of-bound shift operands
         we currently cannot rewrite them into something unconditionally
         well-defined.  */
-      if ((code == LSHIFT_EXPR
-          || code == RSHIFT_EXPR
-          || code == LROTATE_EXPR
-          || code == RROTATE_EXPR)
-         && (TREE_CODE (gimple_assign_rhs2 (stmt)) != INTEGER_CST
-             /* We cannot use ranges at 'stmt' here.  */
-             || wi::geu_p (wi::to_wide (gimple_assign_rhs2 (stmt)),
-                           element_precision (type))))
-       ret = MOVE_PRESERVE_EXECUTION;
+      if (code == LSHIFT_EXPR
+         || code == RSHIFT_EXPR
+         || code == LROTATE_EXPR
+         || code == RROTATE_EXPR)
+       {
+         tree count = gimple_assign_rhs2 (stmt);
+         bool in_range = false;
+         if (TREE_CODE (count) == INTEGER_CST)
+           in_range = wi::ltu_p (wi::to_wide (count),
+                                 element_precision (type));
+         else if (TREE_CODE (count) == SSA_NAME)
+           {
+             /* We cannot use ranges at 'stmt' here, contextual or global;
+                only a bound implied by the definition of the count holds
+                where the statement is moved to.  */
+             gimple *def = SSA_NAME_DEF_STMT (count);
+             if (is_gimple_assign (def)
+                 && gimple_assign_rhs_code (def) == BIT_AND_EXPR
+                 && TREE_CODE (gimple_assign_rhs2 (def)) == INTEGER_CST
+                 && wi::ltu_p (wi::to_wide (gimple_assign_rhs2 (def)),
+                               element_precision (type)))
+               in_range = true;
+           }
+         if (!in_range)
+           ret = MOVE_PRESERVE_EXECUTION;
+       }
     }
 
   /* Non local loads in a transaction cannot be hoisted out.  Well,
-- 
2.55.0

Reply via email to