blob: 056d030ae1e7b0efc05bc6c0680cee9351a8ebec [file] [log] [blame]
David Netodd992212017-06-23 17:47:55 -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
David Neto156783e2017-07-05 15:39:41 -040015// Cluster POD kernel arguments.
16//
17// Collect plain-old-data kernel arguments and place them into a single
18// struct argument, at the end. Other arguments are pointers, and retain
19// their relative order.
20//
21// We will create a kernel function as the new entry point, and change
22// the original kernel function into a regular SPIR function. Key
23// kernel metadata is moved from the old function to the wrapper.
24// We also attach a "kernel_arg_map" metadata node to the function to
25// encode the mapping from old kernel argument to new kernel argument.
26
David Netodd992212017-06-23 17:47:55 -040027#include <cassert>
28
29#include <llvm/IR/Constants.h>
30#include <llvm/IR/DerivedTypes.h>
31#include <llvm/IR/Function.h>
32#include <llvm/IR/Instructions.h>
33#include <llvm/IR/IRBuilder.h>
David Neto156783e2017-07-05 15:39:41 -040034#include <llvm/IR/Metadata.h>
David Netodd992212017-06-23 17:47:55 -040035#include <llvm/IR/Module.h>
36#include <llvm/Pass.h>
37#include <llvm/Support/raw_ostream.h>
38
David Netodd992212017-06-23 17:47:55 -040039
40using namespace llvm;
41
42#define DEBUG_TYPE "clusterpodkernelargs"
43
44namespace {
45struct ClusterPodKernelArgumentsPass : public ModulePass {
46 static char ID;
47 ClusterPodKernelArgumentsPass() : ModulePass(ID) {}
48
49 bool runOnModule(Module &M) override;
50};
51} // namespace
52
53char ClusterPodKernelArgumentsPass::ID = 0;
54static RegisterPass<ClusterPodKernelArgumentsPass>
55 X("ClusterPodKernelArgumentsPass",
56 "Cluster POD Kernel Arguments Pass");
57
58namespace clspv {
59llvm::ModulePass *createClusterPodKernelArgumentsPass() {
60 return new ClusterPodKernelArgumentsPass();
61}
62} // namespace clspv
63
64bool ClusterPodKernelArgumentsPass::runOnModule(Module &M) {
65 bool Changed = false;
66 LLVMContext &Context = M.getContext();
67
68 SmallVector<Function *, 8> WorkList;
69
70 for (Function &F : M) {
71 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL) {
72 continue;
73 }
74 for (Argument &Arg : F.args()) {
75 if (!isa<PointerType>(Arg.getType())) {
76 WorkList.push_back(&F);
77 break;
78 }
79 }
80 }
81
David Netodd992212017-06-23 17:47:55 -040082 for (Function* F : WorkList) {
83 Changed = true;
84
David Neto156783e2017-07-05 15:39:41 -040085 // An ArgMapping describes how a kernel argument is remapped.
86 struct ArgMapping {
87 std::string name;
88 // 0-based argument index in the old kernel function.
89 unsigned old_index;
90 // 0-based argument index in the new kernel function.
91 unsigned new_index;
92 // Offset of the argument value within the new kernel argument.
93 // This is always zero for non-POD arguments. For a POD argument,
94 // this is the byte offset within the POD arguments struct.
95 unsigned offset;
96 };
97
David Netodd992212017-06-23 17:47:55 -040098 // In OpenCL, kernel arguments are either pointers or POD. A composite with
99 // an element or memeber that is a pointer is not allowed. So we'll use POD
100 // as a shorthand for non-pointer.
101
102 SmallVector<Type *, 8> PtrArgTys;
103 SmallVector<Type *, 8> PodArgTys;
David Neto156783e2017-07-05 15:39:41 -0400104 SmallVector<ArgMapping, 8> RemapInfo;
105 unsigned arg_index = 0;
David Netodd992212017-06-23 17:47:55 -0400106 for (Argument &Arg : F->args()) {
107 Type *ArgTy = Arg.getType();
108 if (isa<PointerType>(ArgTy)) {
109 PtrArgTys.push_back(ArgTy);
David Neto156783e2017-07-05 15:39:41 -0400110 RemapInfo.push_back({std::string(Arg.getName()), arg_index,
111 unsigned(RemapInfo.size()), 0u});
David Netodd992212017-06-23 17:47:55 -0400112 } else {
113 PodArgTys.push_back(ArgTy);
114 }
David Neto156783e2017-07-05 15:39:41 -0400115 arg_index++;
David Netodd992212017-06-23 17:47:55 -0400116 }
117
David Netodd992212017-06-23 17:47:55 -0400118 // Put the pointer arguments first, and then POD arguments struct last.
119 auto PodArgsStructTy =
120 StructType::create(PodArgTys, F->getName().str() + ".podargs");
121 SmallVector<Type *, 8> NewFuncParamTys(PtrArgTys);
122 NewFuncParamTys.push_back(PodArgsStructTy);
123
David Neto156783e2017-07-05 15:39:41 -0400124 // We've recorded the remapping for pointer arguments. Now record the
125 // remapping for POD arguments.
126 {
127 const auto StructLayout =
128 M.getDataLayout().getStructLayout(PodArgsStructTy);
129 arg_index = 0;
130 int pod_index = 0;
131 const unsigned num_pointer_args = unsigned(RemapInfo.size());
132 for (Argument &Arg : F->args()) {
133 Type *ArgTy = Arg.getType();
134 if (!isa<PointerType>(ArgTy)) {
135 RemapInfo.push_back(
136 {std::string(Arg.getName()), arg_index, num_pointer_args,
137 unsigned(StructLayout->getElementOffset(pod_index++))});
138 }
139 arg_index++;
140 }
141 }
142
David Netodd992212017-06-23 17:47:55 -0400143 FunctionType *NewFuncTy =
144 FunctionType::get(F->getReturnType(), NewFuncParamTys, false);
145
146 // Create the new function and set key properties.
147 auto NewFunc = Function::Create(NewFuncTy, F->getLinkage());
148 // The new function adopts the real name so that linkage to the outside
149 // world remains the same.
150 NewFunc->setName(F->getName());
151 F->setName(NewFunc->getName().str() + ".inner");
152
153 NewFunc->setCallingConv(F->getCallingConv());
154 F->setCallingConv(CallingConv::SPIR_FUNC);
155
156 NewFunc->setAttributes(F->getAttributes());
157 // Move OpenCL kernel named attributes.
158 // TODO(dneto): Attributes starting with kernel_arg_* should be rewritten
159 // to reflect change in the argument shape.
160 std::vector<const char *> Metadatas{
161 "reqd_work_group_size", "kernel_arg_addr_space",
162 "kernel_arg_access_qual", "kernel_arg_type",
163 "kernel_arg_base_type", "kernel_arg_type_qual"};
164 for (auto name : Metadatas) {
165 NewFunc->setMetadata(name, F->getMetadata(name));
166 F->setMetadata(name, nullptr);
167 }
168
David Neto156783e2017-07-05 15:39:41 -0400169 IRBuilder<> Builder(BasicBlock::Create(Context, "entry", NewFunc));
170
171 // Set kernel argument mapping metadata.
172 {
173 // Attach a metadata node named "kernel_arg_map" to the new kernel
174 // function. It is a tuple of nodes, each of which is a tuple for
175 // each argument, with members:
176 // - Argument name
177 // - Ordinal index in the original kernel function
178 // - Ordinal index in the new kernel function
179 // - Byte offset within the argument. This is always 0 for pointer
180 // arguments. For POD arguments this is the offest within the POD
181 // argument struct.
182 LLVMContext& Context = M.getContext();
183 SmallVector<Metadata*, 8> mappings;
184 for (auto &arg_mapping : RemapInfo) {
185 auto *name_md = MDString::get(Context, arg_mapping.name);
186 auto *old_index_md =
187 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.old_index));
188 auto *new_index_md =
189 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.new_index));
190 auto *offset =
191 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.offset));
192 auto *arg_md =
193 MDNode::get(Context, {name_md, old_index_md, new_index_md, offset});
194 mappings.push_back(arg_md);
195 }
196
197 NewFunc->setMetadata("kernel_arg_map", MDNode::get(Context, mappings));
198 }
199
David Netodd992212017-06-23 17:47:55 -0400200 // Insert the function after the original, to preserve ordering
201 // in the module as much as possible.
202 auto &FunctionList = M.getFunctionList();
203 for (auto Iter = FunctionList.begin(), IterEnd = FunctionList.end();
204 Iter != IterEnd; ++Iter) {
205 if (&*Iter == F) {
206 FunctionList.insertAfter(Iter, NewFunc);
207 break;
208 }
209 }
210
211 // The body of the wrapper is essentially a call to the original function,
212 // but we have to unwrap the non-pointer arguments from the struct.
David Netodd992212017-06-23 17:47:55 -0400213
214 // Map the wrapper's arguments to the callee's arguments.
215 SmallVector<Argument *, 8> CallerArgs;
216 for (Argument &Arg : NewFunc->args()) {
217 CallerArgs.push_back(&Arg);
218 }
219 Argument *PodArg = CallerArgs.back();
220 PodArg->setName("podargs");
221
222 SmallVector<Value *, 8> CalleeArgs;
223 unsigned podIndex = 0;
224 unsigned ptrIndex = 0;
225 for (const Argument &Arg : F->args()) {
226 if (isa<PointerType>(Arg.getType())) {
227 CalleeArgs.push_back(CallerArgs[ptrIndex++]);
228 } else {
229 CalleeArgs.push_back(Builder.CreateExtractValue(PodArg, {podIndex++}));
230 }
231 CalleeArgs.back()->setName(Arg.getName());
232 }
233 assert(ptrIndex + podIndex == F->arg_size());
234 assert(ptrIndex = PtrArgTys.size());
235 assert(podIndex = PodArgTys.size());
236
237 auto Call = Builder.CreateCall(F, CalleeArgs);
238 Call->setCallingConv(F->getCallingConv());
239
240 Builder.CreateRetVoid();
241 }
242
243 return Changed;
244}