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