blob: 6ab49ac580d6a858bf6f5a39cc06af9a349b7f98 [file] [log] [blame]
David Neto6373d822017-10-12 19:09:53 -04001// Copyright 2017 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 <string>
16
David Neto118188e2018-08-24 11:27:54 -040017#include "llvm/ADT/DenseMap.h"
18#include "llvm/IR/Attributes.h"
19#include "llvm/IR/DerivedTypes.h"
20#include "llvm/IR/Function.h"
21#include "llvm/IR/Instructions.h"
22#include "llvm/IR/Module.h"
23#include "llvm/Pass.h"
24#include "llvm/Support/raw_ostream.h"
David Neto6373d822017-10-12 19:09:53 -040025
David Neto118188e2018-08-24 11:27:54 -040026#include "clspv/AddressSpace.h"
David Neto6373d822017-10-12 19:09:53 -040027
Diego Novilloa4c44fa2019-04-11 10:56:15 -040028#include "Passes.h"
29
David Neto6373d822017-10-12 19:09:53 -040030using namespace llvm;
31using std::string;
32
33#define DEBUG_TYPE "hideconstantloads"
34
David Neto6373d822017-10-12 19:09:53 -040035namespace {
36
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040037const char *kWrapFunctionPrefix = "clspv.wrap_constant_load.";
David Neto6373d822017-10-12 19:09:53 -040038
39class HideConstantLoadsPass : public ModulePass {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040040public:
David Neto6373d822017-10-12 19:09:53 -040041 static char ID;
42 HideConstantLoadsPass() : ModulePass(ID) {}
43
44 bool runOnModule(Module &M) override;
45
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040046private:
47 // Return the name for the wrap function for the given type.
48 string &WrapFunctionNameForType(Type *type) {
49 auto where = function_for_type_.find(type);
50 if (where == function_for_type_.end()) {
51 // Insert it.
52 auto &result = function_for_type_[type] =
53 string(kWrapFunctionPrefix) +
54 std::to_string(function_for_type_.size());
55 return result;
56 } else {
57 return where->second;
58 }
59 }
David Neto6373d822017-10-12 19:09:53 -040060
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040061 // Maps a loaded type to the name of the wrap function for that type.
62 DenseMap<Type *, string> function_for_type_;
David Neto6373d822017-10-12 19:09:53 -040063};
64} // namespace
65
66char HideConstantLoadsPass::ID = 0;
Diego Novilloa4c44fa2019-04-11 10:56:15 -040067INITIALIZE_PASS(HideConstantLoadsPass, "HideConstantLoads",
alan-baker6a3930b2020-05-21 10:09:11 -040068 "Hide loads from __constant and push constant memory", false,
69 false)
David Neto6373d822017-10-12 19:09:53 -040070
71namespace clspv {
72llvm::ModulePass *createHideConstantLoadsPass() {
73 return new HideConstantLoadsPass();
74}
75} // namespace clspv
76
David Neto6373d822017-10-12 19:09:53 -040077bool HideConstantLoadsPass::runOnModule(Module &M) {
78 bool Changed = false;
79
80 SmallVector<LoadInst *, 16> WorkList;
81 for (Function &F : M) {
82 for (BasicBlock &BB : F) {
83 for (Instruction &I : BB) {
84 if (LoadInst *load = dyn_cast<LoadInst>(&I)) {
alan-baker6a3930b2020-05-21 10:09:11 -040085 if (clspv::AddressSpace::Constant == load->getPointerAddressSpace() ||
86 clspv::AddressSpace::PushConstant ==
87 load->getPointerAddressSpace()) {
David Neto6373d822017-10-12 19:09:53 -040088 WorkList.push_back(load);
89 }
90 }
91 }
92 }
93 }
94
95 if (WorkList.size() == 0) {
96 return Changed;
97 }
98
99 for (LoadInst *load : WorkList) {
100 Changed = true;
101
102 auto loadedTy = load->getType();
103
104 // The wrap function conceptually maps the loaded value to itself.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400105 const string &fn_name = WrapFunctionNameForType(loadedTy);
106 Function *fn = M.getFunction(fn_name);
David Neto6373d822017-10-12 19:09:53 -0400107 if (!fn) {
108 // Make the function.
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400109 FunctionType *fnTy = FunctionType::get(loadedTy, {loadedTy}, false);
David Neto6373d822017-10-12 19:09:53 -0400110 auto fn_constant = M.getOrInsertFunction(fn_name, fnTy);
alan-bakerbccf62c2019-03-29 10:32:41 -0400111 fn = cast<Function>(fn_constant.getCallee());
David Neto6373d822017-10-12 19:09:53 -0400112 fn->addFnAttr(Attribute::ReadOnly);
David Neto6373d822017-10-12 19:09:53 -0400113 }
114
115 // Wrap the load
116 auto call = CallInst::Create(fn, {load});
117 call->insertAfter(load);
118
119 // Replace other uses of the load with the result of the wrap call.
120 {
121 SmallVector<User *, 16> ToReplaceIn;
122 for (auto &use : load->uses()) {
123 User *user = use.getUser();
124 ToReplaceIn.push_back(user);
125 }
126 for (auto *user : ToReplaceIn) {
127 if (dyn_cast<CallInst>(user) != call) {
128 user->replaceUsesOfWith(load, call);
129 }
130 }
131 }
132 }
133
134 return Changed;
135}
136
137namespace {
138class UnhideConstantLoadsPass : public ModulePass {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400139public:
David Neto6373d822017-10-12 19:09:53 -0400140 static char ID;
141 UnhideConstantLoadsPass() : ModulePass(ID) {}
142
143 bool runOnModule(Module &M) override;
144
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400145private:
146 // Maps a loaded type to the name of the wrap function for that type.
147 DenseMap<Type *, string> function_for_type_;
David Neto6373d822017-10-12 19:09:53 -0400148};
149
150} // namespace
151
152char UnhideConstantLoadsPass::ID = 0;
Diego Novilloa4c44fa2019-04-11 10:56:15 -0400153INITIALIZE_PASS(UnhideConstantLoadsPass, "UnhideConstantLoads",
alan-baker6a3930b2020-05-21 10:09:11 -0400154 "Unhide loads from __constant and push constant memory", false,
155 false)
David Neto6373d822017-10-12 19:09:53 -0400156
157namespace clspv {
158llvm::ModulePass *createUnhideConstantLoadsPass() {
159 return new UnhideConstantLoadsPass();
160}
161} // namespace clspv
162
163bool UnhideConstantLoadsPass::runOnModule(Module &M) {
164 bool Changed = false;
165
166 SmallVector<Function *, 16> WorkList;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400167 for (auto &F : M.getFunctionList()) {
David Neto6373d822017-10-12 19:09:53 -0400168 if (F.getName().startswith(kWrapFunctionPrefix)) {
169 WorkList.push_back(&F);
170 }
171 }
172
173 if (WorkList.size() == 0)
174 return Changed;
175
176 SmallVector<CallInst *, 16> RemoveList;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400177 for (auto *F : WorkList) {
178 for (auto &use : F->uses()) {
179 if (auto *call = dyn_cast<CallInst>(use.getUser())) {
David Neto6373d822017-10-12 19:09:53 -0400180 assert(call->getNumArgOperands() == 1);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400181 auto *load = call->getArgOperand(0);
David Neto6373d822017-10-12 19:09:53 -0400182 call->replaceAllUsesWith(load);
183 RemoveList.push_back(call);
184 }
185 }
186 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400187 for (auto *call : RemoveList) {
David Neto6373d822017-10-12 19:09:53 -0400188 call->eraseFromParent();
189 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400190 for (auto *F : WorkList) {
David Neto6373d822017-10-12 19:09:53 -0400191 F->eraseFromParent();
192 }
193
194 return Changed;
195}