LLVM 24.0.0git
SPIRVCBufferAccess.cpp
Go to the documentation of this file.
1//===- SPIRVCBufferAccess.cpp - Translate CBuffer Loads ---------*- C++ -*-===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This pass replaces all accesses to constant buffer global variables with
10// accesses to the proper SPIR-V resource.
11//
12// The pass operates as follows:
13// 1. It finds all constant buffers by looking for the `!hlsl.cbs` metadata.
14// 2. For each cbuffer, it finds the global variable holding the resource handle
15// and the global variables for each of the cbuffer's members.
16// 3. For each member variable, it creates a call to the
17// `llvm.spv.resource.getpointer` intrinsic. This intrinsic takes the
18// resource handle and the member's index within the cbuffer as arguments.
19// The result is a pointer to that member within the SPIR-V resource.
20// 4. It then replaces all uses of the original member global variable with the
21// pointer returned by the `getpointer` intrinsic. This effectively retargets
22// all loads and GEPs to the new resource pointer.
23// 5. Finally, it cleans up by deleting the original global variables and the
24// `!hlsl.cbs` metadata.
25//
26// This approach allows subsequent passes, like SPIRVEmitIntrinsics, to
27// correctly handle GEPs that operate on the result of the `getpointer` call,
28// folding them into a single OpAccessChain instruction.
29//
30//===----------------------------------------------------------------------===//
31
32#include "SPIRV.h"
34#include "llvm/IR/IRBuilder.h"
35#include "llvm/IR/IntrinsicsSPIRV.h"
36#include "llvm/IR/Module.h"
39
40#define DEBUG_TYPE "spirv-cbuffer-access"
41using namespace llvm;
42
43// Finds the single instruction that defines the resource handle. This is
44// typically a call to `llvm.spv.resource.handlefrombinding`.
46 for (User *U : HandleVar->users()) {
47 if (auto *SI = dyn_cast<StoreInst>(U)) {
48 if (auto *I = dyn_cast<Instruction>(SI->getValueOperand())) {
49 return I;
50 }
51 }
52 }
53 return nullptr;
54}
55
57 std::optional<hlsl::CBufferMetadata> CBufMD =
59 if (auto *TET = dyn_cast<TargetExtType>(Ty))
60 return TET->getName() == "spirv.Padding";
61 return false;
62 });
63 if (!CBufMD)
64 return false;
65
67 SmallVector<Constant *> CBufferGlobals;
68 for (const hlsl::CBufferMapping &Mapping : *CBufMD) {
69 CBufferHandles.insert(Mapping.Handle);
70 for (const hlsl::CBufferMember &Member : Mapping.Members)
71 CBufferGlobals.push_back(Member.GV);
72 }
74
75 for (const hlsl::CBufferMapping &Mapping : *CBufMD) {
76 Instruction *HandleDef = findHandleDef(Mapping.Handle);
77 if (!HandleDef) {
78 report_fatal_error("Could not find handle definition for cbuffer: " +
79 Mapping.Handle->getName());
80 }
81
82 // The handle definition should dominate all uses of the cbuffer members.
83 // We'll insert our getpointer calls right after it.
84 IRBuilder<> Builder(HandleDef->getNextNode());
85 auto *HandleTy = cast<TargetExtType>(Mapping.Handle->getValueType());
86 auto *LayoutTy = cast<StructType>(HandleTy->getTypeParameter(0));
87 const StructLayout *SL = M.getDataLayout().getStructLayout(LayoutTy);
88
89 for (const hlsl::CBufferMember &Member : Mapping.Members) {
90 GlobalVariable *MemberGV = Member.GV;
91 if (MemberGV->use_empty()) {
92 continue;
93 }
94
95 uint32_t IndexInStruct = SL->getElementContainingOffset(Member.Offset);
96
97 // Create the getpointer intrinsic call.
98 Value *IndexVal = Builder.getInt32(IndexInStruct);
99 Type *PtrType = MemberGV->getType();
100 Value *GetPointerCall = Builder.CreateIntrinsic(
101 PtrType, Intrinsic::spv_resource_getpointer, {HandleDef, IndexVal});
102
103 MemberGV->replaceAllUsesWith(GetPointerCall);
104 }
105 }
106
107 // Remove cbuffer handle globals from @llvm.compiler.used list.
108 llvm::removeFromUsedLists(M, [&](Constant *C) -> bool {
109 auto *GV = dyn_cast<GlobalVariable>(C);
110 return GV && CBufferHandles.contains(GV);
111 });
112 for (GlobalVariable *HandleGV : CBufferHandles)
113 HandleGV->removeDeadConstantUsers();
114
115 // Now that all uses are replaced, clean up the globals and metadata.
116 for (const hlsl::CBufferMapping &Mapping : *CBufMD) {
117 for (const auto &Member : Mapping.Members) {
118 Member.GV->eraseFromParent();
119 }
120 // Erase the stores to the handle variable before erasing the handle itself.
122 for (User *U : Mapping.Handle->users()) {
123 if (auto *SI = dyn_cast<StoreInst>(U)) {
124 HandleStores.push_back(SI);
125 }
126 }
127 for (Instruction *I : HandleStores) {
128 I->eraseFromParent();
129 }
130 Mapping.Handle->eraseFromParent();
131 }
132
133 CBufMD->eraseFromModule();
134 return true;
135}
136
144
145namespace {
146class SPIRVCBufferAccessLegacy : public ModulePass {
147public:
148 bool runOnModule(Module &M) override { return replaceCBufferAccesses(M); }
149 StringRef getPassName() const override { return "SPIRV CBuffer Access"; }
150 SPIRVCBufferAccessLegacy() : ModulePass(ID) {}
151
152 static char ID; // Pass identification.
153};
154char SPIRVCBufferAccessLegacy::ID = 0;
155} // end anonymous namespace
156
157INITIALIZE_PASS(SPIRVCBufferAccessLegacy, DEBUG_TYPE, "SPIRV CBuffer Access",
158 false, false)
159
161 return new SPIRVCBufferAccessLegacy();
162}
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static bool replaceCBufferAccesses(Module &M)
#define DEBUG_TYPE
Module.h This file contains the declarations for the Module class.
#define I(x, y, z)
Definition MD5.cpp:57
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static bool replaceCBufferAccesses(Module &M)
static Instruction * findHandleDef(GlobalVariable *HandleVar)
This is an important base class in LLVM.
Definition Constant.h:43
PointerType * getType() const
Global values are always pointers.
This provides a uniform API for creating instructions and inserting them into a basic block: either a...
Definition IRBuilder.h:2910
ModulePass class - This class is used to implement unstructured interprocedural optimizations and ana...
Definition Pass.h:255
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses none()
Convenience factory function for the empty preserved set.
Definition Analysis.h:115
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
PreservedAnalyses run(Module &M, ModuleAnalysisManager &AM)
std::pair< iterator, bool > insert(PtrType Ptr)
Inserts Ptr if and only if there is no element in the container equal to Ptr.
bool contains(ConstPtrType Ptr) const
SmallPtrSet - This class implements a set which is optimized for holding SmallSize or less elements.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
Used to lazily calculate structure layout information for a target machine, based on the DataLayout s...
Definition DataLayout.h:743
LLVM_ABI unsigned getElementContainingOffset(uint64_t FixedOffset) const
Given a valid byte offset into the structure, returns the structure index that contains it.
The instances of the Type class are immutable: once they are created, they are never changed.
Definition Type.h:46
LLVM Value Representation.
Definition Value.h:75
LLVM_ABI void replaceAllUsesWith(Value *V)
Change all uses of this to point to a new Value.
Definition Value.cpp:553
iterator_range< user_iterator > users()
Definition Value.h:426
bool use_empty() const
Definition Value.h:346
static LLVM_ABI std::optional< CBufferMetadata > get(Module &M, llvm::function_ref< bool(Type *)> IsPadding)
Definition CBuffer.cpp:38
NodeTy * getNextNode()
Get the next node, or nullptr for the list tail.
Definition ilist_node.h:348
This is an optimization pass for GlobalISel generic memory operations.
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
LLVM_ABI bool convertUsersOfConstantsToInstructions(ArrayRef< Constant * > Consts, Function *RestrictToFunc=nullptr, bool RemoveDeadConstants=true, bool IncludeSelf=false)
Replace constant expressions users of the given constants with instructions.
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
LLVM_ABI void removeFromUsedLists(Module &M, function_ref< bool(Constant *)> ShouldRemove)
Removes global values from the llvm.used and llvm.compiler.used arrays.
ModulePass * createSPIRVCBufferAccessLegacyPass()
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39