[llvm] [SPIR-V][NewPM] Register IR-level passes with the new pass manager (PR #193660)
Arseniy Obolenskiy via llvm-commits
llvm-commits at lists.llvm.org
Wed Apr 22 21:22:08 PDT 2026
https://github.com/aobolensk created https://github.com/llvm/llvm-project/pull/193660
Add NPM wrappers for SPIRVRegularizer, SPIRVLegalizeImplicitBinding, SPIRVLegalizePointerCast, and SPIRVMergeRegionExitTargets
>From fee55b9ccacc415945565eff7f254c3f01fea466 Mon Sep 17 00:00:00 2001
From: Arseniy Obolenskiy <arseniy.obolenskiy at amd.com>
Date: Thu, 23 Apr 2026 06:20:35 +0200
Subject: [PATCH] [SPIR-V][NewPM] Register IR-level passes with the new pass
manager
Add NPM wrappers for SPIRVRegularizer, SPIRVLegalizeImplicitBinding, SPIRVLegalizePointerCast, and SPIRVMergeRegionExitTargets
---
llvm/lib/Target/SPIRV/SPIRV.h | 8 +-
.../SPIRV/SPIRVLegalizeImplicitBinding.cpp | 50 ++-
.../SPIRV/SPIRVLegalizeImplicitBinding.h | 24 ++
.../Target/SPIRV/SPIRVLegalizePointerCast.cpp | 34 +-
.../Target/SPIRV/SPIRVLegalizePointerCast.h | 29 ++
.../SPIRV/SPIRVMergeRegionExitTargets.cpp | 322 +++++++++---------
.../SPIRV/SPIRVMergeRegionExitTargets.h | 24 ++
llvm/lib/Target/SPIRV/SPIRVPassRegistry.def | 11 +
llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp | 30 +-
llvm/lib/Target/SPIRV/SPIRVRegularizer.h | 23 ++
llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp | 13 +-
.../CodeGen/SPIRV/ctor-dtor-lowering-ir.ll | 1 +
.../passes/SPIRVLegalizeImplicitBinding.ll | 20 ++
.../SPIRV/passes/SPIRVLegalizePointerCast.ll | 30 ++
.../passes/SPIRVMergeRegionExitTargets.ll | 46 +++
.../SPIRV/passes/SPIRVRegularizer-i1-icmp.ll | 24 ++
16 files changed, 491 insertions(+), 198 deletions(-)
create mode 100644 llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.h
create mode 100644 llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.h
create mode 100644 llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.h
create mode 100644 llvm/lib/Target/SPIRV/SPIRVRegularizer.h
create mode 100644 llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizeImplicitBinding.ll
create mode 100644 llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizePointerCast.ll
create mode 100644 llvm/test/CodeGen/SPIRV/passes/SPIRVMergeRegionExitTargets.ll
create mode 100644 llvm/test/CodeGen/SPIRV/passes/SPIRVRegularizer-i1-icmp.ll
diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index 71c200a0d09c3..efc4da613c456 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -50,12 +50,12 @@ void initializeSPIRVStructurizerPass(PassRegistry &);
void initializeSPIRVCBufferAccessLegacyPass(PassRegistry &);
void initializeSPIRVPushConstantAccessLegacyPass(PassRegistry &);
void initializeSPIRVEmitIntrinsicsPass(PassRegistry &);
-void initializeSPIRVLegalizePointerCastPass(PassRegistry &);
-void initializeSPIRVRegularizerPass(PassRegistry &);
-void initializeSPIRVMergeRegionExitTargetsPass(PassRegistry &);
+void initializeSPIRVLegalizePointerCastLegacyPass(PassRegistry &);
+void initializeSPIRVRegularizerLegacyPass(PassRegistry &);
+void initializeSPIRVMergeRegionExitTargetsLegacyPass(PassRegistry &);
void initializeSPIRVPrepareFunctionsPass(PassRegistry &);
void initializeSPIRVPrepareGlobalsPass(PassRegistry &);
-void initializeSPIRVLegalizeImplicitBindingPass(PassRegistry &);
+void initializeSPIRVLegalizeImplicitBindingLegacyPass(PassRegistry &);
void initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PassRegistry &);
void initializeSPIRVCtorDtorLoweringLegacyPass(PassRegistry &);
} // namespace llvm
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
index f7be2a1bef7b5..c1ec46255d6b4 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.cpp
@@ -13,6 +13,7 @@
//
//===----------------------------------------------------------------------===//
+#include "SPIRVLegalizeImplicitBinding.h"
#include "SPIRV.h"
#include "llvm/ADT/BitVector.h"
#include "llvm/ADT/SmallVector.h"
@@ -28,14 +29,9 @@
using namespace llvm;
namespace {
-class SPIRVLegalizeImplicitBinding : public ModulePass {
+class SPIRVLegalizeImplicitBindingImpl {
public:
- static char ID;
- SPIRVLegalizeImplicitBinding() : ModulePass(ID) {}
- StringRef getPassName() const override {
- return "SPIRV Legalize Implicit Binding";
- }
- bool runOnModule(Module &M) override;
+ bool runOnModule(Module &M);
private:
void collectBindingInfo(Module &M);
@@ -53,6 +49,18 @@ class SPIRVLegalizeImplicitBinding : public ModulePass {
SmallVector<CallInst *, 16> ImplicitBindingCalls;
};
+class SPIRVLegalizeImplicitBindingLegacy : public ModulePass {
+public:
+ static char ID;
+ SPIRVLegalizeImplicitBindingLegacy() : ModulePass(ID) {}
+ StringRef getPassName() const override {
+ return "SPIRV Legalize Implicit Binding";
+ }
+ bool runOnModule(Module &M) override {
+ return SPIRVLegalizeImplicitBindingImpl().runOnModule(M);
+ }
+};
+
struct BindingInfoCollector : public InstVisitor<BindingInfoCollector> {
std::vector<BitVector> &UsedBindings;
SmallVector<CallInst *, 16> &ImplicitBindingCalls;
@@ -129,7 +137,7 @@ static uint32_t getDescSet(const CallInst *CI) {
return cast<ConstantInt>(CI->getArgOperand(DescSetArgIdx))->getZExtValue();
}
-void SPIRVLegalizeImplicitBinding::collectBindingInfo(Module &M) {
+void SPIRVLegalizeImplicitBindingImpl::collectBindingInfo(Module &M) {
BindingInfoCollector InfoCollector(UsedBindings, ImplicitBindingCalls);
InfoCollector.visit(M);
@@ -140,7 +148,7 @@ void SPIRVLegalizeImplicitBinding::collectBindingInfo(Module &M) {
});
}
-void SPIRVLegalizeImplicitBinding::verifyUniqueOrderIdPerResource(
+void SPIRVLegalizeImplicitBindingImpl::verifyUniqueOrderIdPerResource(
SmallVectorImpl<CallInst *> &Calls) {
// Check that the order Id is unique per resource.
for (uint32_t i = 1; i < Calls.size(); ++i) {
@@ -157,7 +165,7 @@ void SPIRVLegalizeImplicitBinding::verifyUniqueOrderIdPerResource(
}
}
-uint32_t SPIRVLegalizeImplicitBinding::getAndReserveFirstUnusedBinding(
+uint32_t SPIRVLegalizeImplicitBindingImpl::getAndReserveFirstUnusedBinding(
uint32_t DescSet) {
if (UsedBindings.size() <= DescSet) {
UsedBindings.resize(DescSet + 1);
@@ -174,7 +182,7 @@ uint32_t SPIRVLegalizeImplicitBinding::getAndReserveFirstUnusedBinding(
return NewBinding;
}
-void SPIRVLegalizeImplicitBinding::replaceImplicitBindingCalls(Module &M) {
+void SPIRVLegalizeImplicitBindingImpl::replaceImplicitBindingCalls(Module &M) {
uint32_t lastOrderId = -1;
uint32_t lastBindingNumber = -1;
@@ -202,7 +210,7 @@ void SPIRVLegalizeImplicitBinding::replaceImplicitBindingCalls(Module &M) {
}
}
-bool SPIRVLegalizeImplicitBinding::runOnModule(Module &M) {
+bool SPIRVLegalizeImplicitBindingImpl::runOnModule(Module &M) {
collectBindingInfo(M);
if (ImplicitBindingCalls.empty()) {
return false;
@@ -214,16 +222,24 @@ bool SPIRVLegalizeImplicitBinding::runOnModule(Module &M) {
}
} // namespace
-char SPIRVLegalizeImplicitBinding::ID = 0;
+PreservedAnalyses
+SPIRVLegalizeImplicitBinding::run(Module &M, ModuleAnalysisManager &AM) {
+ return SPIRVLegalizeImplicitBindingImpl().runOnModule(M)
+ ? PreservedAnalyses::none()
+ : PreservedAnalyses::all();
+}
+
+char SPIRVLegalizeImplicitBindingLegacy::ID = 0;
-INITIALIZE_PASS(SPIRVLegalizeImplicitBinding, "legalize-spirv-implicit-binding",
+INITIALIZE_PASS(SPIRVLegalizeImplicitBindingLegacy,
+ "legalize-spirv-implicit-binding",
"Legalize SPIR-V implicit bindings", false, false)
ModulePass *llvm::createSPIRVLegalizeImplicitBindingPass() {
- return new SPIRVLegalizeImplicitBinding();
+ return new SPIRVLegalizeImplicitBindingLegacy();
}
-void SPIRVLegalizeImplicitBinding::replaceResourceHandleCall(
+void SPIRVLegalizeImplicitBindingImpl::replaceResourceHandleCall(
Module &M, CallInst *OldCI, uint32_t NewBinding) {
IRBuilder<> Builder(OldCI);
const uint32_t DescSet =
@@ -247,7 +263,7 @@ void SPIRVLegalizeImplicitBinding::replaceResourceHandleCall(
OldCI->eraseFromParent();
}
-void SPIRVLegalizeImplicitBinding::replaceCounterHandleCall(
+void SPIRVLegalizeImplicitBindingImpl::replaceCounterHandleCall(
Module &M, CallInst *OldCI, uint32_t NewBinding) {
IRBuilder<> Builder(OldCI);
const uint32_t DescSet =
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.h b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.h
new file mode 100644
index 0000000000000..9c43690bae6d8
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizeImplicitBinding.h
@@ -0,0 +1,24 @@
+//===- SPIRVLegalizeImplicitBinding.h - Legalize implicit bindings -*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEIMPLICITBINDING_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEIMPLICITBINDING_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class SPIRVLegalizeImplicitBinding
+ : public PassInfoMixin<SPIRVLegalizeImplicitBinding> {
+public:
+ PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEIMPLICITBINDING_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
index 86a54e1274b9a..b4e0c478c7900 100644
--- a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.cpp
@@ -42,6 +42,7 @@
//
//===----------------------------------------------------------------------===//
+#include "SPIRVLegalizePointerCast.h"
#include "SPIRV.h"
#include "SPIRVSubtarget.h"
#include "SPIRVTargetMachine.h"
@@ -56,7 +57,7 @@
using namespace llvm;
namespace {
-class SPIRVLegalizePointerCast : public FunctionPass {
+class SPIRVLegalizePointerCastImpl {
// Builds the `spv_assign_type` assigning |Ty| to |Value| at the current
// builder position.
@@ -531,10 +532,10 @@ class SPIRVLegalizePointerCast : public FunctionPass {
}
public:
- SPIRVLegalizePointerCast(SPIRVTargetMachine *TM) : FunctionPass(ID), TM(TM) {}
+ SPIRVLegalizePointerCastImpl(const SPIRVTargetMachine &TM) : TM(TM) {}
- bool runOnFunction(Function &F) override {
- const SPIRVSubtarget &ST = TM->getSubtarget<SPIRVSubtarget>(F);
+ bool run(Function &F) {
+ const SPIRVSubtarget &ST = TM.getSubtarget<SPIRVSubtarget>(F);
GR = ST.getSPIRVGlobalRegistry();
DeadInstructions.clear();
@@ -557,19 +558,36 @@ class SPIRVLegalizePointerCast : public FunctionPass {
}
private:
- SPIRVTargetMachine *TM = nullptr;
+ const SPIRVTargetMachine &TM;
SPIRVGlobalRegistry *GR = nullptr;
std::vector<Instruction *> DeadInstructions;
+};
+class SPIRVLegalizePointerCastLegacy : public FunctionPass {
public:
static char ID;
+ SPIRVLegalizePointerCastLegacy(SPIRVTargetMachine *TM)
+ : FunctionPass(ID), TM(TM) {}
+
+ bool runOnFunction(Function &F) override {
+ return SPIRVLegalizePointerCastImpl(*TM).run(F);
+ }
+
+private:
+ SPIRVTargetMachine *TM = nullptr;
};
} // namespace
-char SPIRVLegalizePointerCast::ID = 0;
-INITIALIZE_PASS(SPIRVLegalizePointerCast, "spirv-legalize-bitcast",
+PreservedAnalyses
+SPIRVLegalizePointerCast::run(Function &F, FunctionAnalysisManager &AM) {
+ return SPIRVLegalizePointerCastImpl(TM).run(F) ? PreservedAnalyses::none()
+ : PreservedAnalyses::all();
+}
+
+char SPIRVLegalizePointerCastLegacy::ID = 0;
+INITIALIZE_PASS(SPIRVLegalizePointerCastLegacy, "spirv-legalize-bitcast",
"SPIRV legalize bitcast pass", false, false)
FunctionPass *llvm::createSPIRVLegalizePointerCastPass(SPIRVTargetMachine *TM) {
- return new SPIRVLegalizePointerCast(TM);
+ return new SPIRVLegalizePointerCastLegacy(TM);
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.h b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.h
new file mode 100644
index 0000000000000..438415cb3bc8f
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVLegalizePointerCast.h
@@ -0,0 +1,29 @@
+//===-- SPIRVLegalizePointerCast.h ------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEPOINTERCAST_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEPOINTERCAST_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class SPIRVTargetMachine;
+
+class SPIRVLegalizePointerCast
+ : public PassInfoMixin<SPIRVLegalizePointerCast> {
+ const SPIRVTargetMachine &TM;
+
+public:
+ explicit SPIRVLegalizePointerCast(const SPIRVTargetMachine &TM) : TM(TM) {}
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVLEGALIZEPOINTERCAST_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.cpp b/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.cpp
index 54ead93f2d52d..c6112554f794a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.cpp
@@ -12,6 +12,7 @@
//
//===----------------------------------------------------------------------===//
+#include "SPIRVMergeRegionExitTargets.h"
#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
#include "SPIRV.h"
#include "SPIRVSubtarget.h"
@@ -31,178 +32,183 @@ using namespace llvm;
namespace {
-class SPIRVMergeRegionExitTargets : public FunctionPass {
-public:
- static char ID;
-
- SPIRVMergeRegionExitTargets() : FunctionPass(ID) {}
-
- /// Create a value in BB set to the value associated with the branch the block
- /// terminator will take.
- llvm::Value *createExitVariable(
- BasicBlock *BB,
- const DenseMap<BasicBlock *, ConstantInt *> &TargetToValue) {
- auto *T = BB->getTerminator();
- if (isa<ReturnInst>(T))
- return nullptr;
- if (auto *BI = dyn_cast<UncondBrInst>(T))
- return TargetToValue.lookup(BI->getSuccessor());
-
- IRBuilder<> Builder(BB);
- Builder.SetInsertPoint(T);
-
- if (auto *BI = dyn_cast<CondBrInst>(T)) {
- Value *LHS = TargetToValue.lookup(BI->getSuccessor(0));
- Value *RHS = TargetToValue.lookup(BI->getSuccessor(1));
-
- if (LHS == nullptr || RHS == nullptr)
- return LHS == nullptr ? RHS : LHS;
- return Builder.CreateSelect(BI->getCondition(), LHS, RHS);
- }
-
- // TODO: add support for switch cases.
- llvm_unreachable("Unhandled terminator type.");
+/// Create a value in BB set to the value associated with the branch the block
+/// terminator will take.
+static llvm::Value *
+createExitVariable(BasicBlock *BB,
+ const DenseMap<BasicBlock *, ConstantInt *> &TargetToValue) {
+ auto *T = BB->getTerminator();
+ if (isa<ReturnInst>(T))
+ return nullptr;
+ if (auto *BI = dyn_cast<UncondBrInst>(T))
+ return TargetToValue.lookup(BI->getSuccessor());
+
+ IRBuilder<> Builder(BB);
+ Builder.SetInsertPoint(T);
+
+ if (auto *BI = dyn_cast<CondBrInst>(T)) {
+ Value *LHS = TargetToValue.lookup(BI->getSuccessor(0));
+ Value *RHS = TargetToValue.lookup(BI->getSuccessor(1));
+
+ if (LHS == nullptr || RHS == nullptr)
+ return LHS == nullptr ? RHS : LHS;
+ return Builder.CreateSelect(BI->getCondition(), LHS, RHS);
}
- AllocaInst *CreateVariable(Function &F, Type *Type,
- BasicBlock::iterator Position) {
- const DataLayout &DL = F.getDataLayout();
- return new AllocaInst(Type, DL.getAllocaAddrSpace(), nullptr, "reg",
- Position);
- }
+ // TODO: add support for switch cases.
+ llvm_unreachable("Unhandled terminator type.");
+}
- // Run the pass on the given convergence region, ignoring the sub-regions.
- // Returns true if the CFG changed, false otherwise.
- bool runOnConvergenceRegionNoRecurse(LoopInfo &LI,
- SPIRV::ConvergenceRegion *CR) {
- // Gather all the exit targets for this region.
- SmallPtrSet<BasicBlock *, 4> ExitTargets;
- for (BasicBlock *Exit : CR->Exits) {
- for (BasicBlock *Target : successors(Exit)) {
- if (CR->Blocks.count(Target) == 0)
- ExitTargets.insert(Target);
- }
- }
+static AllocaInst *createVariable(Function &F, Type *Type,
+ BasicBlock::iterator Position) {
+ const DataLayout &DL = F.getDataLayout();
+ return new AllocaInst(Type, DL.getAllocaAddrSpace(), nullptr, "reg",
+ Position);
+}
- // If we have zero or one exit target, nothing do to.
- if (ExitTargets.size() <= 1)
- return false;
-
- // Create the new single exit target.
- auto F = CR->Entry->getParent();
- auto NewExitTarget = BasicBlock::Create(F->getContext(), "new.exit", F);
- IRBuilder<> Builder(NewExitTarget);
-
- AllocaInst *Variable = CreateVariable(*F, Builder.getInt32Ty(),
- F->begin()->getFirstInsertionPt());
-
- // CodeGen output needs to be stable. Using the set as-is would order
- // the targets differently depending on the allocation pattern.
- // Sorting per basic-block ordering in the function.
- std::vector<BasicBlock *> SortedExitTargets;
- std::vector<BasicBlock *> SortedExits;
- for (BasicBlock &BB : *F) {
- if (ExitTargets.count(&BB) != 0)
- SortedExitTargets.push_back(&BB);
- if (CR->Exits.count(&BB) != 0)
- SortedExits.push_back(&BB);
+// Run the pass on the given convergence region, ignoring the sub-regions.
+// Returns true if the CFG changed, false otherwise.
+static bool runOnConvergenceRegionNoRecurse(LoopInfo &LI,
+ SPIRV::ConvergenceRegion *CR) {
+ // Gather all the exit targets for this region.
+ SmallPtrSet<BasicBlock *, 4> ExitTargets;
+ for (BasicBlock *Exit : CR->Exits) {
+ for (BasicBlock *Target : successors(Exit)) {
+ if (CR->Blocks.count(Target) == 0)
+ ExitTargets.insert(Target);
}
+ }
- // Creating one constant per distinct exit target. This will be route to the
- // correct target.
- DenseMap<BasicBlock *, ConstantInt *> TargetToValue;
- for (BasicBlock *Target : SortedExitTargets)
- TargetToValue.insert(
- std::make_pair(Target, Builder.getInt32(TargetToValue.size())));
-
- // Creating one variable per exit node, set to the constant matching the
- // targeted external block.
- std::vector<std::pair<BasicBlock *, Value *>> ExitToVariable;
- for (auto Exit : SortedExits) {
- llvm::Value *Value = createExitVariable(Exit, TargetToValue);
- IRBuilder<> B2(Exit);
- B2.SetInsertPoint(Exit->getFirstInsertionPt());
- B2.CreateStore(Value, Variable);
- ExitToVariable.emplace_back(std::make_pair(Exit, Value));
- }
+ // If we have zero or one exit target, nothing do to.
+ if (ExitTargets.size() <= 1)
+ return false;
+
+ // Create the new single exit target.
+ auto F = CR->Entry->getParent();
+ auto NewExitTarget = BasicBlock::Create(F->getContext(), "new.exit", F);
+ IRBuilder<> Builder(NewExitTarget);
+
+ AllocaInst *Variable = createVariable(*F, Builder.getInt32Ty(),
+ F->begin()->getFirstInsertionPt());
+
+ // CodeGen output needs to be stable. Using the set as-is would order
+ // the targets differently depending on the allocation pattern.
+ // Sorting per basic-block ordering in the function.
+ std::vector<BasicBlock *> SortedExitTargets;
+ std::vector<BasicBlock *> SortedExits;
+ for (BasicBlock &BB : *F) {
+ if (ExitTargets.count(&BB) != 0)
+ SortedExitTargets.push_back(&BB);
+ if (CR->Exits.count(&BB) != 0)
+ SortedExits.push_back(&BB);
+ }
- llvm::Value *Load = Builder.CreateLoad(Builder.getInt32Ty(), Variable);
+ // Creating one constant per distinct exit target. This will be route to the
+ // correct target.
+ DenseMap<BasicBlock *, ConstantInt *> TargetToValue;
+ for (BasicBlock *Target : SortedExitTargets)
+ TargetToValue.insert(
+ std::make_pair(Target, Builder.getInt32(TargetToValue.size())));
+
+ // Creating one variable per exit node, set to the constant matching the
+ // targeted external block.
+ std::vector<std::pair<BasicBlock *, Value *>> ExitToVariable;
+ for (auto Exit : SortedExits) {
+ llvm::Value *Value = createExitVariable(Exit, TargetToValue);
+ IRBuilder<> B2(Exit);
+ B2.SetInsertPoint(Exit->getFirstInsertionPt());
+ B2.CreateStore(Value, Variable);
+ ExitToVariable.emplace_back(std::make_pair(Exit, Value));
+ }
- // Creating the switch to jump to the correct exit target.
- llvm::SwitchInst *Sw = Builder.CreateSwitch(Load, SortedExitTargets[0],
- SortedExitTargets.size() - 1);
- for (size_t i = 1; i < SortedExitTargets.size(); i++) {
- BasicBlock *BB = SortedExitTargets[i];
- Sw->addCase(TargetToValue[BB], BB);
- }
+ llvm::Value *Load = Builder.CreateLoad(Builder.getInt32Ty(), Variable);
- // Fix exit branches to redirect to the new exit.
- for (auto Exit : CR->Exits) {
- Instruction *T = Exit->getTerminator();
- for (auto I = succ_begin(T), E = succ_end(T); I != E; ++I)
- if (ExitTargets.contains(*I))
- I.getUse()->set(NewExitTarget);
- }
+ // Creating the switch to jump to the correct exit target.
+ llvm::SwitchInst *Sw = Builder.CreateSwitch(Load, SortedExitTargets[0],
+ SortedExitTargets.size() - 1);
+ for (size_t i = 1; i < SortedExitTargets.size(); i++) {
+ BasicBlock *BB = SortedExitTargets[i];
+ Sw->addCase(TargetToValue[BB], BB);
+ }
- CR = CR->Parent;
- while (CR) {
- CR->Blocks.insert(NewExitTarget);
- CR = CR->Parent;
- }
+ // Fix exit branches to redirect to the new exit.
+ for (auto Exit : CR->Exits) {
+ Instruction *T = Exit->getTerminator();
+ for (auto I = succ_begin(T), E = succ_end(T); I != E; ++I)
+ if (ExitTargets.contains(*I))
+ I.getUse()->set(NewExitTarget);
+ }
- return true;
+ CR = CR->Parent;
+ while (CR) {
+ CR->Blocks.insert(NewExitTarget);
+ CR = CR->Parent;
}
- /// Run the pass on the given convergence region and sub-regions (DFS).
- /// Returns true if a region/sub-region was modified, false otherwise.
- /// This returns as soon as one region/sub-region has been modified.
- bool runOnConvergenceRegion(LoopInfo &LI, SPIRV::ConvergenceRegion *CR) {
- for (auto *Child : CR->Children)
- if (runOnConvergenceRegion(LI, Child))
- return true;
+ return true;
+}
- return runOnConvergenceRegionNoRecurse(LI, CR);
- }
+/// Run the pass on the given convergence region and sub-regions (DFS).
+/// Returns true if a region/sub-region was modified, false otherwise.
+/// This returns as soon as one region/sub-region has been modified.
+static bool runOnConvergenceRegion(LoopInfo &LI,
+ SPIRV::ConvergenceRegion *CR) {
+ for (auto *Child : CR->Children)
+ if (runOnConvergenceRegion(LI, Child))
+ return true;
+
+ return runOnConvergenceRegionNoRecurse(LI, CR);
+}
#if !NDEBUG
- /// Validates each edge exiting the region has the same destination basic
- /// block.
- void validateRegionExits(const SPIRV::ConvergenceRegion *CR) {
- for (auto *Child : CR->Children)
- validateRegionExits(Child);
-
- std::unordered_set<BasicBlock *> ExitTargets;
- for (auto *Exit : CR->Exits) {
- for (auto *BB : successors(Exit)) {
- if (CR->Blocks.count(BB) == 0)
- ExitTargets.insert(BB);
- }
+/// Validates each edge exiting the region has the same destination basic
+/// block.
+static void validateRegionExits(const SPIRV::ConvergenceRegion *CR) {
+ for (auto *Child : CR->Children)
+ validateRegionExits(Child);
+
+ std::unordered_set<BasicBlock *> ExitTargets;
+ for (auto *Exit : CR->Exits) {
+ for (auto *BB : successors(Exit)) {
+ if (CR->Blocks.count(BB) == 0)
+ ExitTargets.insert(BB);
}
-
- assert(ExitTargets.size() <= 1);
}
+
+ assert(ExitTargets.size() <= 1);
+}
#endif
- bool runOnFunction(Function &F) override {
- LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
- auto *TopLevelRegion =
- getAnalysis<SPIRVConvergenceRegionAnalysisWrapperPass>()
- .getRegionInfo()
- .getWritableTopLevelRegion();
-
- // FIXME: very inefficient method: each time a region is modified, we bubble
- // back up, and recompute the whole convergence region tree. Once the
- // algorithm is completed and test coverage good enough, rewrite this pass
- // to be efficient instead of simple.
- bool modified = false;
- while (runOnConvergenceRegion(LI, TopLevelRegion)) {
- modified = true;
- }
+static bool runImpl(Function &F, LoopInfo &LI,
+ SPIRV::ConvergenceRegionInfo &RegionInfo) {
+ auto *TopLevelRegion = RegionInfo.getWritableTopLevelRegion();
+
+ // FIXME: very inefficient method: each time a region is modified, we bubble
+ // back up, and recompute the whole convergence region tree. Once the
+ // algorithm is completed and test coverage good enough, rewrite this pass
+ // to be efficient instead of simple.
+ bool Modified = false;
+ while (runOnConvergenceRegion(LI, TopLevelRegion)) {
+ Modified = true;
+ }
#if !defined(NDEBUG) || defined(EXPENSIVE_CHECKS)
- validateRegionExits(TopLevelRegion);
+ validateRegionExits(TopLevelRegion);
#endif
- return modified;
+ return Modified;
+}
+
+class SPIRVMergeRegionExitTargetsLegacy : public FunctionPass {
+public:
+ static char ID;
+
+ SPIRVMergeRegionExitTargetsLegacy() : FunctionPass(ID) {}
+
+ bool runOnFunction(Function &F) override {
+ LoopInfo &LI = getAnalysis<LoopInfoWrapperPass>().getLoopInfo();
+ auto &RegionInfo =
+ getAnalysis<SPIRVConvergenceRegionAnalysisWrapperPass>().getRegionInfo();
+ return runImpl(F, LI, RegionInfo);
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
@@ -216,18 +222,28 @@ class SPIRVMergeRegionExitTargets : public FunctionPass {
};
} // namespace
-char SPIRVMergeRegionExitTargets::ID = 0;
+PreservedAnalyses
+SPIRVMergeRegionExitTargets::run(Function &F, FunctionAnalysisManager &AM) {
+ auto &LI = AM.getResult<LoopAnalysis>(F);
+ auto &RegionInfo = AM.getResult<SPIRVConvergenceRegionAnalysis>(F);
+ return runImpl(F, LI, RegionInfo) ? PreservedAnalyses::none()
+ : PreservedAnalyses::all();
+}
+
+char SPIRVMergeRegionExitTargetsLegacy::ID = 0;
-INITIALIZE_PASS_BEGIN(SPIRVMergeRegionExitTargets, "split-region-exit-blocks",
+INITIALIZE_PASS_BEGIN(SPIRVMergeRegionExitTargetsLegacy,
+ "split-region-exit-blocks",
"SPIRV split region exit blocks", false, false)
INITIALIZE_PASS_DEPENDENCY(LoopSimplify)
INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
INITIALIZE_PASS_DEPENDENCY(LoopInfoWrapperPass)
INITIALIZE_PASS_DEPENDENCY(SPIRVConvergenceRegionAnalysisWrapperPass)
-INITIALIZE_PASS_END(SPIRVMergeRegionExitTargets, "split-region-exit-blocks",
+INITIALIZE_PASS_END(SPIRVMergeRegionExitTargetsLegacy,
+ "split-region-exit-blocks",
"SPIRV split region exit blocks", false, false)
FunctionPass *llvm::createSPIRVMergeRegionExitTargetsPass() {
- return new SPIRVMergeRegionExitTargets();
+ return new SPIRVMergeRegionExitTargetsLegacy();
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.h b/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.h
new file mode 100644
index 0000000000000..c8a710cf82a6b
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVMergeRegionExitTargets.h
@@ -0,0 +1,24 @@
+//===-- SPIRVMergeRegionExitTargets.h ---------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVMERGEREGIONEXITTARGETS_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVMERGEREGIONEXITTARGETS_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class SPIRVMergeRegionExitTargets
+ : public PassInfoMixin<SPIRVMergeRegionExitTargets> {
+public:
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVMERGEREGIONEXITTARGETS_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def b/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
index 39f18cf8218a4..2ffaa9d4c2200 100644
--- a/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
+++ b/llvm/lib/Target/SPIRV/SPIRVPassRegistry.def
@@ -17,7 +17,9 @@
#define MODULE_PASS(NAME, CREATE_PASS)
#endif
MODULE_PASS("spirv-cbuffer-access", SPIRVCBufferAccess())
+MODULE_PASS("spirv-legalize-implicit-binding", SPIRVLegalizeImplicitBinding())
MODULE_PASS("spirv-legalize-zero-size-arrays", SPIRVLegalizeZeroSizeArrays(*static_cast<const SPIRVTargetMachine *>(this)))
+MODULE_PASS("spirv-lower-ctor-dtor", SPIRVCtorDtorLoweringPass())
MODULE_PASS("spirv-pushconstant-access", SPIRVPushConstantAccess(*static_cast<const SPIRVTargetMachine *>(this)))
MODULE_PASS("spirv-emit-intrinsics", SPIRVEmitIntrinsicsPass(*static_cast<const SPIRVTargetMachine *>(this)))
#undef MODULE_PASS
@@ -25,5 +27,14 @@ MODULE_PASS("spirv-emit-intrinsics", SPIRVEmitIntrinsicsPass(*static_cast<const
#ifndef FUNCTION_PASS
#define FUNCTION_PASS(NAME, CREATE_PASS)
#endif
+FUNCTION_PASS("spirv-legalize-pointer-cast", SPIRVLegalizePointerCast(*static_cast<const SPIRVTargetMachine *>(this)))
+FUNCTION_PASS("spirv-merge-region-exits", SPIRVMergeRegionExitTargets())
+FUNCTION_PASS("spirv-regularizer", SPIRVRegularizer())
FUNCTION_PASS("spirv-structurizer", SPIRVStructurizerWrapper())
#undef FUNCTION_PASS
+
+#ifndef FUNCTION_ANALYSIS
+#define FUNCTION_ANALYSIS(NAME, CREATE_PASS)
+#endif
+FUNCTION_ANALYSIS("spirv-convergence-region", SPIRVConvergenceRegionAnalysis())
+#undef FUNCTION_ANALYSIS
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
index c864556783a2a..bcfae91b63da2 100644
--- a/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVRegularizer.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "SPIRVRegularizer.h"
#include "SPIRV.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/IRBuilder.h"
@@ -24,27 +25,25 @@
using namespace llvm;
+static bool runImpl(Function &F);
+
namespace {
-struct SPIRVRegularizer : public FunctionPass {
+struct SPIRVRegularizerLegacy : public FunctionPass {
public:
static char ID;
- SPIRVRegularizer() : FunctionPass(ID) {}
- bool runOnFunction(Function &F) override;
+ SPIRVRegularizerLegacy() : FunctionPass(ID) {}
+ bool runOnFunction(Function &F) override { return runImpl(F); }
StringRef getPassName() const override { return "SPIR-V Regularizer"; }
void getAnalysisUsage(AnalysisUsage &AU) const override {
FunctionPass::getAnalysisUsage(AU);
}
-
-private:
- void runLowerConstExpr(Function &F);
- void runLowerI1Comparisons(Function &F);
};
} // namespace
-char SPIRVRegularizer::ID = 0;
+char SPIRVRegularizerLegacy::ID = 0;
-INITIALIZE_PASS(SPIRVRegularizer, DEBUG_TYPE, "SPIR-V Regularizer", false,
+INITIALIZE_PASS(SPIRVRegularizerLegacy, DEBUG_TYPE, "SPIR-V Regularizer", false,
false)
// Since SPIR-V cannot represent constant expression, constant expressions
@@ -55,7 +54,7 @@ INITIALIZE_PASS(SPIRVRegularizer, DEBUG_TYPE, "SPIR-V Regularizer", false,
// and all uses of it by instructions in that function are replaced by
// one instruction.
// TODO: remove redundant instructions for common subexpression.
-void SPIRVRegularizer::runLowerConstExpr(Function &F) {
+static void runLowerConstExpr(Function &F) {
LLVMContext &Ctx = F.getContext();
std::list<Instruction *> WorkList;
for (auto &II : instructions(F))
@@ -157,7 +156,7 @@ void SPIRVRegularizer::runLowerConstExpr(Function &F) {
// The backend treats i1 as boolean values, and SPIR-V only allows logical
// operations for boolean values. This function lowers i1 comparisons with
// certain predicates to logical operations to generate valid SPIR-V.
-void SPIRVRegularizer::runLowerI1Comparisons(Function &F) {
+static void runLowerI1Comparisons(Function &F) {
for (auto &I : make_early_inc_range(instructions(F))) {
auto *Cmp = dyn_cast<ICmpInst>(&I);
if (!Cmp)
@@ -209,12 +208,17 @@ void SPIRVRegularizer::runLowerI1Comparisons(Function &F) {
}
}
-bool SPIRVRegularizer::runOnFunction(Function &F) {
+static bool runImpl(Function &F) {
runLowerI1Comparisons(F);
runLowerConstExpr(F);
return true;
}
+PreservedAnalyses SPIRVRegularizer::run(Function &F,
+ FunctionAnalysisManager &AM) {
+ return runImpl(F) ? PreservedAnalyses::none() : PreservedAnalyses::all();
+}
+
FunctionPass *llvm::createSPIRVRegularizerPass() {
- return new SPIRVRegularizer();
+ return new SPIRVRegularizerLegacy();
}
diff --git a/llvm/lib/Target/SPIRV/SPIRVRegularizer.h b/llvm/lib/Target/SPIRV/SPIRVRegularizer.h
new file mode 100644
index 0000000000000..6daa1d51a8344
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVRegularizer.h
@@ -0,0 +1,23 @@
+//===-- SPIRVRegularizer.h - regularize IR for SPIR-V -----------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVREGULARIZER_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVREGULARIZER_H
+
+#include "llvm/IR/PassManager.h"
+
+namespace llvm {
+
+class SPIRVRegularizer : public PassInfoMixin<SPIRVRegularizer> {
+public:
+ PreservedAnalyses run(Function &F, FunctionAnalysisManager &AM);
+};
+
+} // namespace llvm
+
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVREGULARIZER_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index 54dfbc6937d6a..01ec003703b4a 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -11,13 +11,19 @@
//===----------------------------------------------------------------------===//
#include "SPIRVTargetMachine.h"
+#include "Analysis/SPIRVConvergenceRegionAnalysis.h"
#include "SPIRV.h"
#include "SPIRVCBufferAccess.h"
+#include "SPIRVCtorDtorLowering.h"
#include "SPIRVEmitIntrinsics.h"
#include "SPIRVGlobalRegistry.h"
+#include "SPIRVLegalizeImplicitBinding.h"
+#include "SPIRVLegalizePointerCast.h"
#include "SPIRVLegalizeZeroSizeArrays.h"
#include "SPIRVLegalizerInfo.h"
+#include "SPIRVMergeRegionExitTargets.h"
#include "SPIRVPushConstantAccess.h"
+#include "SPIRVRegularizer.h"
#include "SPIRVStructurizerWrapper.h"
#include "SPIRVTargetObjectFile.h"
#include "SPIRVTargetTransformInfo.h"
@@ -56,15 +62,16 @@ extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
initializeSPIRVCBufferAccessLegacyPass(PR);
initializeSPIRVPushConstantAccessLegacyPass(PR);
initializeSPIRVPreLegalizerCombinerPass(PR);
- initializeSPIRVLegalizePointerCastPass(PR);
+ initializeSPIRVLegalizePointerCastLegacyPass(PR);
initializeSPIRVLegalizeZeroSizeArraysLegacyPass(PR);
- initializeSPIRVRegularizerPass(PR);
+ initializeSPIRVRegularizerLegacyPass(PR);
initializeSPIRVPreLegalizerPass(PR);
initializeSPIRVPostLegalizerPass(PR);
- initializeSPIRVMergeRegionExitTargetsPass(PR);
+ initializeSPIRVMergeRegionExitTargetsLegacyPass(PR);
initializeSPIRVEmitIntrinsicsPass(PR);
initializeSPIRVPrepareFunctionsPass(PR);
initializeSPIRVPrepareGlobalsPass(PR);
+ initializeSPIRVLegalizeImplicitBindingLegacyPass(PR);
initializeSPIRVCtorDtorLoweringLegacyPass(PR);
}
diff --git a/llvm/test/CodeGen/SPIRV/ctor-dtor-lowering-ir.ll b/llvm/test/CodeGen/SPIRV/ctor-dtor-lowering-ir.ll
index 84d8375ab1310..a234069bb8917 100644
--- a/llvm/test/CodeGen/SPIRV/ctor-dtor-lowering-ir.ll
+++ b/llvm/test/CodeGen/SPIRV/ctor-dtor-lowering-ir.ll
@@ -1,5 +1,6 @@
; Test for SPIR-V constructor/destructor lowering pass at IR level.
; RUN: llc -mtriple=spirv64-intel-unknown --spirv-ext=+SPV_INTEL_function_pointers %s -o /dev/null -print-after=spirv-lower-ctor-dtor 2>&1 | FileCheck %s
+; RUN: opt -S -passes=spirv-lower-ctor-dtor -mtriple=spirv64-intel-unknown < %s | FileCheck %s
; This test verifies that:
; 1. The init/fini kernels are created.
diff --git a/llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizeImplicitBinding.ll b/llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizeImplicitBinding.ll
new file mode 100644
index 0000000000000..eb0d0311bedf0
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizeImplicitBinding.ll
@@ -0,0 +1,20 @@
+; RUN: opt -S -passes=spirv-legalize-implicit-binding -mtriple=spirv1.6-vulkan1.3-library < %s | FileCheck %s
+
+ at .str.b = private unnamed_addr constant [2 x i8] c"b\00", align 1
+ at .str.c = private unnamed_addr constant [2 x i8] c"c\00", align 1
+
+; Verify implicit-binding intrinsic calls are rewritten to explicit
+; handlefrombinding intrinsic calls; the descriptor set is preserved
+; (1st operand of the original) and a new binding number is assigned.
+
+define void @main() local_unnamed_addr #0 {
+entry:
+; CHECK-LABEL: define void @main(
+; CHECK: call target({{.*}}) @llvm.spv.resource.handlefrombinding{{.*}}(i32 0, i32 0, i32 1, i32 0, ptr {{.*}}@.str.b)
+; CHECK: call target({{.*}}) @llvm.spv.resource.handlefrombinding{{.*}}(i32 1, i32 0, i32 1, i32 0, ptr {{.*}}@.str.c)
+ %0 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 0, i32 0, i32 1, i32 0, ptr nonnull @.str.b)
+ %1 = tail call target("spirv.SignedImage", i32, 5, 2, 0, 0, 2, 0) @llvm.spv.resource.handlefromimplicitbinding.tspirv.SignedImage_i32_5_2_0_0_2_0t(i32 1, i32 1, i32 1, i32 0, ptr nonnull @.str.c)
+ ret void
+}
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizePointerCast.ll b/llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizePointerCast.ll
new file mode 100644
index 0000000000000..3773a42ece424
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/passes/SPIRVLegalizePointerCast.ll
@@ -0,0 +1,30 @@
+; spirv-legalize-pointer-cast consumes spv.ptrcast intrinsics produced by
+; spirv-emit-intrinsics, so we chain both passes and check the ptrcast is
+; rewritten into a sequence of typed loads + gep/extractelt.
+;
+; RUN: opt -S -passes='spirv-emit-intrinsics,function(spirv-legalize-pointer-cast)' -mtriple=spirv-unknown-vulkan-compute < %s | FileCheck %s
+
+target triple = "spirv-unknown-vulkan1.3-compute"
+
+ at M = internal addrspace(10) global [4 x <2 x float>] zeroinitializer, align 4
+ at OUT = internal addrspace(10) global float zeroinitializer, align 4
+
+; Loading a <5 x float> through a [4 x <2 x float>] forces emit-intrinsics to
+; insert spv.ptrcast; legalize-pointer-cast lowers it to typed <2 x float>
+; loads stitched together with extractelt. After the pass, no spv.ptrcast call
+; should remain.
+
+define spir_func void @main() #0 {
+; CHECK-LABEL: define spir_func void @main(
+; CHECK-NOT: call {{.*}}@llvm.spv.ptrcast
+; CHECK: call ptr addrspace(10) {{.*}}@llvm.spv.gep.p10.p10(i1 false, ptr addrspace(10) @M, i32 0, i32 0)
+; CHECK: load <2 x float>, ptr addrspace(10)
+; CHECK: call float @llvm.spv.extractelt.f32.v2f32.i32(<2 x float>
+entry:
+ %v = load <5 x float>, ptr addrspace(10) @M, align 4
+ %x = extractelement <5 x float> %v, i32 4
+ store float %x, ptr addrspace(10) @OUT, align 4
+ ret void
+}
+
+attributes #0 = { "hlsl.numthreads"="1,1,1" "hlsl.shader"="compute" }
diff --git a/llvm/test/CodeGen/SPIRV/passes/SPIRVMergeRegionExitTargets.ll b/llvm/test/CodeGen/SPIRV/passes/SPIRVMergeRegionExitTargets.ll
new file mode 100644
index 0000000000000..866fcb6371d8b
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/passes/SPIRVMergeRegionExitTargets.ll
@@ -0,0 +1,46 @@
+; RUN: opt -S -passes=spirv-merge-region-exits -mtriple=spirv-unknown-vulkan-compute < %s | FileCheck %s
+
+target triple = "spirv-unknown-vulkan1.3-compute"
+
+; A loop with two distinct exit targets (exit_a, exit_b). The pass
+; should merge them into a single new.exit block dispatched via a
+; switch on a stored discriminator value.
+
+define spir_func i32 @two_exits(i1 %ca, i1 %cb) #0 {
+; CHECK-LABEL: define spir_func i32 @two_exits(
+; CHECK: entry:
+; CHECK: [[REG:%.*]] = alloca i32
+; CHECK: loop:
+; CHECK: store i32 {{.*}}, ptr [[REG]]
+; CHECK: br i1 %ca, label %new.exit, label %body
+; CHECK: body:
+; CHECK: store i32 {{.*}}, ptr [[REG]]
+; CHECK: br i1 %cb, label %new.exit, label %loop
+; CHECK: new.exit:
+; CHECK: [[V:%.*]] = load i32, ptr [[REG]]
+; CHECK: switch i32 [[V]], label %exit_a [
+; CHECK: i32 {{.*}}, label %exit_b
+; CHECK: ]
+entry:
+ %t0 = call token @llvm.experimental.convergence.entry()
+ br label %loop
+
+loop:
+ %t1 = call token @llvm.experimental.convergence.loop() [ "convergencectrl"(token %t0) ]
+ br i1 %ca, label %exit_a, label %body
+
+body:
+ br i1 %cb, label %exit_b, label %loop
+
+exit_a:
+ ret i32 1
+
+exit_b:
+ ret i32 2
+}
+
+declare token @llvm.experimental.convergence.entry() #1
+declare token @llvm.experimental.convergence.loop() #1
+
+attributes #0 = { convergent noinline nounwind }
+attributes #1 = { convergent nocallback nofree nosync nounwind willreturn memory(none) }
diff --git a/llvm/test/CodeGen/SPIRV/passes/SPIRVRegularizer-i1-icmp.ll b/llvm/test/CodeGen/SPIRV/passes/SPIRVRegularizer-i1-icmp.ll
new file mode 100644
index 0000000000000..361a422c425c3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/passes/SPIRVRegularizer-i1-icmp.ll
@@ -0,0 +1,24 @@
+; RUN: opt -S -passes=spirv-regularizer -mtriple=spirv64-unknown-unknown < %s | FileCheck %s
+
+; Verify that i1 ICMP comparisons are lowered to logical operations,
+; matching the legacy pass behavior in runLowerI1Comparisons.
+
+define i1 @ult_i1(i1 %p, i1 %q) {
+; CHECK-LABEL: define i1 @ult_i1(
+; CHECK-SAME: i1 [[P:%.*]], i1 [[Q:%.*]]) {
+; CHECK: [[NOT:%.*]] = xor i1 [[P]], true
+; CHECK: [[R:%.*]] = and i1 [[Q]], [[NOT]]
+; CHECK: ret i1 [[R]]
+ %r = icmp ult i1 %p, %q
+ ret i1 %r
+}
+
+define i1 @ugt_i1(i1 %p, i1 %q) {
+; CHECK-LABEL: define i1 @ugt_i1(
+; CHECK-SAME: i1 [[P:%.*]], i1 [[Q:%.*]]) {
+; CHECK: [[NOT:%.*]] = xor i1 [[Q]], true
+; CHECK: [[R:%.*]] = and i1 [[P]], [[NOT]]
+; CHECK: ret i1 [[R]]
+ %r = icmp ugt i1 %p, %q
+ ret i1 %r
+}
More information about the llvm-commits
mailing list