[llvm] AMDGPU: Delay value replacement in PromoteAlloca (PR #186944)

via llvm-commits llvm-commits at lists.llvm.org
Thu Mar 19 02:39:12 PDT 2026


https://github.com/ruiling updated https://github.com/llvm/llvm-project/pull/186944

>From 9d57df7d74f96be6aee9fe95051b9b2dc1a84d11 Mon Sep 17 00:00:00 2001
From: Ruiling Song <ruiling.song at amd.com>
Date: Tue, 17 Mar 2026 10:17:20 +0800
Subject: [PATCH 1/5] AMDGPU: Delay value replacement in PromoteAlloca

When we do alloca promotion, there might be cross references to the
values derived from different allocas. RAUW immediately during promotion
may fail to update the values cached in the AllocaAnalysis structure.

Solving the problem by postpone the value replacement, and also the
value deletion as well to make this possible.
---
 .../lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp | 90 +++++++++++--------
 .../promote-alloca-delay-value-replacement.ll | 29 ++++++
 2 files changed, 81 insertions(+), 38 deletions(-)
 create mode 100644 llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
index 120b2a0ef0baa..60f24793331f1 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
@@ -124,6 +124,25 @@ struct AllocaAnalysis {
   explicit AllocaAnalysis(AllocaInst *Alloca) : Alloca(Alloca) {}
 };
 
+// ValueReplacer was used to postpone the value replacement after all the
+// alloca's being processed. The postpone is needed to handle cross-alloca value
+// reference correctly.
+struct ValueReplacer {
+  // Keep record of the value replacement pair.
+  void replaceAllUsesWith(Value *Old, Value *New) {
+    ReplaceVec.push_back({Old, New});
+  }
+
+  // Do the actual value replacement.
+  void doReplacement() {
+    for (auto &[Old, New] : ReplaceVec)
+      Old->replaceAllUsesWith(New);
+  }
+
+private:
+  SmallVector<std::pair<Value *, Value *>> ReplaceVec;
+};
+
 // Shared implementation which can do both promotion to vector and to LDS.
 class AMDGPUPromoteAllocaImpl {
 private:
@@ -160,7 +179,7 @@ class AMDGPUPromoteAllocaImpl {
 
   FixedVectorType *getVectorTypeForAlloca(Type *AllocaTy) const;
   void analyzePromoteToVector(AllocaAnalysis &AA) const;
-  void promoteAllocaToVector(AllocaAnalysis &AA);
+  void promoteAllocaToVector(AllocaAnalysis &AA, ValueReplacer &VR);
   void analyzePromoteToLDS(AllocaAnalysis &AA) const;
   bool tryPromoteAllocaToLDS(AllocaAnalysis &AA, bool SufficientLDS,
                              SetVector<IntrinsicInst *> &DeferredIntrs);
@@ -420,6 +439,9 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
 
   bool Changed = false;
   SetVector<IntrinsicInst *> DeferredIntrs;
+  ValueReplacer VR;
+  SmallVector<Instruction *> ToBeErased;
+
   for (AllocaAnalysis &AA : Allocas) {
     if (AA.Vector.Ty) {
       std::optional<TypeSize> Size = AA.Alloca->getAllocationSize(*DL);
@@ -427,7 +449,13 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
       const unsigned AllocaCost = Size->getFixedValue() * 8;
       // First, check if we have enough budget to vectorize this alloca.
       if (AllocaCost <= VectorizationBudget) {
-        promoteAllocaToVector(AA);
+        promoteAllocaToVector(AA, VR);
+
+        ToBeErased.append(AA.Vector.Worklist);
+        // Append in reverse order so that further users would be erased first.
+        append_range(ToBeErased, reverse(AA.Vector.UsersToRemove));
+        ToBeErased.push_back(AA.Alloca);
+
         Changed = true;
         assert((VectorizationBudget - AllocaCost) < VectorizationBudget &&
                "Underflow!");
@@ -448,9 +476,13 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
   }
   finishDeferredAllocaToLDSPromotion(DeferredIntrs);
 
-  // NOTE: tryPromoteAllocaToVector removes the alloca, so Allocas contains
-  // dangling pointers. If we want to reuse it past this point, the loop above
-  // would need to be updated to remove successfully promoted allocas.
+
+  VR.doReplacement();
+  for (auto *I : ToBeErased) {
+    I->dropDroppableUses();
+    assert(I->use_empty());
+    I->eraseFromParent();
+  }
 
   return Changed;
 }
@@ -626,16 +658,15 @@ computeGEPToVectorIndex(GetElementPtrInst *GEP, AllocaInst *Alloca,
 /// \param VecStoreSize   Size of \p VectorTy in bytes.
 /// \param ElementSize    Size of \p VectorTy element type in bytes.
 /// \param CurVal         Current value of the vector (e.g. last stored value)
-/// \param[out]  DeferredLoads \p Inst is added to this vector if it can't
-///              be promoted now. This happens when promoting requires \p
-///              CurVal, but \p CurVal is nullptr.
+/// \param VR             Keep record of postponed value replacement.
 /// \return the stored value if \p Inst would have written to the alloca, or
 ///         nullptr otherwise.
 static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
                                         AllocaAnalysis &AA,
                                         unsigned VecStoreSize,
                                         unsigned ElementSize,
-                                        function_ref<Value *()> GetCurVal) {
+                                        function_ref<Value *()> GetCurVal,
+                                        ValueReplacer &VR) {
   // Note: we use InstSimplifyFolder because it can leverage the DataLayout
   // to do more folding, especially in the case of vector splats.
   IRBuilder<InstSimplifyFolder> Builder(Inst->getContext(),
@@ -655,8 +686,8 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
     TypeSize AccessSize = DL.getTypeStoreSize(AccessTy);
     if (Constant *CI = dyn_cast<Constant>(Index)) {
       if (CI->isNullValue() && AccessSize == VecStoreSize) {
-        Inst->replaceAllUsesWith(
-            Builder.CreateBitPreservingCastChain(DL, CurVal, AccessTy));
+        VR.replaceAllUsesWith(
+            Inst, Builder.CreateBitPreservingCastChain(DL, CurVal, AccessTy));
         return nullptr;
       }
     }
@@ -694,7 +725,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
             Index, ConstantInt::get(Index->getType(), LShrAmt));
         Value *ExtVal = Builder.CreateExtractElement(BCVal, NewIdx);
         Value *BCOut = Builder.CreateBitCast(ExtVal, AccessTy);
-        Inst->replaceAllUsesWith(BCOut);
+        VR.replaceAllUsesWith(Inst, BCOut);
         return nullptr;
       }
 
@@ -706,8 +737,8 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
             SubVec, Builder.CreateExtractElement(CurVal, CurIdx), K);
       }
 
-      Inst->replaceAllUsesWith(
-          Builder.CreateBitPreservingCastChain(DL, SubVec, AccessTy));
+      VR.replaceAllUsesWith(
+          Inst, Builder.CreateBitPreservingCastChain(DL, SubVec, AccessTy));
       return nullptr;
     }
 
@@ -715,8 +746,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
     Value *ExtractElement = Builder.CreateExtractElement(CurVal, Index);
     if (AccessTy != VecEltTy)
       ExtractElement = Builder.CreateBitOrPointerCast(ExtractElement, AccessTy);
-
-    Inst->replaceAllUsesWith(ExtractElement);
+    VR.replaceAllUsesWith(Inst, ExtractElement);
     return nullptr;
   }
   case Instruction::Store: {
@@ -806,9 +836,9 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
 
     if (auto *Intr = dyn_cast<IntrinsicInst>(Inst)) {
       if (Intr->getIntrinsicID() == Intrinsic::objectsize) {
-        Intr->replaceAllUsesWith(
-            Builder.getIntN(Intr->getType()->getIntegerBitWidth(),
-                            DL.getTypeAllocSize(AA.Vector.Ty)));
+        VR.replaceAllUsesWith(
+            Intr, Builder.getIntN(Intr->getType()->getIntegerBitWidth(),
+                                  DL.getTypeAllocSize(AA.Vector.Ty)));
         return nullptr;
       }
     }
@@ -1113,7 +1143,8 @@ void AMDGPUPromoteAllocaImpl::analyzePromoteToVector(AllocaAnalysis &AA) const {
   }
 }
 
-void AMDGPUPromoteAllocaImpl::promoteAllocaToVector(AllocaAnalysis &AA) {
+void AMDGPUPromoteAllocaImpl::promoteAllocaToVector(AllocaAnalysis &AA,
+                                                    ValueReplacer &VR) {
   LLVM_DEBUG(dbgs() << "Promoting to vectors: " << *AA.Alloca << '\n');
   LLVM_DEBUG(dbgs() << "  type conversion: " << *AA.Alloca->getAllocatedType()
                     << " -> " << *AA.Vector.Ty << '\n');
@@ -1160,7 +1191,7 @@ void AMDGPUPromoteAllocaImpl::promoteAllocaToVector(AllocaAnalysis &AA) {
     };
 
     Value *Result = promoteAllocaUserToVector(I, *DL, AA, VecStoreSize,
-                                              ElementSize, GetCurVal);
+                                              ElementSize, GetCurVal, VR);
     if (Result)
       Updater.AddAvailableValue(BB, Result);
   });
@@ -1180,23 +1211,6 @@ void AMDGPUPromoteAllocaImpl::promoteAllocaToVector(AllocaAnalysis &AA) {
       Placeholder->replaceAllUsesWith(PlaceholderToNewVal[Index]);
     Placeholder->eraseFromParent();
   }
-
-  // Delete all instructions.
-  for (Instruction *I : AA.Vector.Worklist) {
-    assert(I->use_empty());
-    I->eraseFromParent();
-  }
-
-  // Delete all the users that are known to be removeable.
-  for (Instruction *I : reverse(AA.Vector.UsersToRemove)) {
-    I->dropDroppableUses();
-    assert(I->use_empty());
-    I->eraseFromParent();
-  }
-
-  // Alloca should now be dead too.
-  assert(AA.Alloca->use_empty());
-  AA.Alloca->eraseFromParent();
 }
 
 std::pair<Value *, Value *>
diff --git a/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll b/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll
new file mode 100644
index 0000000000000..76ef877aea70b
--- /dev/null
+++ b/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll
@@ -0,0 +1,29 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -mtriple=amdgcn-unknown-unknown -passes=amdgpu-promote-alloca < %s | FileCheck %s
+
+define amdgpu_ps void @_amdgpu_ps_main() {
+; CHECK-LABEL: define amdgpu_ps void @_amdgpu_ps_main() {
+; CHECK-NEXT:  [[_ENTRY:.*:]]
+; CHECK-NEXT:    [[HIT_ORDERED:%.*]] = freeze <16 x float> poison
+; CHECK-NEXT:    [[HIT_INDEX:%.*]] = freeze <16 x i32> poison
+; CHECK-NEXT:    [[TMP0:%.*]] = insertelement <16 x i32> [[HIT_INDEX]], i32 0, i32 0
+; CHECK-NEXT:    br [[DOTLR_PH5:label %.*]]
+; CHECK:       [[_LR_PH5:.*:]]
+; CHECK-NEXT:    [[TMP1:%.*]] = extractelement <16 x i32> [[TMP0]], i32 0
+; CHECK-NEXT:    [[TMP2:%.*]] = insertelement <16 x float> [[HIT_ORDERED]], float 0.000000e+00, i32 [[TMP1]]
+; CHECK-NEXT:    ret void
+;
+.entry:
+  %hit_ordered = alloca [16 x float], align 4, addrspace(5)
+  %hit_index = alloca [16 x i32], align 4, addrspace(5)
+  store i32 0, ptr addrspace(5) %hit_index, align 4
+  br label %.lr.ph5
+
+  ; The separate block is needed to avoid constant-folding on
+  ; the load from %hit_index.
+.lr.ph5:                                          ; preds = %.entry
+  %0 = load i32, ptr addrspace(5) %hit_index, align 4
+  %1 = getelementptr float, ptr addrspace(5) %hit_ordered, i32 %0
+  store float 0.000000e+00, ptr addrspace(5) %1, align 4
+  ret void
+}

>From 6dea23c595354412ecd5bc80b8048de57dfb5e69 Mon Sep 17 00:00:00 2001
From: Ruiling Song <ruiling.song at amd.com>
Date: Tue, 17 Mar 2026 11:24:54 +0800
Subject: [PATCH 2/5] Fix clang-format error

---
 llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
index 60f24793331f1..b37c4aba528ba 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
@@ -476,7 +476,6 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
   }
   finishDeferredAllocaToLDSPromotion(DeferredIntrs);
 
-
   VR.doReplacement();
   for (auto *I : ToBeErased) {
     I->dropDroppableUses();

>From f513683b40ef7d3c35400a2ee47516b062bf9c11 Mon Sep 17 00:00:00 2001
From: Ruiling Song <ruiling.song at amd.com>
Date: Wed, 18 Mar 2026 13:57:28 +0800
Subject: [PATCH 3/5] Address review comments

---
 .../lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp | 53 +++++++++++--------
 .../promote-alloca-delay-value-replacement.ll | 23 ++++----
 2 files changed, 44 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
index b37c4aba528ba..1f743d0c5de66 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
@@ -124,23 +124,40 @@ struct AllocaAnalysis {
   explicit AllocaAnalysis(AllocaInst *Alloca) : Alloca(Alloca) {}
 };
 
-// ValueReplacer was used to postpone the value replacement after all the
-// alloca's being processed. The postpone is needed to handle cross-alloca value
-// reference correctly.
+// ValueReplacer was used to postpone the value replacement and erase dead
+// instructions after all the alloca's being processed. The postpone is needed
+// to handle cross-alloca value reference correctly.
 struct ValueReplacer {
   // Keep record of the value replacement pair.
-  void replaceAllUsesWith(Value *Old, Value *New) {
+  void postReplaceAllUsesWith(Value *Old, Value *New) {
     ReplaceVec.push_back({Old, New});
   }
 
-  // Do the actual value replacement.
-  void doReplacement() {
+  // Do the actual value replacement and erase dead instructions.
+  void replaceAndErase() {
     for (auto &[Old, New] : ReplaceVec)
       Old->replaceAllUsesWith(New);
+
+    for (auto *I : ErasableInstrs) {
+      I->dropDroppableUses();
+      assert(I->use_empty());
+      I->eraseFromParent();
+    }
+  }
+
+  // Mark an instruction as dead, and will be erased later. Note the order the
+  // instruction was inserted matters if there is possible cross references. The
+  // caller need to take care of this. The instructions will be deleted in the
+  // order they were added.
+  void postErase(Instruction *I) { ErasableInstrs.push_back(I); }
+
+  template <typename Range> void postErase(const Range &R) {
+    append_range(ErasableInstrs, R);
   }
 
 private:
   SmallVector<std::pair<Value *, Value *>> ReplaceVec;
+  SmallVector<Instruction *> ErasableInstrs;
 };
 
 // Shared implementation which can do both promotion to vector and to LDS.
@@ -451,10 +468,10 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
       if (AllocaCost <= VectorizationBudget) {
         promoteAllocaToVector(AA, VR);
 
-        ToBeErased.append(AA.Vector.Worklist);
+        VR.postErase(AA.Vector.Worklist);
         // Append in reverse order so that further users would be erased first.
-        append_range(ToBeErased, reverse(AA.Vector.UsersToRemove));
-        ToBeErased.push_back(AA.Alloca);
+        VR.postErase(reverse(AA.Vector.UsersToRemove));
+        VR.postErase(AA.Alloca);
 
         Changed = true;
         assert((VectorizationBudget - AllocaCost) < VectorizationBudget &&
@@ -476,13 +493,7 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
   }
   finishDeferredAllocaToLDSPromotion(DeferredIntrs);
 
-  VR.doReplacement();
-  for (auto *I : ToBeErased) {
-    I->dropDroppableUses();
-    assert(I->use_empty());
-    I->eraseFromParent();
-  }
-
+  VR.replaceAndErase();
   return Changed;
 }
 
@@ -685,7 +696,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
     TypeSize AccessSize = DL.getTypeStoreSize(AccessTy);
     if (Constant *CI = dyn_cast<Constant>(Index)) {
       if (CI->isNullValue() && AccessSize == VecStoreSize) {
-        VR.replaceAllUsesWith(
+        VR.postReplaceAllUsesWith(
             Inst, Builder.CreateBitPreservingCastChain(DL, CurVal, AccessTy));
         return nullptr;
       }
@@ -724,7 +735,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
             Index, ConstantInt::get(Index->getType(), LShrAmt));
         Value *ExtVal = Builder.CreateExtractElement(BCVal, NewIdx);
         Value *BCOut = Builder.CreateBitCast(ExtVal, AccessTy);
-        VR.replaceAllUsesWith(Inst, BCOut);
+        VR.postReplaceAllUsesWith(Inst, BCOut);
         return nullptr;
       }
 
@@ -736,7 +747,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
             SubVec, Builder.CreateExtractElement(CurVal, CurIdx), K);
       }
 
-      VR.replaceAllUsesWith(
+      VR.postReplaceAllUsesWith(
           Inst, Builder.CreateBitPreservingCastChain(DL, SubVec, AccessTy));
       return nullptr;
     }
@@ -745,7 +756,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
     Value *ExtractElement = Builder.CreateExtractElement(CurVal, Index);
     if (AccessTy != VecEltTy)
       ExtractElement = Builder.CreateBitOrPointerCast(ExtractElement, AccessTy);
-    VR.replaceAllUsesWith(Inst, ExtractElement);
+    VR.postReplaceAllUsesWith(Inst, ExtractElement);
     return nullptr;
   }
   case Instruction::Store: {
@@ -835,7 +846,7 @@ static Value *promoteAllocaUserToVector(Instruction *Inst, const DataLayout &DL,
 
     if (auto *Intr = dyn_cast<IntrinsicInst>(Inst)) {
       if (Intr->getIntrinsicID() == Intrinsic::objectsize) {
-        VR.replaceAllUsesWith(
+        VR.postReplaceAllUsesWith(
             Intr, Builder.getIntN(Intr->getType()->getIntegerBitWidth(),
                                   DL.getTypeAllocSize(AA.Vector.Ty)));
         return nullptr;
diff --git a/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll b/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll
index 76ef877aea70b..3ec33e1f5519c 100644
--- a/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll
+++ b/llvm/test/CodeGen/AMDGPU/promote-alloca-delay-value-replacement.ll
@@ -1,16 +1,17 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; RUN: opt -S -mtriple=amdgcn-unknown-unknown -passes=amdgpu-promote-alloca < %s | FileCheck %s
 
-define amdgpu_ps void @_amdgpu_ps_main() {
-; CHECK-LABEL: define amdgpu_ps void @_amdgpu_ps_main() {
+define void @alloca_value_cross_reference() {
+; CHECK-LABEL: define void @alloca_value_cross_reference() {
 ; CHECK-NEXT:  [[_ENTRY:.*:]]
-; CHECK-NEXT:    [[HIT_ORDERED:%.*]] = freeze <16 x float> poison
-; CHECK-NEXT:    [[HIT_INDEX:%.*]] = freeze <16 x i32> poison
-; CHECK-NEXT:    [[TMP0:%.*]] = insertelement <16 x i32> [[HIT_INDEX]], i32 0, i32 0
+; CHECK-NEXT:    [[HIT_ORDERED:%.*]] = alloca [16 x float], align 4, addrspace(5)
+; CHECK-NEXT:    [[HIT_INDEX:%.*]] = alloca [16 x i32], align 4, addrspace(5)
+; CHECK-NEXT:    store i32 0, ptr addrspace(5) [[HIT_INDEX]], align 4
 ; CHECK-NEXT:    br [[DOTLR_PH5:label %.*]]
 ; CHECK:       [[_LR_PH5:.*:]]
-; CHECK-NEXT:    [[TMP1:%.*]] = extractelement <16 x i32> [[TMP0]], i32 0
-; CHECK-NEXT:    [[TMP2:%.*]] = insertelement <16 x float> [[HIT_ORDERED]], float 0.000000e+00, i32 [[TMP1]]
+; CHECK-NEXT:    [[I:%.*]] = load i32, ptr addrspace(5) [[HIT_INDEX]], align 4
+; CHECK-NEXT:    [[P:%.*]] = getelementptr float, ptr addrspace(5) [[HIT_ORDERED]], i32 [[I]]
+; CHECK-NEXT:    store float 0.000000e+00, ptr addrspace(5) [[P]], align 4
 ; CHECK-NEXT:    ret void
 ;
 .entry:
@@ -21,9 +22,9 @@ define amdgpu_ps void @_amdgpu_ps_main() {
 
   ; The separate block is needed to avoid constant-folding on
   ; the load from %hit_index.
-.lr.ph5:                                          ; preds = %.entry
-  %0 = load i32, ptr addrspace(5) %hit_index, align 4
-  %1 = getelementptr float, ptr addrspace(5) %hit_ordered, i32 %0
-  store float 0.000000e+00, ptr addrspace(5) %1, align 4
+.lr.ph5:
+  %i = load i32, ptr addrspace(5) %hit_index, align 4
+  %p = getelementptr float, ptr addrspace(5) %hit_ordered, i32 %i
+  store float 0.000000e+00, ptr addrspace(5) %p, align 4
   ret void
 }

>From 536ff85cd530b788b3e0a912998745ca6086e7ce Mon Sep 17 00:00:00 2001
From: Ruiling Song <ruiling.song at amd.com>
Date: Wed, 18 Mar 2026 14:20:08 +0800
Subject: [PATCH 4/5] use emplace_back() and fix build error

---
 llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
index 1f743d0c5de66..6498bd6af069d 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
@@ -130,7 +130,7 @@ struct AllocaAnalysis {
 struct ValueReplacer {
   // Keep record of the value replacement pair.
   void postReplaceAllUsesWith(Value *Old, Value *New) {
-    ReplaceVec.push_back({Old, New});
+    ReplaceVec.emplace_back(Old, New);
   }
 
   // Do the actual value replacement and erase dead instructions.
@@ -151,7 +151,7 @@ struct ValueReplacer {
   // order they were added.
   void postErase(Instruction *I) { ErasableInstrs.push_back(I); }
 
-  template <typename Range> void postErase(const Range &R) {
+  template <typename Range> void postEraseRange(const Range &R) {
     append_range(ErasableInstrs, R);
   }
 
@@ -468,9 +468,9 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
       if (AllocaCost <= VectorizationBudget) {
         promoteAllocaToVector(AA, VR);
 
-        VR.postErase(AA.Vector.Worklist);
+        VR.postEraseRange(AA.Vector.Worklist);
         // Append in reverse order so that further users would be erased first.
-        VR.postErase(reverse(AA.Vector.UsersToRemove));
+        VR.postEraseRange(reverse(AA.Vector.UsersToRemove));
         VR.postErase(AA.Alloca);
 
         Changed = true;

>From 3939c0c53095cb71e0c906f3e24cd969d167844b Mon Sep 17 00:00:00 2001
From: Ruiling Song <ruiling.song at amd.com>
Date: Thu, 19 Mar 2026 17:34:01 +0800
Subject: [PATCH 5/5] overload postErase properly

---
 llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
index 6498bd6af069d..acff367e646cd 100644
--- a/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
+++ b/llvm/lib/Target/AMDGPU/AMDGPUPromoteAlloca.cpp
@@ -151,7 +151,11 @@ struct ValueReplacer {
   // order they were added.
   void postErase(Instruction *I) { ErasableInstrs.push_back(I); }
 
-  template <typename Range> void postEraseRange(const Range &R) {
+  void postErase(const SmallVectorImpl<Instruction *> &C) {
+    ErasableInstrs.append(C);
+  }
+
+  template <typename RangeTy> void postErase(const iterator_range<RangeTy> &R) {
     append_range(ErasableInstrs, R);
   }
 
@@ -468,9 +472,9 @@ bool AMDGPUPromoteAllocaImpl::run(Function &F, bool PromoteToLDS) {
       if (AllocaCost <= VectorizationBudget) {
         promoteAllocaToVector(AA, VR);
 
-        VR.postEraseRange(AA.Vector.Worklist);
+        VR.postErase(AA.Vector.Worklist);
         // Append in reverse order so that further users would be erased first.
-        VR.postEraseRange(reverse(AA.Vector.UsersToRemove));
+        VR.postErase(reverse(AA.Vector.UsersToRemove));
         VR.postErase(AA.Alloca);
 
         Changed = true;



More information about the llvm-commits mailing list