blob: 61f754c848ae7ad542862ef023436115a406f81a [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
Alan Bakerfc6888e2018-08-20 20:54:33 -040025#include "clspv/AddressSpace.h"
26#include "clspv/Option.h"
Diego Novilloa4c44fa2019-04-11 10:56:15 -040027
28#include "ArgKind.h"
29#include "Passes.h"
Alan Bakerfc6888e2018-08-20 20:54:33 -040030
31using namespace llvm;
32
33namespace {
34
35cl::opt<bool> ShowSMSV("show-smsv", cl::init(false), cl::Hidden,
36 cl::desc("Show share module scope variables details"));
37
38class ShareModuleScopeVariablesPass final : public ModulePass {
39public:
40 typedef DenseMap<Function *, UniqueVector<Function *>> EntryPointMap;
41
42 static char ID;
43 ShareModuleScopeVariablesPass() : ModulePass(ID) {}
44 bool runOnModule(Module &M) override;
45
46private:
47 // Maps functions to entry points that can call them including themselves.
48 void MapEntryPoints(Module &M);
49
50 // Traces the callable functions from |function| and maps them to
51 // |entry_point|.
52 void TraceFunction(Function *function, Function *entry_point);
53
54 // Attempts to share module scope variables. Returns true if any variables are
55 // shared. Shares variables of the same type that are used by
56 // non-intersecting sets of kernels.
57 bool ShareModuleScopeVariables(Module &M);
58
59 // Collects the entry points that can reach |value| into |user_entry_points|.
60 void CollectUserEntryPoints(Value *value,
61 UniqueVector<Function *> *user_entry_points);
62
63 // Returns true if there is an intersection between the |user_functions| and
64 // |other_entry_points|.
65 bool HasSharedEntryPoints(const DenseSet<Function *> &user_functions,
66 const UniqueVector<Function *> &other_entry_points);
67
68 EntryPointMap function_to_entry_points_;
69};
70
Alan Bakerfc6888e2018-08-20 20:54:33 -040071} // namespace
72
Diego Novilloa4c44fa2019-04-11 10:56:15 -040073char ShareModuleScopeVariablesPass::ID = 0;
74INITIALIZE_PASS(ShareModuleScopeVariablesPass, "ShareModuleScopeVariablesPass",
75 "Share module scope variables", false, false)
76
Alan Bakerfc6888e2018-08-20 20:54:33 -040077namespace clspv {
78ModulePass *createShareModuleScopeVariablesPass() {
79 return new ShareModuleScopeVariablesPass();
80}
81} // namespace clspv
82
83namespace {
84
85bool ShareModuleScopeVariablesPass::runOnModule(Module &M) {
86 bool Changed = false;
87
88 if (clspv::Option::ShareModuleScopeVariables()) {
89 MapEntryPoints(M);
90 Changed = ShareModuleScopeVariables(M);
91 }
92
93 return Changed;
94}
95
96void ShareModuleScopeVariablesPass::MapEntryPoints(Module &M) {
97 // TODO: this could be more efficient if it memoized results for non-kernel
98 // functions.
99 for (auto &func : M) {
100 if (func.isDeclaration() ||
101 func.getCallingConv() != CallingConv::SPIR_KERNEL)
102 continue;
103
104 TraceFunction(&func, &func);
105 }
106}
107
108void ShareModuleScopeVariablesPass::TraceFunction(Function *function,
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400109 Function *entry_point) {
Alan Bakerfc6888e2018-08-20 20:54:33 -0400110 function_to_entry_points_[function].insert(entry_point);
111
112 for (auto &BB : *function) {
113 for (auto &I : BB) {
114 if (auto call = dyn_cast<CallInst>(&I)) {
115 Function *callee = call->getCalledFunction();
116 if (!callee->isDeclaration())
117 TraceFunction(callee, entry_point);
118 }
119 }
120 }
121}
122
123bool ShareModuleScopeVariablesPass::ShareModuleScopeVariables(Module &M) {
124 // Greedily attempts to share global variables.
125 // TODO: this should be analysis driven to aid direct resource access more
126 // directly.
127 bool Changed = false;
128 DenseMap<Value *, UniqueVector<Function *>> global_entry_points;
129 for (auto &G : M.globals()) {
130 if (!clspv::IsLocalPtr(G.getType()))
131 continue;
132
133 auto &entry_points = global_entry_points[&G];
134 CollectUserEntryPoints(&G, &entry_points);
135 }
136
137 SmallVector<GlobalVariable *, 8> dead_globals;
138 for (auto global = M.global_begin(); global != M.global_end(); ++global) {
139 if (!clspv::IsLocalPtr(global->getType()))
140 continue;
141
142 if (global->user_empty())
143 continue;
144
145 auto &entry_points = global_entry_points[&*global];
146 DenseSet<Function *> user_functions;
147 for (auto entry_point : entry_points) {
148 user_functions.insert(entry_point);
149 }
150
151 auto next = global;
152 ++next;
153 for (; next != M.global_end(); ++next) {
154 if (global->getType() != next->getType())
155 continue;
156 if (next->user_empty())
157 continue;
158
159 auto &other_entry_points = global_entry_points[&*next];
160 if (!HasSharedEntryPoints(user_functions, other_entry_points)) {
161 if (ShowSMSV) {
162 outs() << "SMSV: Combining module scope variables\n"
163 << " " << *global << "\n"
164 << " " << *next << "\n";
165 }
166 next->replaceAllUsesWith(&*global);
167 // Don't need to update |entry_points| because we won't revisit this
168 // global variable after the outer loop iteration.
169 for (auto fn : other_entry_points) {
170 user_functions.insert(fn);
171 }
172 dead_globals.push_back(&*next);
173 Changed = true;
174 }
175 }
176 }
177
178 // Remove the unused variables that were merged.
179 for (auto GV : dead_globals) {
180 GV->eraseFromParent();
181 }
182
183 return Changed;
184}
185
186void ShareModuleScopeVariablesPass::CollectUserEntryPoints(
187 Value *value, UniqueVector<Function *> *user_entry_points) {
188 if (auto I = dyn_cast<Instruction>(value)) {
189 Function *function = I->getParent()->getParent();
190 auto &entry_points = function_to_entry_points_[function];
191 for (auto fn : entry_points) {
192 user_entry_points->insert(fn);
193 }
194
195 // We're looking for the first use inside a function to identify the entry
196 // points. Mutations beyond that point do not prevent sharing so we do not
197 // consider them.
198 return;
199 }
200
201 for (auto user : value->users()) {
202 CollectUserEntryPoints(user, user_entry_points);
203 }
204}
205
206bool ShareModuleScopeVariablesPass::HasSharedEntryPoints(
207 const DenseSet<Function *> &user_functions,
208 const UniqueVector<Function *> &other_entry_points) {
209 for (auto fn : other_entry_points) {
210 if (user_functions.count(fn))
211 return true;
212 }
213
214 return false;
215}
216} // namespace