1//===- DXILForwardHandleAccesses.cpp - Cleanup Handles --------------------===//
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#include "DXILForwardHandleAccesses.h"
10#include "DXILShaderFlags.h"
11#include "DirectX.h"
12#include "llvm/ADT/STLExtras.h"
13#include "llvm/Analysis/DXILResource.h"
14#include "llvm/Analysis/Loads.h"
15#include "llvm/IR/DiagnosticInfo.h"
16#include "llvm/IR/Dominators.h"
17#include "llvm/IR/InstrTypes.h"
18#include "llvm/IR/Instructions.h"
19#include "llvm/IR/IntrinsicInst.h"
20#include "llvm/IR/Intrinsics.h"
21#include "llvm/IR/IntrinsicsDirectX.h"
22#include "llvm/IR/Module.h"
23#include "llvm/InitializePasses.h"
24#include "llvm/Pass.h"
25#include "llvm/Transforms/Utils/Local.h"
26
27#define DEBUG_TYPE "dxil-forward-handle-accesses"
28
29using namespace llvm;
30
31static void diagnoseAmbiguousHandle(IntrinsicInst *NewII,
32 IntrinsicInst *PrevII) {
33 Function *F = NewII->getFunction();
34 LLVMContext &Context = F->getParent()->getContext();
35 Context.diagnose(DI: DiagnosticInfoGeneric(
36 Twine("Handle at \"") + NewII->getName() + "\" overwrites handle at \"" +
37 PrevII->getName() + "\""));
38}
39
40static void diagnoseHandleNotFound(LoadInst *LI) {
41 Function *F = LI->getFunction();
42 LLVMContext &Context = F->getParent()->getContext();
43 Context.diagnose(DI: DiagnosticInfoGeneric(
44 LI, Twine("Load of \"") + LI->getPointerOperand()->getName() +
45 "\" is not a global resource handle"));
46}
47
48static void diagnoseUndominatedLoad(LoadInst *LI, IntrinsicInst *Handle) {
49 Function *F = LI->getFunction();
50 LLVMContext &Context = F->getParent()->getContext();
51 Context.diagnose(DI: DiagnosticInfoGeneric(
52 LI, Twine("Load at \"") + LI->getName() +
53 "\" is not dominated by handle creation at \"" +
54 Handle->getName() + "\""));
55}
56
57static void
58processHandle(IntrinsicInst *II,
59 DenseMap<GlobalVariable *, IntrinsicInst *> &HandleMap) {
60 for (User *U : II->users())
61 if (auto *SI = dyn_cast<StoreInst>(Val: U))
62 if (auto *GV = dyn_cast<GlobalVariable>(Val: SI->getPointerOperand())) {
63 auto Entry = HandleMap.try_emplace(Key: GV, Args&: II);
64 if (Entry.second)
65 LLVM_DEBUG(dbgs() << "Added " << GV->getName() << " to handle map\n");
66 else
67 diagnoseAmbiguousHandle(NewII: II, PrevII: Entry.first->second);
68 }
69}
70
71static bool forwardHandleAccesses(Function &F, DominatorTree &DT) {
72 bool Changed = false;
73
74 DenseMap<GlobalVariable *, IntrinsicInst *> HandleMap;
75 SmallVector<LoadInst *> LoadsToProcess;
76 DenseMap<AllocaInst *, SmallVector<IntrinsicInst *>> LifeTimeIntrinsicMap;
77 for (BasicBlock &BB : F)
78 for (Instruction &Inst : BB)
79 if (auto *II = dyn_cast<IntrinsicInst>(Val: &Inst)) {
80 switch (II->getIntrinsicID()) {
81 case Intrinsic::dx_resource_handlefrombinding:
82 case Intrinsic::dx_resource_handlefromimplicitbinding:
83 processHandle(II, HandleMap);
84 break;
85 case Intrinsic::lifetime_start:
86 case Intrinsic::lifetime_end:
87 if (II->arg_size() >= 1) {
88 Value *Ptr = II->getArgOperand(i: 0);
89 if (auto *Alloca = dyn_cast<AllocaInst>(Val: Ptr))
90 LifeTimeIntrinsicMap[Alloca].push_back(Elt: II);
91 }
92 break;
93 default:
94 continue;
95 }
96 } else if (auto *LI = dyn_cast<LoadInst>(Val: &Inst))
97 if (isa<dxil::AnyResourceExtType>(Val: LI->getType()))
98 LoadsToProcess.push_back(Elt: LI);
99
100 for (LoadInst *LI : LoadsToProcess) {
101 Value *V = LI->getPointerOperand();
102 auto *GV = dyn_cast<GlobalVariable>(Val: V);
103
104 // If we didn't find the global, we may need to walk through a level of
105 // indirection. This generally happens at -O0.
106 if (!GV) {
107 if (auto *NestedLI = dyn_cast<LoadInst>(Val: V)) {
108 BasicBlock::iterator BBI(NestedLI);
109 Value *Loaded = FindAvailableLoadedValue(
110 Load: NestedLI, ScanBB: NestedLI->getParent(), ScanFrom&: BBI, MaxInstsToScan: 0, AA: nullptr, IsLoadCSE: nullptr);
111 GV = dyn_cast_or_null<GlobalVariable>(Val: Loaded);
112 } else if (auto *NestedAlloca = dyn_cast<AllocaInst>(Val: V)) {
113
114 if (auto It = LifeTimeIntrinsicMap.find(Val: NestedAlloca);
115 It != LifeTimeIntrinsicMap.end()) {
116 llvm::for_each(Range&: It->second,
117 F: [](IntrinsicInst *II) { II->eraseFromParent(); });
118 LifeTimeIntrinsicMap.erase(I: It);
119 }
120
121 for (auto *User : NestedAlloca->users()) {
122 auto *Store = dyn_cast<StoreInst>(Val: User);
123 if (!Store)
124 continue;
125
126 Value *StoredVal = Store->getValueOperand();
127 if (!StoredVal)
128 continue;
129
130 // Try direct global match
131 GV = dyn_cast<GlobalVariable>(Val: StoredVal);
132 if (GV)
133 break;
134
135 // If it's a load, check its source
136 if (auto *Load = dyn_cast<LoadInst>(Val: StoredVal)) {
137 GV = dyn_cast<GlobalVariable>(Val: Load->getPointerOperand());
138 if (GV)
139 break;
140
141 // If loading from an unmodified stack copy of the global, reuse the
142 // global's value. Note: we are just repeating what we are doing for
143 // the load case for the alloca store pattern.
144 BasicBlock::iterator BBI(Load);
145 Value *Loaded = FindAvailableLoadedValue(Load, ScanBB: Load->getParent(),
146 ScanFrom&: BBI, MaxInstsToScan: 0, AA: nullptr, IsLoadCSE: nullptr);
147 GV = dyn_cast<GlobalVariable>(Val: Loaded);
148 if (GV)
149 break;
150 }
151 }
152 }
153 }
154
155 auto It = HandleMap.find(Val: GV);
156 if (It == HandleMap.end()) {
157 diagnoseHandleNotFound(LI);
158 continue;
159 }
160 Changed = true;
161
162 if (!DT.dominates(Def: It->second, User: LI)) {
163 diagnoseUndominatedLoad(LI, Handle: It->second);
164 continue;
165 }
166
167 LLVM_DEBUG(dbgs() << "Replacing uses of " << GV->getName() << " at "
168 << LI->getName() << " with " << It->second->getName()
169 << "\n");
170 LI->replaceAllUsesWith(V: It->second);
171 LI->eraseFromParent();
172 }
173
174 return Changed;
175}
176
177PreservedAnalyses DXILForwardHandleAccesses::run(Function &F,
178 FunctionAnalysisManager &AM) {
179 PreservedAnalyses PA;
180
181 DominatorTree *DT = &AM.getResult<DominatorTreeAnalysis>(IR&: F);
182 bool Changed = forwardHandleAccesses(F, DT&: *DT);
183
184 if (!Changed)
185 return PreservedAnalyses::all();
186 return PA;
187}
188
189namespace {
190class DXILForwardHandleAccessesLegacy : public FunctionPass {
191public:
192 bool runOnFunction(Function &F) override {
193 DominatorTree *DT = &getAnalysis<DominatorTreeWrapperPass>().getDomTree();
194 return forwardHandleAccesses(F, DT&: *DT);
195 }
196 StringRef getPassName() const override {
197 return "DXIL Forward Handle Accesses";
198 }
199
200 void getAnalysisUsage(AnalysisUsage &AU) const override {
201 AU.addRequired<DominatorTreeWrapperPass>();
202 }
203
204 DXILForwardHandleAccessesLegacy() : FunctionPass(ID) {}
205
206 static char ID; // Pass identification.
207};
208char DXILForwardHandleAccessesLegacy::ID = 0;
209} // end anonymous namespace
210
211INITIALIZE_PASS_BEGIN(DXILForwardHandleAccessesLegacy, DEBUG_TYPE,
212 "DXIL Forward Handle Accesses", false, false)
213INITIALIZE_PASS_DEPENDENCY(DominatorTreeWrapperPass)
214INITIALIZE_PASS_END(DXILForwardHandleAccessesLegacy, DEBUG_TYPE,
215 "DXIL Forward Handle Accesses", false, false)
216
217FunctionPass *llvm::createDXILForwardHandleAccessesLegacyPass() {
218 return new DXILForwardHandleAccessesLegacy();
219}
220