[llvm] [LV] Compute SCEV for memcheck before unlinking (PR #160326)

Igor Kirillov via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 23 08:36:07 PDT 2025


https://github.com/igogo-x86 created https://github.com/llvm/llvm-project/pull/160326

When generating runtime memory checks for outer loops, we split blocks and later unlink them, making the memcheck block unreachable. For instructions in unreachable blocks, ScalarEvolution returns an unknown/poison SCEV, which is treated as a constant and thus loop-invariant. The cost model then assumes the check can be hoisted and underestimates its cost. See this code in `GeneratedRTChecks::getCost`:

```
        const SCEV *Cond = SE->getSCEV(MemRuntimeCheckCond);
        if (SE->isLoopInvariant(Cond, OuterLoop)) {
```

Set OuterLoop early in GeneratedRTChecks::create and compute the SCEV for MemRuntimeCheckCond before unlinking, so getCost() sees the cached expression rather than a poison constant.

>From da4ec02c53cc549a22ae53792c8643a33d1a1062 Mon Sep 17 00:00:00 2001
From: Igor Kirillov <igor.kirillov at arm.com>
Date: Tue, 23 Sep 2025 15:27:40 +0000
Subject: [PATCH] [LV] Compute SCEV for memcheck before unlinking

When generating runtime memory checks for outer loops, we split
blocks and later unlink them, making the memcheck block unreachable.
For instructions in unreachable blocks, ScalarEvolution returns an
unknown/poison SCEV, which is treated as a constant and thus
loop-invariant. The cost model then assumes the check can be hoisted
and underestimates its cost.

Set OuterLoop early in GeneratedRTChecks::create and compute the
SCEV for MemRuntimeCheckCond before unlinking, so getCost() sees the
cached expression rather than a poison constant.
---
 llvm/lib/Transforms/Vectorize/LoopVectorize.cpp | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index ca092dcfcb492..575f45b051cf6 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -1807,6 +1807,10 @@ class GeneratedRTChecks {
     BasicBlock *LoopHeader = L->getHeader();
     BasicBlock *Preheader = L->getLoopPreheader();
 
+    // Outer loop is used as part of later cost calculations (e.g. to
+    // determine if runtime checks are loop-invariant and can be hoisted).
+    OuterLoop = L->getParentLoop();
+
     // Use SplitBlock to create blocks for SCEV & memory runtime checks to
     // ensure the blocks are properly added to LoopInfo & DominatorTree. Those
     // may be used by SCEVExpander. The blocks will be un-linked from their
@@ -1850,6 +1854,11 @@ class GeneratedRTChecks {
       assert(MemRuntimeCheckCond &&
              "no RT checks generated although RtPtrChecking "
              "claimed checks are required");
+      // Compute SCEV while the block is reachable.
+      // After unlinking, SCEV returns unknown/poison (constant -> invariant),
+      // which makes getCost() wrongly discount hoisted checks.
+      if (OuterLoop)
+        PSE.getSE()->getSCEV(MemRuntimeCheckCond);
     }
 
     SCEVExp.eraseDeadInstructions(SCEVCheckCond);
@@ -1889,8 +1898,6 @@ class GeneratedRTChecks {
       LI->removeBlock(SCEVCheckBlock);
     }
 
-    // Outer loop is used as part of the later cost calculations.
-    OuterLoop = L->getParentLoop();
   }
 
   InstructionCost getCost() {



More information about the llvm-commits mailing list