[llvm] [LoopVectorize] Fix crash with GC statepoint in epilogue vectorization (PR #179459)

via llvm-commits llvm-commits at lists.llvm.org
Tue Feb 3 05:41:09 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-vectorizers

Author: Luohao Wang (Luohaothu)

<details>
<summary>Changes</summary>

Fixes #<!-- -->179407.

When vectorizing loops in functions with GC statepoint, `fixScalarResumeValuesFromBypass` assumed that `IVPhi->getIncomingValueForBlock(PH)` always returns a PHINode. However, in functions with GC statepoint, the preheader may be simplified and the incoming value could be a constant or other non-PHINode value, causing an assertion failure:
  "isa<> used on a null pointer"

The fix uses dyn_cast instead of cast to safely check the type, and when the incoming value is not a PHINode, creates a new PHI node in the preheader to merge the original value and the bypass value.

---
Full diff: https://github.com/llvm/llvm-project/pull/179459.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/LoopVectorize.cpp (+21-3) 
- (added) llvm/test/Transforms/LoopVectorize/pr179407-gc-statepoint.ll (+40) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
index abac45b265d10..2581353f900bf 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorize.cpp
@@ -9361,12 +9361,30 @@ static void fixScalarResumeValuesFromBypass(BasicBlock *BypassBlock, Loop *L,
   // Fix induction resume values from the additional bypass block.
   IRBuilder<> BypassBuilder(BypassBlock, BypassBlock->getFirstInsertionPt());
   for (const auto &[IVPhi, II] : LVL.getInductionVars()) {
-    auto *Inc = cast<PHINode>(IVPhi->getIncomingValueForBlock(PH));
+    Value *IncomingFromPH = IVPhi->getIncomingValueForBlock(PH);
     Value *V = createInductionAdditionalBypassValues(
         IVPhi, II, BypassBuilder, ExpandedSCEVs, MainVectorTripCount,
         LVL.getPrimaryInduction());
-    // TODO: Directly add as extra operand to the VPResumePHI recipe.
-    Inc->setIncomingValueForBlock(BypassBlock, V);
+
+    if (auto *Inc = dyn_cast<PHINode>(IncomingFromPH)) {
+      // TODO: Directly add as extra operand to the VPResumePHI recipe.
+      Inc->setIncomingValueForBlock(BypassBlock, V);
+    } else {
+      // If the incoming value from preheader is not a PHI node (e.g., a constant
+      // in functions with GC statepoint), we need to create a PHI node in the
+      // preheader that merges the original value and the bypass value, then update
+      // IVPhi to use this new PHI node.
+      PHINode *NewPhi = PHINode::Create(IncomingFromPH->getType(), 2,
+                                        IVPhi->getName() + ".phi.merge", PH->getFirstNonPHIIt());
+      for (BasicBlock *Pred : predecessors(PH)) {
+        if (Pred == BypassBlock)
+          NewPhi->addIncoming(V, BypassBlock);
+        else
+          NewPhi->addIncoming(IncomingFromPH, Pred);
+      }
+      // Update IVPhi to use the new PHI node for the incoming value from PH.
+      IVPhi->setIncomingValueForBlock(PH, NewPhi);
+    }
   }
 }
 
diff --git a/llvm/test/Transforms/LoopVectorize/pr179407-gc-statepoint.ll b/llvm/test/Transforms/LoopVectorize/pr179407-gc-statepoint.ll
new file mode 100644
index 0000000000000..a2c33b3f1a702
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/pr179407-gc-statepoint.ll
@@ -0,0 +1,40 @@
+; RUN: opt -passes=loop-vectorize -enable-epilogue-vectorization -force-vector-width=2 -epilogue-vectorization-force-VF=2 -S < %s | FileCheck %s
+;
+; Test case for issue #179407: LoopVectorize should not crash when
+; vectorizing loops in functions with GC statepoint.
+;
+; The issue was that fixScalarResumeValuesFromBypass assumed
+; IVPhi->getIncomingValueForBlock(PH) always returns a PHINode, but in
+; functions with GC statepoint, it could return a constant or other value.
+;
+; This test verifies that epilogue vectorization works correctly with
+; GC statepoint by checking that the loop is vectorized and the
+; bypass blocks are properly created.
+
+; CHECK: define void @wombat
+; CHECK-SAME: gc "statepoint-example"
+; CHECK: vector.body:
+; CHECK: vec.epilog.vector.body:
+; CHECK: vec.epilog.scalar.ph:
+; CHECK: bb1:
+
+define void @wombat(i64 %arg) gc "statepoint-example" {
+bb:
+  br label %bb1
+
+bb1:                                              ; preds = %bb1, %bb
+  %phi = phi i64 [ 0, %bb ], [ %add, %bb1 ]
+  %phi2 = phi i32 [ 0, %bb ], [ %or, %bb1 ]
+  %phi3 = phi i32 [ 0, %bb ], [ %select, %bb1 ]
+  %icmp = icmp eq i32 0, 0
+  %select = select i1 %icmp, i32 0, i32 %phi3
+  %or = or i32 %phi2, 0
+  %add = add i64 %phi, 1
+  %icmp4 = icmp ult i64 %phi, %arg
+  br i1 %icmp4, label %bb1, label %bb5
+
+bb5:                                              ; preds = %bb1
+  %phi6 = phi i32 [ %select, %bb1 ]
+  %phi7 = phi i32 [ %or, %bb1 ]
+  ret void
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/179459


More information about the llvm-commits mailing list