blob: 61d6282ebc97631225304228e9f9fc2b07964338 [file] [log] [blame]
alan-baker4217b322019-03-06 08:56:12 -05001// Copyright 2019 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/IR/CallingConv.h"
16#include "llvm/IR/Constants.h"
17#include "llvm/IR/Function.h"
18#include "llvm/IR/Instructions.h"
19#include "llvm/IR/Module.h"
20#include "llvm/Pass.h"
21#include "llvm/Support/raw_ostream.h"
22
23#include "clspv/Option.h"
24#include "clspv/Passes.h"
25
26using namespace llvm;
27
28namespace {
29
30class RemoveUnusedArguments final : public ModulePass {
31public:
32 static char ID;
33 RemoveUnusedArguments() : ModulePass(ID) {}
34 bool runOnModule(Module &M) override;
35
36private:
37 struct Candidate {
38 Function *function;
39 SmallVector<Value *, 8> args;
40 };
41
42 // Populate |candidates| with non-kernel functions that have unused function
43 // parameters. Returns true if any such functions are found.
44 bool findCandidates(Module &M, std::vector<Candidate> *candidates);
45
46 // Remove unused parameters in |candidates|. Rebuilds the functions without
47 // the unused parameters. Updates calls and metadata to use the new function.
48 void removeUnusedParameters(Module &M,
49 const std::vector<Candidate> &candidates);
50};
51
52char RemoveUnusedArguments::ID = 0;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040053static RegisterPass<RemoveUnusedArguments>
54 X("RemoveUnusuedArguments",
55 "Remove unused arguments from non-kernel functions");
56} // namespace
alan-baker4217b322019-03-06 08:56:12 -050057
58namespace clspv {
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040059ModulePass *createRemoveUnusedArgumentsPass() {
60 return new RemoveUnusedArguments();
alan-baker4217b322019-03-06 08:56:12 -050061}
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040062} // namespace clspv
alan-baker4217b322019-03-06 08:56:12 -050063
64namespace {
65
66bool RemoveUnusedArguments::runOnModule(Module &M) {
67 if (clspv::Option::KeepUnusedArguments())
68 return false;
69
70 std::vector<Candidate> candidates;
71 bool changed = findCandidates(M, &candidates);
72 removeUnusedParameters(M, candidates);
73
74 return changed;
75}
76
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040077bool RemoveUnusedArguments::findCandidates(Module &M,
78 std::vector<Candidate> *candidates) {
alan-baker4217b322019-03-06 08:56:12 -050079 bool changed = false;
80 for (auto &F : M) {
81 // Don't modify kernel functions.
82 if (F.isDeclaration() || F.getCallingConv() == CallingConv::SPIR_KERNEL)
83 continue;
84
85 if (F.getFunctionType()->isVarArg())
86 continue;
87
88 size_t i = 0;
89 bool local_changed = false;
90 SmallVector<Value *, 8> args;
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040091 for (auto &Arg : F.args()) {
alan-baker4217b322019-03-06 08:56:12 -050092 if (Arg.use_empty()) {
93 local_changed = true;
94 args.push_back(nullptr);
95 } else {
96 ++i;
97 args.push_back(&Arg);
98 }
99 }
100
101 if (local_changed) {
102 candidates->push_back({&F, args});
103 changed = true;
104 }
105 }
106
107 return changed;
108}
109
110void RemoveUnusedArguments::removeUnusedParameters(
111 Module &M, const std::vector<Candidate> &candidates) {
112 for (auto &candidate : candidates) {
113 Function *f = candidate.function;
114 f->removeFromParent();
115
116 // Rebuild the type.
117 SmallVector<Type *, 8> arg_types;
118 for (auto *arg : candidate.args) {
119 if (arg) {
120 arg_types.push_back(arg->getType());
121 }
122 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400123 FunctionType *new_type =
124 FunctionType::get(f->getReturnType(), arg_types, false);
alan-baker4217b322019-03-06 08:56:12 -0500125
126 // Insert the new function. Copy the calling convention, attributes and
127 // metadata.
alan-bakerbccf62c2019-03-29 10:32:41 -0400128 auto inserted =
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400129 M.getOrInsertFunction(f->getName(), new_type, f->getAttributes())
130 .getCallee();
alan-baker4217b322019-03-06 08:56:12 -0500131 Function *new_function = cast<Function>(inserted);
132 new_function->setCallingConv(f->getCallingConv());
133 new_function->copyMetadata(f, 0);
134
135 // Move the basic blocks into the new function.
136 if (!f->isDeclaration()) {
137 std::vector<BasicBlock *> blocks;
138 for (auto &BB : *f) {
139 blocks.push_back(&BB);
140 }
141 for (auto *BB : blocks) {
142 BB->removeFromParent();
143 BB->insertInto(new_function);
144 }
145 }
146
147 // Replace uses of remaining args.
148 auto new_arg_iter = new_function->arg_begin();
149 for (size_t old_arg_index = 0; old_arg_index < candidate.args.size();
150 ++old_arg_index) {
151 if (auto *arg = candidate.args[old_arg_index]) {
152 arg->replaceAllUsesWith(&*new_arg_iter);
153 ++new_arg_iter;
154 }
155 }
156
157 // Update calls to the old function.
158 std::vector<User *> users;
159 for (auto *U : f->users()) {
160 users.push_back(U);
161 }
162
163 for (auto *U : users) {
164 if (auto *call = dyn_cast<CallInst>(U)) {
165 SmallVector<Value *, 8> args;
166 for (size_t i = 0; i < candidate.args.size(); ++i) {
167 if (candidate.args[i]) {
168 args.push_back(call->getOperand(i));
169 }
170 }
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400171 CallInst *new_call =
172 CallInst::Create(new_type, new_function, args, "", call);
alan-baker4217b322019-03-06 08:56:12 -0500173 new_call->takeName(call);
174 call->replaceAllUsesWith(new_call);
175 call->eraseFromParent();
176 }
177 }
178
179 // Now we can delete the old version.
180 delete f;
181 }
182}
183
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400184} // namespace