[llvm] [VectorCombine] Fix duplicate freeze scheduling for shared indexes (PR #213461)

via llvm-commits llvm-commits at lists.llvm.org
Sat Aug 1 09:02:04 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-vectorizers

Author: Aditya prakash jha (AdityaOP007)

<details>
<summary>Changes</summary>

## Summary
#<!-- -->213423 

This PR fixes a crash in `VectorCombine::scalarizeLoadExtract()` when multiple `extractelement` instructions share the same index instruction.

The issue was caused by `NeedFreeze` tracking freeze requests per `ExtractElementInst`, which could schedule duplicate `freeze()` operations for the same index. After the first freeze updated the shared index, processing the second request used stale state and triggered the `is_contained(ToFreeze->users(), &UserI)` assertion.

The fix tracks freeze requests using the shared index instruction instead, ensuring each index is frozen only once while preserving the existing assertion and use-def invariants.

A regression test has also been added to cover the shared-index case and prevent future regressions.


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


4 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/VectorCombine.cpp (+9-6) 
- (added) llvm/test/Transforms/VectorCombine/X86/pr213423.ll (+33) 
- (added) test.ll (+5) 
- (added) test2.ll (+7) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 726f564b1aad9..2c1143b0957a6 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -1929,7 +1929,7 @@ static ScalarizationResult canScalarizeAccess(VectorType *VecTy, Value *Idx,
 
   // If the index may be poison, check if we can insert a freeze before the
   // range of the index is restricted.
-  Value *IdxBase;
+  Value *IdxBase = nullptr;
   ConstantInt *CI;
   if (match(Idx, m_And(m_Value(IdxBase), m_ConstantInt(CI)))) {
     IdxRange = IdxRange.binaryAnd(CI->getValue());
@@ -1937,7 +1937,7 @@ static ScalarizationResult canScalarizeAccess(VectorType *VecTy, Value *Idx,
     IdxRange = IdxRange.urem(CI->getValue());
   }
 
-  if (ValidIndices.contains(IdxRange))
+  if (IdxBase && ValidIndices.contains(IdxRange))
     return ScalarizationResult::safeWithFreeze(IdxBase);
   return ScalarizationResult::unsafe();
 }
@@ -2086,7 +2086,7 @@ bool VectorCombine::scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy,
   if (!TTI.allowVectorElementIndexingUsingGEP())
     return false;
 
-  DenseMap<ExtractElementInst *, ScalarizationResult> NeedFreeze;
+  DenseMap<Instruction *, ScalarizationResult> NeedFreeze;
   llvm::scope_exit FailureGuard([&]() {
     // If the transform is aborted, discard the ScalarizationResults.
     for (auto &Pair : NeedFreeze)
@@ -2106,7 +2106,8 @@ bool VectorCombine::scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy,
     if (ScalarIdx.isUnsafe())
       return false;
     if (ScalarIdx.isSafeWithFreeze()) {
-      NeedFreeze.try_emplace(UI, ScalarIdx);
+      NeedFreeze.try_emplace(cast<Instruction>(UI->getIndexOperand()),
+                             ScalarIdx);
       ScalarIdx.discard();
     }
 
@@ -2140,9 +2141,11 @@ bool VectorCombine::scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy,
     Value *Idx = EI->getIndexOperand();
 
     // Insert 'freeze' for poison indexes.
-    auto It = NeedFreeze.find(EI);
-    if (It != NeedFreeze.end())
+    auto It = NeedFreeze.find(cast<Instruction>(Idx));
+    if (It != NeedFreeze.end()) {
       It->second.freeze(Builder, *cast<Instruction>(Idx));
+      NeedFreeze.erase(It);
+    }
 
     Builder.SetInsertPoint(EI);
     Value *GEP =
diff --git a/llvm/test/Transforms/VectorCombine/X86/pr213423.ll b/llvm/test/Transforms/VectorCombine/X86/pr213423.ll
new file mode 100644
index 0000000000000..2b932bca422d6
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/X86/pr213423.ll
@@ -0,0 +1,33 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt < %s -passes=vector-combine -S | FileCheck %s
+
+; Issue #213423
+; Check that we do not crash when multiple extracts share the same index instruction
+; and it needs to be frozen.
+define void @test_shared_index(ptr %p, i32 %idx.base) {
+; CHECK-LABEL: @test_shared_index(
+; CHECK-NEXT:    [[IDX:%.*]] = and i32 [[IDX_BASE:%.*]], 1
+; CHECK-NEXT:    [[V:%.*]] = load <2 x i32>, ptr [[P:%.*]], align 8
+; CHECK-NEXT:    [[E1:%.*]] = extractelement <2 x i32> [[V]], i32 [[IDX]]
+; CHECK-NEXT:    [[E2:%.*]] = extractelement <2 x i32> [[V]], i32 [[IDX]]
+; CHECK-NEXT:    ret void
+;
+  %idx = and i32 %idx.base, 1
+  %v = load <2 x i32>, ptr %p
+  %e1 = extractelement <2 x i32> %v, i32 %idx
+  %e2 = extractelement <2 x i32> %v, i32 %idx
+  ret void
+}
+
+; Check that we do not use an uninitialized pointer when the index doesn't match and/urem
+; but is naturally in bounds.
+define void @test_uninitialized_base(ptr %p, i1 %idx) {
+; CHECK-LABEL: @test_uninitialized_base(
+; CHECK-NEXT:    [[V:%.*]] = load <2 x i32>, ptr [[P:%.*]], align 8
+; CHECK-NEXT:    [[E:%.*]] = extractelement <2 x i32> [[V]], i1 [[IDX:%.*]]
+; CHECK-NEXT:    ret void
+;
+  %v = load <2 x i32>, ptr %p
+  %e = extractelement <2 x i32> %v, i1 %idx
+  ret void
+}
diff --git a/test.ll b/test.ll
new file mode 100644
index 0000000000000..cc7d1e1111b31
--- /dev/null
+++ b/test.ll
@@ -0,0 +1,5 @@
+define void @test(ptr %p, i1 %idx) {
+  %v = load <2 x i32>, ptr %p
+  %e = extractelement <2 x i32> %v, i1 %idx
+  ret void
+}
diff --git a/test2.ll b/test2.ll
new file mode 100644
index 0000000000000..3b5e2b0a42912
--- /dev/null
+++ b/test2.ll
@@ -0,0 +1,7 @@
+define void @test(ptr %p, i32 %idx.base) {
+  %idx = and i32 %idx.base, 1
+  %v = load <2 x i32>, ptr %p
+  %e1 = extractelement <2 x i32> %v, i32 %idx
+  %e2 = extractelement <2 x i32> %v, i32 %idx
+  ret void
+}

``````````

</details>


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


More information about the llvm-commits mailing list