1//===- DXILResourceAccess.cpp - Resource access via load/store ------------===//
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 "DXILRemoveUnusedResources.h"
10#include "DirectX.h"
11#include "llvm/ADT/SetVector.h"
12#include "llvm/Analysis/VectorUtils.h"
13#include "llvm/IR/BasicBlock.h"
14#include "llvm/IR/DiagnosticInfo.h"
15#include "llvm/IR/Dominators.h"
16#include "llvm/IR/Instruction.h"
17#include "llvm/IR/Instructions.h"
18#include "llvm/IR/IntrinsicInst.h"
19#include "llvm/IR/Intrinsics.h"
20#include "llvm/IR/IntrinsicsDirectX.h"
21#include "llvm/IR/LLVMContext.h"
22#include "llvm/InitializePasses.h"
23#include "llvm/Transforms/Utils/ValueMapper.h"
24
25#define DEBUG_TYPE "dxil-remove-unused-resources"
26
27// Hidden option to disable the pass to make it easier to test
28// other passes related to DXIL resources using llc.
29static llvm::cl::opt<bool> DisableDXILRemoveUnusedResources(
30 "disable-dxil-remove-unused-resources",
31 llvm::cl::desc("Disable dxil-remove-unused-resources pass"),
32 llvm::cl::init(Val: false), llvm::cl::Hidden);
33
34using namespace llvm;
35
36static bool isResourceHandleCreation(Intrinsic::ID ID) {
37 return ID == Intrinsic::dx_resource_handlefrombinding ||
38 ID == Intrinsic::dx_resource_handlefromimplicitbinding ||
39 ID == Intrinsic::dx_resource_handlefromheap;
40}
41
42// Removes all calls to resource handle creation intrinsics that
43// either are not used, or their only use is in a store instruction, which
44// stores the initialized handle into a global variable that does not have
45// external linkage and that is not used anywhere else in the module.
46static bool removeUnusedResources(Function &F) {
47 if (DisableDXILRemoveUnusedResources)
48 return false;
49
50 SmallVector<Instruction *> DeadInstr;
51 SmallSetVector<GlobalVariable *, 4> DeadGlobals;
52 for (BasicBlock &BB : make_early_inc_range(Range&: F)) {
53 for (Instruction &I : BB) {
54 if (auto *II = dyn_cast<IntrinsicInst>(Val: &I)) {
55 if (!isResourceHandleCreation(ID: II->getIntrinsicID()))
56 continue;
57 if (II->user_empty()) {
58 // Initialized handle is not used anywhere.
59 DeadInstr.push_back(Elt: II);
60 continue;
61 }
62 if (!II->hasOneUser())
63 continue;
64
65 // Initialized handle is only used in one store instruction, the store
66 // is into global variable, and that global variable is not used
67 // anywhere else and does not have external linkage.
68 auto *SI = dyn_cast<StoreInst>(Val: *II->user_begin());
69 if (!SI)
70 continue;
71 assert(SI->getValueOperand() == II &&
72 "expected value operand to be the resource handle");
73
74 GlobalVariable *GV = dyn_cast<GlobalVariable>(Val: SI->getPointerOperand());
75 if (!GV || GV->hasExternalLinkage())
76 continue;
77
78 if (GV->hasOneUser()) {
79 assert(*GV->user_begin() == SI &&
80 "expected single user to be the store instruction");
81 DeadInstr.push_back(Elt: SI);
82 DeadInstr.push_back(Elt: II);
83 DeadGlobals.insert(X: GV);
84 }
85 }
86 }
87 }
88
89 if (DeadInstr.empty())
90 return false;
91
92 for (auto *Instr : DeadInstr) {
93 if (auto *II = dyn_cast<IntrinsicInst>(Val: Instr)) {
94 assert(isResourceHandleCreation(II->getIntrinsicID()));
95 // A heap resource does not have an associated global variable to remove.
96 if (II->getIntrinsicID() == Intrinsic::dx_resource_handlefromheap)
97 continue;
98 const unsigned ResourceNameOpIndex = 4;
99 GlobalVariable *ResourceName = dyn_cast_or_null<GlobalVariable>(
100 Val: II->getArgOperand(i: ResourceNameOpIndex));
101 if (ResourceName)
102 DeadGlobals.insert(X: ResourceName);
103 }
104 Instr->eraseFromParent();
105 }
106
107 for (auto *GV : DeadGlobals)
108 if (GV->use_empty())
109 GV->eraseFromParent();
110
111 return true;
112}
113
114PreservedAnalyses DXILRemoveUnusedResources::run(Function &F,
115 FunctionAnalysisManager &AM) {
116 removeUnusedResources(F);
117 return PreservedAnalyses::all();
118}
119
120namespace {
121class DXILRemoveUnusedResourcesLegacy : public FunctionPass {
122public:
123 bool runOnFunction(Function &F) override { return removeUnusedResources(F); }
124 StringRef getPassName() const override {
125 return "DXIL Remove Unused Resources";
126 }
127 DXILRemoveUnusedResourcesLegacy() : FunctionPass(ID) {}
128
129 static char ID; // Pass identification.
130 void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
131 AU.setPreservesAll();
132 }
133};
134char DXILRemoveUnusedResourcesLegacy::ID = 0;
135} // end anonymous namespace
136
137INITIALIZE_PASS_BEGIN(DXILRemoveUnusedResourcesLegacy, DEBUG_TYPE,
138 "DXIL Remove Unused Resources", false, false)
139INITIALIZE_PASS_DEPENDENCY(DXILResourceTypeWrapperPass)
140INITIALIZE_PASS_END(DXILRemoveUnusedResourcesLegacy, DEBUG_TYPE,
141 "DXIL Remove Unused Resources", false, false)
142
143FunctionPass *llvm::createDXILRemoveUnusedResourcesLegacyPass() {
144 return new DXILRemoveUnusedResourcesLegacy();
145}
146