[llvm] [NFC][SPIR-V] Fuse redundant full-traversal loops in prepare/emit-intrinsics/structurizer passes (PR #211299)

Arseniy Obolenskiy via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 22 08:47:31 PDT 2026


https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/211299

None

>From c03f56890a73f3f3e5a39101973d6f3d4a6eba08 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Wed, 22 Jul 2026 17:46:57 +0200
Subject: [PATCH] [NFC][SPIR-V] Fuse redundant full-traversal loops in
 prepare/emit-intrinsics/structurizer passes

---
 llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp | 19 ++---
 .../Target/SPIRV/SPIRVPrepareFunctions.cpp    | 27 +++----
 llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp   | 76 +++++++++----------
 3 files changed, 51 insertions(+), 71 deletions(-)

diff --git a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
index e5afaa5c63e4b..cd3ded2334b26 100644
--- a/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVEmitIntrinsics.cpp
@@ -3643,6 +3643,13 @@ bool SPIRVEmitIntrinsicsImpl::runOnFunction(Function &Func) {
   // Data structure for dead instructions that were simplified and replaced.
   SmallPtrSet<Instruction *, 4> DeadInsts;
   for (auto &I : instructions(Func)) {
+    if (StoreInst *SI = dyn_cast<StoreInst>(&I)) {
+      Type *ElTy = SI->getValueOperand()->getType();
+      if (ElTy->isAggregateType() || ElTy->isVectorTy())
+        AggrStores.insert(&I);
+      continue;
+    }
+
     auto *GEP = dyn_cast<GetElementPtrInst>(&I);
     auto *SGEP = dyn_cast<StructuredGEPInst>(&I);
 
@@ -3670,18 +3677,6 @@ bool SPIRVEmitIntrinsicsImpl::runOnFunction(Function &Func) {
     I->eraseFromParent();
   }
 
-  // StoreInst's operand type can be changed during the next
-  // transformations, so we need to store it in the set. Also store already
-  // transformed types.
-  for (auto &I : instructions(Func)) {
-    StoreInst *SI = dyn_cast<StoreInst>(&I);
-    if (!SI)
-      continue;
-    Type *ElTy = SI->getValueOperand()->getType();
-    if (ElTy->isAggregateType() || ElTy->isVectorTy())
-      AggrStores.insert(&I);
-  }
-
   B.SetInsertPoint(&Func.getEntryBlock(), Func.getEntryBlock().begin());
   for (auto &GV : Func.getParent()->globals())
     processGlobalValue(GV, B);
diff --git a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
index 9b418e6f88d67..6d54b6c6fa19d 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVPrepareFunctions.cpp
@@ -695,20 +695,18 @@ bool SPIRVPrepareFunctionsImpl::substituteAbortKHRCalls(Function *F) {
   if (!isAbortKHRBuiltin(*F))
     return false;
 
-  SmallVector<CallInst *> Calls;
-  for (User *U : F->users()) {
+  bool Changed = false;
+  for (User *U : make_early_inc_range(F->users())) {
     auto *CI = dyn_cast<CallInst>(U);
     if (!CI || CI->getCalledFunction() != F)
       continue;
     if (CI->arg_size() != 1)
       continue;
-    Calls.push_back(CI);
-  }
-
-  for (CallInst *CI : Calls)
     rewriteAbortKHRCall(CI);
+    Changed = true;
+  }
 
-  return !Calls.empty();
+  return Changed;
 }
 
 // When the SPV_KHR_abort extension is enabled, `llvm.trap` and
@@ -734,16 +732,11 @@ bool SPIRVPrepareFunctionsImpl::terminateBlocksAfterTrap(Module &M,
   if (!ST.canUseExtension(SPIRV::Extension::SPV_KHR_abort))
     return false;
 
-  SmallVector<CallInst *> Calls;
-  for (User *U : F->users()) {
+  bool Changed = false;
+  for (User *U : make_early_inc_range(F->users())) {
     auto *CI = dyn_cast<CallInst>(U);
     if (!CI || CI->getCalledFunction() != F)
       continue;
-    Calls.push_back(CI);
-  }
-
-  bool Changed = false;
-  for (CallInst *CI : Calls) {
     Instruction *Next = CI->getNextNode();
     if (!Next || isa<UnreachableInst>(Next))
       continue;
@@ -883,6 +876,7 @@ bool SPIRVPrepareFunctionsImpl::runOnModule(Module &M) {
     }
   }
 
+  std::vector<Function *> FuncsWorklist;
   for (Function &F : M) {
     // MachineFunctionPass skips available_externally; strip + tag so AuxData
     // can re-emit the original linkage as NonSemantic.AuxData::Linkage.
@@ -895,11 +889,8 @@ bool SPIRVPrepareFunctionsImpl::runOnModule(Module &M) {
     Changed |= substituteIntrinsicCalls(&F);
     Changed |= sortBlocks(F);
     Changed |= removeAggregateTypesFromCalls(&F);
-  }
-
-  std::vector<Function *> FuncsWorklist;
-  for (auto &F : M)
     FuncsWorklist.push_back(&F);
+  }
 
   for (auto *F : FuncsWorklist) {
     Function *NewF = removeAggregateTypesFromSignature(F);
diff --git a/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp b/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
index 0e6f5ecab1437..13b27d09ca9ef 100644
--- a/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVStructurizer.cpp
@@ -131,33 +131,6 @@ static bool isMergeInstruction(Instruction *I) {
   return getDesignatedMergeBlock(I) != nullptr;
 }
 
-// Returns all blocks in F having at least one OpLoopMerge or OpSelectionMerge
-// instruction.
-static SmallPtrSet<BasicBlock *, 2> getHeaderBlocks(Function &F) {
-  SmallPtrSet<BasicBlock *, 2> Output;
-  for (BasicBlock &BB : F) {
-    for (Instruction &I : BB) {
-      if (getDesignatedMergeBlock(&I) != nullptr)
-        Output.insert(&BB);
-    }
-  }
-  return Output;
-}
-
-// Returns all basic blocks in |F| referenced by at least 1
-// OpSelectionMerge/OpLoopMerge instruction.
-static SmallPtrSet<BasicBlock *, 2> getMergeBlocks(Function &F) {
-  SmallPtrSet<BasicBlock *, 2> Output;
-  for (BasicBlock &BB : F) {
-    for (Instruction &I : BB) {
-      BasicBlock *MB = getDesignatedMergeBlock(&I);
-      if (MB != nullptr)
-        Output.insert(MB);
-    }
-  }
-  return Output;
-}
-
 // Return all the merge instructions contained in BB.
 // Note: the SPIR-V spec doesn't allow a single BB to contain more than 1 merge
 // instruction, but this can happen while we structurize the CFG.
@@ -169,15 +142,33 @@ static std::vector<Instruction *> getMergeInstructions(BasicBlock &BB) {
   return Output;
 }
 
-// Returns all basic blocks in |F| referenced as continue target by at least 1
-// OpLoopMerge instruction.
-static SmallPtrSet<BasicBlock *, 2> getContinueBlocks(Function &F) {
-  SmallPtrSet<BasicBlock *, 2> Output;
+// Bundles the header/merge/continue block sets computed by
+// getHeaderMergeContinueBlocks so callers only needing a subset of them can
+// still share the single underlying scan.
+struct HeaderMergeContinueBlocks {
+  // Blocks in F having at least one OpLoopMerge or OpSelectionMerge
+  // instruction.
+  SmallPtrSet<BasicBlock *, 2> Header;
+  // Blocks in F referenced by at least 1 OpSelectionMerge/OpLoopMerge
+  // instruction.
+  SmallPtrSet<BasicBlock *, 2> Merge;
+  // Blocks in F referenced as continue target by at least 1 OpLoopMerge
+  // instruction.
+  SmallPtrSet<BasicBlock *, 2> Continue;
+};
+
+// Computes Header, Merge and Continue block sets for |F| in a single scan,
+// since they all classify the same instructions.
+static HeaderMergeContinueBlocks getHeaderMergeContinueBlocks(Function &F) {
+  HeaderMergeContinueBlocks Output;
   for (BasicBlock &BB : F) {
     for (Instruction &I : BB) {
-      BasicBlock *MB = getDesignatedContinueBlock(&I);
-      if (MB != nullptr)
-        Output.insert(MB);
+      if (BasicBlock *MB = getDesignatedMergeBlock(&I)) {
+        Output.Header.insert(&BB);
+        Output.Merge.insert(MB);
+      }
+      if (BasicBlock *CB = getDesignatedContinueBlock(&I))
+        Output.Continue.insert(CB);
     }
   }
   return Output;
@@ -689,8 +680,9 @@ class SPIRVStructurizerImpl {
     PDT.recalculate(F);
     bool Modified = false;
 
-    auto MergeBlocks = getMergeBlocks(F);
-    auto ContinueBlocks = getContinueBlocks(F);
+    HeaderMergeContinueBlocks Blocks = getHeaderMergeContinueBlocks(F);
+    auto &MergeBlocks = Blocks.Merge;
+    auto &ContinueBlocks = Blocks.Continue;
 
     for (auto &BB : F) {
       if (getMergeInstructions(BB).size() != 0)
@@ -956,8 +948,9 @@ class SPIRVStructurizerImpl {
   bool removeUselessBlocks(Function &F) {
     std::vector<BasicBlock *> ToRemove;
 
-    auto MergeBlocks = getMergeBlocks(F);
-    auto ContinueBlocks = getContinueBlocks(F);
+    HeaderMergeContinueBlocks Blocks = getHeaderMergeContinueBlocks(F);
+    auto &MergeBlocks = Blocks.Merge;
+    auto &ContinueBlocks = Blocks.Continue;
 
     for (BasicBlock &BB : F) {
       if (BB.size() != 1)
@@ -989,9 +982,10 @@ class SPIRVStructurizerImpl {
   bool addHeaderToRemainingDivergentDAG(Function &F) {
     bool Modified = false;
 
-    auto MergeBlocks = getMergeBlocks(F);
-    auto ContinueBlocks = getContinueBlocks(F);
-    auto HeaderBlocks = getHeaderBlocks(F);
+    HeaderMergeContinueBlocks Blocks = getHeaderMergeContinueBlocks(F);
+    auto &MergeBlocks = Blocks.Merge;
+    auto &ContinueBlocks = Blocks.Continue;
+    auto &HeaderBlocks = Blocks.Header;
 
     DomTreeBuilder::BBDomTree DT;
     DomTreeBuilder::BBPostDomTree PDT;



More information about the llvm-commits mailing list