blob: 1a70c896e328bfd10be011220fc2e98985c4afa2 [file] [log] [blame]
Alan Bakerfc6888e2018-08-20 20:54:33 -04001// Copyright 2018 The Clspv Authors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "llvm/ADT/DenseMap.h"
16#include "llvm/ADT/DenseSet.h"
17#include "llvm/ADT/SmallVector.h"
18#include "llvm/ADT/UniqueVector.h"
19#include "llvm/IR/Function.h"
20#include "llvm/IR/Instructions.h"
21#include "llvm/IR/Module.h"
22#include "llvm/Pass.h"
23#include "llvm/Support/raw_ostream.h"
24
25#include "ArgKind.h"
26#include "clspv/AddressSpace.h"
27#include "clspv/Option.h"
28#include "clspv/Passes.h"
29
30using namespace llvm;
31
32namespace {
33
34cl::opt<bool> ShowSMSV("show-smsv", cl::init(false), cl::Hidden,
35 cl::desc("Show share module scope variables details"));
36
37class ShareModuleScopeVariablesPass final : public ModulePass {
38public:
39 typedef DenseMap<Function *, UniqueVector<Function *>> EntryPointMap;
40
41 static char ID;
42 ShareModuleScopeVariablesPass() : ModulePass(ID) {}
43 bool runOnModule(Module &M) override;
44
45private:
46 // Maps functions to entry points that can call them including themselves.
47 void MapEntryPoints(Module &M);
48
49 // Traces the callable functions from |function| and maps them to
50 // |entry_point|.
51 void TraceFunction(Function *function, Function *entry_point);
52
53 // Attempts to share module scope variables. Returns true if any variables are
54 // shared. Shares variables of the same type that are used by
55 // non-intersecting sets of kernels.
56 bool ShareModuleScopeVariables(Module &M);
57
58 // Collects the entry points that can reach |value| into |user_entry_points|.
59 void CollectUserEntryPoints(Value *value,
60 UniqueVector<Function *> *user_entry_points);
61
62 // Returns true if there is an intersection between the |user_functions| and
63 // |other_entry_points|.
64 bool HasSharedEntryPoints(const DenseSet<Function *> &user_functions,
65 const UniqueVector<Function *> &other_entry_points);
66
67 EntryPointMap function_to_entry_points_;
68};
69
70char ShareModuleScopeVariablesPass::ID = 0;
71static RegisterPass<ShareModuleScopeVariablesPass>
72 X("ShareModuleScopeVariablesPass", "Share module scope variables");
73
74} // namespace
75
76namespace clspv {
77ModulePass *createShareModuleScopeVariablesPass() {
78 return new ShareModuleScopeVariablesPass();
79}
80} // namespace clspv
81
82namespace {
83
84bool ShareModuleScopeVariablesPass::runOnModule(Module &M) {
85 bool Changed = false;
86
87 if (clspv::Option::ShareModuleScopeVariables()) {
88 MapEntryPoints(M);
89 Changed = ShareModuleScopeVariables(M);
90 }
91
92 return Changed;
93}
94
95void ShareModuleScopeVariablesPass::MapEntryPoints(Module &M) {
96 // TODO: this could be more efficient if it memoized results for non-kernel
97 // functions.
98 for (auto &func : M) {
99 if (func.isDeclaration() ||
100 func.getCallingConv() != CallingConv::SPIR_KERNEL)
101 continue;
102
103 TraceFunction(&func, &func);
104 }
105}
106
107void ShareModuleScopeVariablesPass::TraceFunction(Function *function,
108 Function *entry_point) {
109 function_to_entry_points_[function].insert(entry_point);
110
111 for (auto &BB : *function) {
112 for (auto &I : BB) {
113 if (auto call = dyn_cast<CallInst>(&I)) {
114 Function *callee = call->getCalledFunction();
115 if (!callee->isDeclaration())
116 TraceFunction(callee, entry_point);
117 }
118 }
119 }
120}
121
122bool ShareModuleScopeVariablesPass::ShareModuleScopeVariables(Module &M) {
123 // Greedily attempts to share global variables.
124 // TODO: this should be analysis driven to aid direct resource access more
125 // directly.
126 bool Changed = false;
127 DenseMap<Value *, UniqueVector<Function *>> global_entry_points;
128 for (auto &G : M.globals()) {
129 if (!clspv::IsLocalPtr(G.getType()))
130 continue;
131
132 auto &entry_points = global_entry_points[&G];
133 CollectUserEntryPoints(&G, &entry_points);
134 }
135
136 SmallVector<GlobalVariable *, 8> dead_globals;
137 for (auto global = M.global_begin(); global != M.global_end(); ++global) {
138 if (!clspv::IsLocalPtr(global->getType()))
139 continue;
140
141 if (global->user_empty())
142 continue;
143
144 auto &entry_points = global_entry_points[&*global];
145 DenseSet<Function *> user_functions;
146 for (auto entry_point : entry_points) {
147 user_functions.insert(entry_point);
148 }
149
150 auto next = global;
151 ++next;
152 for (; next != M.global_end(); ++next) {
153 if (global->getType() != next->getType())
154 continue;
155 if (next->user_empty())
156 continue;
157
158 auto &other_entry_points = global_entry_points[&*next];
159 if (!HasSharedEntryPoints(user_functions, other_entry_points)) {
160 if (ShowSMSV) {
161 outs() << "SMSV: Combining module scope variables\n"
162 << " " << *global << "\n"
163 << " " << *next << "\n";
164 }
165 next->replaceAllUsesWith(&*global);
166 // Don't need to update |entry_points| because we won't revisit this
167 // global variable after the outer loop iteration.
168 for (auto fn : other_entry_points) {
169 user_functions.insert(fn);
170 }
171 dead_globals.push_back(&*next);
172 Changed = true;
173 }
174 }
175 }
176
177 // Remove the unused variables that were merged.
178 for (auto GV : dead_globals) {
179 GV->eraseFromParent();
180 }
181
182 return Changed;
183}
184
185void ShareModuleScopeVariablesPass::CollectUserEntryPoints(
186 Value *value, UniqueVector<Function *> *user_entry_points) {
187 if (auto I = dyn_cast<Instruction>(value)) {
188 Function *function = I->getParent()->getParent();
189 auto &entry_points = function_to_entry_points_[function];
190 for (auto fn : entry_points) {
191 user_entry_points->insert(fn);
192 }
193
194 // We're looking for the first use inside a function to identify the entry
195 // points. Mutations beyond that point do not prevent sharing so we do not
196 // consider them.
197 return;
198 }
199
200 for (auto user : value->users()) {
201 CollectUserEntryPoints(user, user_entry_points);
202 }
203}
204
205bool ShareModuleScopeVariablesPass::HasSharedEntryPoints(
206 const DenseSet<Function *> &user_functions,
207 const UniqueVector<Function *> &other_entry_points) {
208 for (auto fn : other_entry_points) {
209 if (user_functions.count(fn))
210 return true;
211 }
212
213 return false;
214}
215} // namespace