blob: 2dea19ae4301f8fcc25bbc5bc4e45ce7a9a663d4 [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;
53static RegisterPass<RemoveUnusedArguments> X("RemoveUnusuedArguments",
54 "Remove unused arguments from non-kernel functions");
55}
56
57namespace clspv {
58ModulePass *createRemoveUnusedArgumentsPass() { return new RemoveUnusedArguments(); }
59}
60
61namespace {
62
63bool RemoveUnusedArguments::runOnModule(Module &M) {
64 if (clspv::Option::KeepUnusedArguments())
65 return false;
66
67 std::vector<Candidate> candidates;
68 bool changed = findCandidates(M, &candidates);
69 removeUnusedParameters(M, candidates);
70
71 return changed;
72}
73
74bool RemoveUnusedArguments::findCandidates(Module &M, std::vector<Candidate> *candidates) {
75 bool changed = false;
76 for (auto &F : M) {
77 // Don't modify kernel functions.
78 if (F.isDeclaration() || F.getCallingConv() == CallingConv::SPIR_KERNEL)
79 continue;
80
81 if (F.getFunctionType()->isVarArg())
82 continue;
83
84 size_t i = 0;
85 bool local_changed = false;
86 SmallVector<Value *, 8> args;
87 for (auto &Arg: F.args()) {
88 if (Arg.use_empty()) {
89 local_changed = true;
90 args.push_back(nullptr);
91 } else {
92 ++i;
93 args.push_back(&Arg);
94 }
95 }
96
97 if (local_changed) {
98 candidates->push_back({&F, args});
99 changed = true;
100 }
101 }
102
103 return changed;
104}
105
106void RemoveUnusedArguments::removeUnusedParameters(
107 Module &M, const std::vector<Candidate> &candidates) {
108 for (auto &candidate : candidates) {
109 Function *f = candidate.function;
110 f->removeFromParent();
111
112 // Rebuild the type.
113 SmallVector<Type *, 8> arg_types;
114 for (auto *arg : candidate.args) {
115 if (arg) {
116 arg_types.push_back(arg->getType());
117 }
118 }
119 FunctionType *new_type = FunctionType::get(f->getReturnType(), arg_types, false);
120
121 // Insert the new function. Copy the calling convention, attributes and
122 // metadata.
alan-bakerbccf62c2019-03-29 10:32:41 -0400123 auto inserted =
124 M.getOrInsertFunction(f->getName(), new_type, f->getAttributes()).getCallee();
alan-baker4217b322019-03-06 08:56:12 -0500125 Function *new_function = cast<Function>(inserted);
126 new_function->setCallingConv(f->getCallingConv());
127 new_function->copyMetadata(f, 0);
128
129 // Move the basic blocks into the new function.
130 if (!f->isDeclaration()) {
131 std::vector<BasicBlock *> blocks;
132 for (auto &BB : *f) {
133 blocks.push_back(&BB);
134 }
135 for (auto *BB : blocks) {
136 BB->removeFromParent();
137 BB->insertInto(new_function);
138 }
139 }
140
141 // Replace uses of remaining args.
142 auto new_arg_iter = new_function->arg_begin();
143 for (size_t old_arg_index = 0; old_arg_index < candidate.args.size();
144 ++old_arg_index) {
145 if (auto *arg = candidate.args[old_arg_index]) {
146 arg->replaceAllUsesWith(&*new_arg_iter);
147 ++new_arg_iter;
148 }
149 }
150
151 // Update calls to the old function.
152 std::vector<User *> users;
153 for (auto *U : f->users()) {
154 users.push_back(U);
155 }
156
157 for (auto *U : users) {
158 if (auto *call = dyn_cast<CallInst>(U)) {
159 SmallVector<Value *, 8> args;
160 for (size_t i = 0; i < candidate.args.size(); ++i) {
161 if (candidate.args[i]) {
162 args.push_back(call->getOperand(i));
163 }
164 }
165 CallInst *new_call = CallInst::Create(new_type, new_function, args, "", call);
166 new_call->takeName(call);
167 call->replaceAllUsesWith(new_call);
168 call->eraseFromParent();
169 }
170 }
171
172 // Now we can delete the old version.
173 delete f;
174 }
175}
176
177}