blob: 35a93646a2bffae02755303ff0765631c1d9156a [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>
David Netoc6f3ab22018-04-06 18:02:31 -040028#include <cstring>
David Netodd992212017-06-23 17:47:55 -040029
David Netoc6f3ab22018-04-06 18:02:31 -040030#include "llvm/IR/Constants.h"
31#include "llvm/IR/DerivedTypes.h"
32#include "llvm/IR/Function.h"
33#include "llvm/IR/Instructions.h"
34#include "llvm/IR/IRBuilder.h"
35#include "llvm/IR/Metadata.h"
36#include "llvm/IR/Module.h"
37#include "llvm/Pass.h"
38#include "llvm/Support/CommandLine.h"
39#include "llvm/Support/raw_ostream.h"
40#include "llvm/Transforms/Utils/Cloning.h"
David Netodd992212017-06-23 17:47:55 -040041
David Neto4feb7a42017-10-06 17:29:42 -040042#include "ArgKind.h"
David Netodd992212017-06-23 17:47:55 -040043
44using namespace llvm;
45
46#define DEBUG_TYPE "clusterpodkernelargs"
47
48namespace {
49struct ClusterPodKernelArgumentsPass : public ModulePass {
50 static char ID;
51 ClusterPodKernelArgumentsPass() : ModulePass(ID) {}
52
53 bool runOnModule(Module &M) override;
54};
David Neto48f56a42017-10-06 16:44:25 -040055
David Netodd992212017-06-23 17:47:55 -040056} // namespace
57
58char ClusterPodKernelArgumentsPass::ID = 0;
59static RegisterPass<ClusterPodKernelArgumentsPass>
60 X("ClusterPodKernelArgumentsPass",
61 "Cluster POD Kernel Arguments Pass");
62
63namespace clspv {
64llvm::ModulePass *createClusterPodKernelArgumentsPass() {
65 return new ClusterPodKernelArgumentsPass();
66}
67} // namespace clspv
68
69bool ClusterPodKernelArgumentsPass::runOnModule(Module &M) {
70 bool Changed = false;
71 LLVMContext &Context = M.getContext();
72
73 SmallVector<Function *, 8> WorkList;
74
75 for (Function &F : M) {
76 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL) {
77 continue;
78 }
79 for (Argument &Arg : F.args()) {
80 if (!isa<PointerType>(Arg.getType())) {
81 WorkList.push_back(&F);
82 break;
83 }
84 }
85 }
86
David Netod5b3f982017-09-28 14:49:49 -040087 SmallVector<CallInst*, 8> CallList;
88
David Netoc6f3ab22018-04-06 18:02:31 -040089 // Note: The transformation done in this pass preserves the pointer-to-local
90 // arg to spec-id mapping.
91 clspv::ArgIdMapType arg_spec_id_map = clspv::AllocateArgSpecIds(M);
92
David Netodd992212017-06-23 17:47:55 -040093 for (Function* F : WorkList) {
94 Changed = true;
95
David Neto156783e2017-07-05 15:39:41 -040096 // An ArgMapping describes how a kernel argument is remapped.
97 struct ArgMapping {
98 std::string name;
99 // 0-based argument index in the old kernel function.
100 unsigned old_index;
101 // 0-based argument index in the new kernel function.
David Netoc6f3ab22018-04-06 18:02:31 -0400102 int new_index;
David Neto156783e2017-07-05 15:39:41 -0400103 // Offset of the argument value within the new kernel argument.
104 // This is always zero for non-POD arguments. For a POD argument,
105 // this is the byte offset within the POD arguments struct.
106 unsigned offset;
Kévin PETITa353c832018-03-20 23:21:21 +0000107 // Size of the argument
108 unsigned arg_size;
Kévin Petit8bea15e2019-04-09 14:05:17 +0100109 // Argument type.
110 clspv::ArgKind arg_kind;
David Netoc6f3ab22018-04-06 18:02:31 -0400111 // If non-negative, this argument is a pointer-to-local, and the value
112 // here is the specialization constant id for the array size.
113 int spec_id;
David Neto156783e2017-07-05 15:39:41 -0400114 };
115
David Netodd992212017-06-23 17:47:55 -0400116 // In OpenCL, kernel arguments are either pointers or POD. A composite with
Kévin Petit921c1ab2019-03-19 21:25:44 +0000117 // an element or member that is a pointer is not allowed. So we'll use POD
David Netodd992212017-06-23 17:47:55 -0400118 // as a shorthand for non-pointer.
119
120 SmallVector<Type *, 8> PtrArgTys;
121 SmallVector<Type *, 8> PodArgTys;
David Neto156783e2017-07-05 15:39:41 -0400122 SmallVector<ArgMapping, 8> RemapInfo;
123 unsigned arg_index = 0;
David Netoc6f3ab22018-04-06 18:02:31 -0400124 int new_index = 0;
David Netodd992212017-06-23 17:47:55 -0400125 for (Argument &Arg : F->args()) {
126 Type *ArgTy = Arg.getType();
127 if (isa<PointerType>(ArgTy)) {
128 PtrArgTys.push_back(ArgTy);
David Neto862b7d82018-06-14 18:48:37 -0400129 const auto kind = clspv::GetArgKindForType(ArgTy);
David Netoc6f3ab22018-04-06 18:02:31 -0400130 int spec_id = -1;
David Neto862b7d82018-06-14 18:48:37 -0400131 if (kind == clspv::ArgKind::Local) {
David Netoc6f3ab22018-04-06 18:02:31 -0400132 spec_id = arg_spec_id_map[&Arg];
133 assert(spec_id > 0);
134 }
David Neto862b7d82018-06-14 18:48:37 -0400135 RemapInfo.push_back({std::string(Arg.getName()), arg_index, new_index++,
Kévin Petit8bea15e2019-04-09 14:05:17 +0100136 0u, 0u, kind, spec_id});
David Netodd992212017-06-23 17:47:55 -0400137 } else {
138 PodArgTys.push_back(ArgTy);
139 }
David Neto156783e2017-07-05 15:39:41 -0400140 arg_index++;
David Netodd992212017-06-23 17:47:55 -0400141 }
142
David Netodd992212017-06-23 17:47:55 -0400143 // Put the pointer arguments first, and then POD arguments struct last.
David Neto2ded02e2017-10-23 15:30:59 -0400144 // Use StructType::get so we reuse types where possible.
145 auto PodArgsStructTy = StructType::get(Context, PodArgTys);
David Netodd992212017-06-23 17:47:55 -0400146 SmallVector<Type *, 8> NewFuncParamTys(PtrArgTys);
147 NewFuncParamTys.push_back(PodArgsStructTy);
148
David Neto156783e2017-07-05 15:39:41 -0400149 // We've recorded the remapping for pointer arguments. Now record the
150 // remapping for POD arguments.
151 {
Kévin PETITa353c832018-03-20 23:21:21 +0000152 const DataLayout DL(&M);
153 const auto StructLayout = DL.getStructLayout(PodArgsStructTy);
David Neto156783e2017-07-05 15:39:41 -0400154 arg_index = 0;
155 int pod_index = 0;
David Neto156783e2017-07-05 15:39:41 -0400156 for (Argument &Arg : F->args()) {
157 Type *ArgTy = Arg.getType();
158 if (!isa<PointerType>(ArgTy)) {
Kévin PETITa353c832018-03-20 23:21:21 +0000159 unsigned arg_size = DL.getTypeStoreSize(ArgTy);
David Neto156783e2017-07-05 15:39:41 -0400160 RemapInfo.push_back(
David Netoc6f3ab22018-04-06 18:02:31 -0400161 {std::string(Arg.getName()), arg_index, new_index,
David Neto48f56a42017-10-06 16:44:25 -0400162 unsigned(StructLayout->getElementOffset(pod_index++)),
Kévin Petit8bea15e2019-04-09 14:05:17 +0100163 arg_size, clspv::GetArgKindForType(ArgTy), -1});
David Neto156783e2017-07-05 15:39:41 -0400164 }
165 arg_index++;
166 }
167 }
168
David Netodd992212017-06-23 17:47:55 -0400169 FunctionType *NewFuncTy =
170 FunctionType::get(F->getReturnType(), NewFuncParamTys, false);
171
172 // Create the new function and set key properties.
173 auto NewFunc = Function::Create(NewFuncTy, F->getLinkage());
174 // The new function adopts the real name so that linkage to the outside
175 // world remains the same.
176 NewFunc->setName(F->getName());
177 F->setName(NewFunc->getName().str() + ".inner");
178
179 NewFunc->setCallingConv(F->getCallingConv());
180 F->setCallingConv(CallingConv::SPIR_FUNC);
181
Kévin Petit921c1ab2019-03-19 21:25:44 +0000182 // Transfer attributes that don't apply to the POD arguments
183 // to the new functions.
184 auto Attributes = F->getAttributes();
185 SmallVector<std::pair<unsigned, AttributeSet>, 8> AttrBuildInfo;
186
187 // Return attributes have to come first
188 if (Attributes.hasAttributes(AttributeList::ReturnIndex)) {
189 auto idx = AttributeList::ReturnIndex;
190 auto attrs = Attributes.getRetAttributes();
191 AttrBuildInfo.push_back(std::make_pair(idx, attrs));
192 }
193
Kévin Petit8bea15e2019-04-09 14:05:17 +0100194 // Then attributes for non-POD parameters
Kévin Petit921c1ab2019-03-19 21:25:44 +0000195 for (auto &rinfo : RemapInfo) {
Kévin Petit8bea15e2019-04-09 14:05:17 +0100196 bool argIsPod = rinfo.arg_kind == clspv::ArgKind::Pod ||
197 rinfo.arg_kind == clspv::ArgKind::PodUBO;
198 if (!argIsPod && Attributes.hasParamAttrs(rinfo.old_index)) {
Kévin Petit921c1ab2019-03-19 21:25:44 +0000199 auto idx = rinfo.new_index + AttributeList::FirstArgIndex;
200 auto attrs = Attributes.getParamAttributes(rinfo.old_index);
201 AttrBuildInfo.push_back(std::make_pair(idx, attrs));
202 }
203 }
204
alan-bakerbccf62c2019-03-29 10:32:41 -0400205 // And finally function attributes.
Kévin Petit921c1ab2019-03-19 21:25:44 +0000206 if (Attributes.hasAttributes(AttributeList::FunctionIndex)) {
Kévin Petit921c1ab2019-03-19 21:25:44 +0000207 auto idx = AttributeList::FunctionIndex;
alan-bakerbccf62c2019-03-29 10:32:41 -0400208 auto attrs = Attributes.getFnAttributes();
209 AttrBuildInfo.push_back(std::make_pair(idx, attrs));
Kévin Petit921c1ab2019-03-19 21:25:44 +0000210 }
alan-bakerbccf62c2019-03-29 10:32:41 -0400211 auto newAttributes = AttributeList::get(M.getContext(), AttrBuildInfo);
Kévin Petit921c1ab2019-03-19 21:25:44 +0000212 NewFunc->setAttributes(newAttributes);
213
David Netodd992212017-06-23 17:47:55 -0400214 // Move OpenCL kernel named attributes.
215 // TODO(dneto): Attributes starting with kernel_arg_* should be rewritten
216 // to reflect change in the argument shape.
217 std::vector<const char *> Metadatas{
218 "reqd_work_group_size", "kernel_arg_addr_space",
219 "kernel_arg_access_qual", "kernel_arg_type",
220 "kernel_arg_base_type", "kernel_arg_type_qual"};
221 for (auto name : Metadatas) {
222 NewFunc->setMetadata(name, F->getMetadata(name));
223 F->setMetadata(name, nullptr);
224 }
225
David Neto156783e2017-07-05 15:39:41 -0400226 IRBuilder<> Builder(BasicBlock::Create(Context, "entry", NewFunc));
227
228 // Set kernel argument mapping metadata.
229 {
230 // Attach a metadata node named "kernel_arg_map" to the new kernel
231 // function. It is a tuple of nodes, each of which is a tuple for
232 // each argument, with members:
233 // - Argument name
234 // - Ordinal index in the original kernel function
235 // - Ordinal index in the new kernel function
236 // - Byte offset within the argument. This is always 0 for pointer
237 // arguments. For POD arguments this is the offest within the POD
238 // argument struct.
David Neto48f56a42017-10-06 16:44:25 -0400239 // - Argument type
David Neto156783e2017-07-05 15:39:41 -0400240 LLVMContext& Context = M.getContext();
241 SmallVector<Metadata*, 8> mappings;
242 for (auto &arg_mapping : RemapInfo) {
243 auto *name_md = MDString::get(Context, arg_mapping.name);
244 auto *old_index_md =
245 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.old_index));
David Netoc6f3ab22018-04-06 18:02:31 -0400246 auto *new_index_md = ConstantAsMetadata::get(
247 Builder.getInt32(arg_mapping.new_index));
248 auto *offset_md =
David Neto156783e2017-07-05 15:39:41 -0400249 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.offset));
Kévin PETITa353c832018-03-20 23:21:21 +0000250 auto *arg_size_md =
251 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.arg_size));
Kévin Petit8bea15e2019-04-09 14:05:17 +0100252 auto argKindName = GetArgKindName(arg_mapping.arg_kind);
253 auto *argtype_md = MDString::get(Context, argKindName);
David Netoc6f3ab22018-04-06 18:02:31 -0400254 auto *spec_id_md = ConstantAsMetadata::get(
255 Builder.getInt32(arg_mapping.spec_id));
256 auto *arg_md =
257 MDNode::get(Context, {name_md, old_index_md, new_index_md,
Kévin PETITa353c832018-03-20 23:21:21 +0000258 offset_md, arg_size_md, argtype_md, spec_id_md});
David Neto156783e2017-07-05 15:39:41 -0400259 mappings.push_back(arg_md);
260 }
261
262 NewFunc->setMetadata("kernel_arg_map", MDNode::get(Context, mappings));
263 }
264
David Netodd992212017-06-23 17:47:55 -0400265 // Insert the function after the original, to preserve ordering
266 // in the module as much as possible.
267 auto &FunctionList = M.getFunctionList();
268 for (auto Iter = FunctionList.begin(), IterEnd = FunctionList.end();
269 Iter != IterEnd; ++Iter) {
270 if (&*Iter == F) {
271 FunctionList.insertAfter(Iter, NewFunc);
272 break;
273 }
274 }
275
276 // The body of the wrapper is essentially a call to the original function,
277 // but we have to unwrap the non-pointer arguments from the struct.
David Netodd992212017-06-23 17:47:55 -0400278
279 // Map the wrapper's arguments to the callee's arguments.
280 SmallVector<Argument *, 8> CallerArgs;
281 for (Argument &Arg : NewFunc->args()) {
282 CallerArgs.push_back(&Arg);
283 }
284 Argument *PodArg = CallerArgs.back();
285 PodArg->setName("podargs");
286
287 SmallVector<Value *, 8> CalleeArgs;
288 unsigned podIndex = 0;
289 unsigned ptrIndex = 0;
290 for (const Argument &Arg : F->args()) {
291 if (isa<PointerType>(Arg.getType())) {
292 CalleeArgs.push_back(CallerArgs[ptrIndex++]);
293 } else {
294 CalleeArgs.push_back(Builder.CreateExtractValue(PodArg, {podIndex++}));
295 }
296 CalleeArgs.back()->setName(Arg.getName());
297 }
298 assert(ptrIndex + podIndex == F->arg_size());
Kévin Petit98d9c332019-03-13 15:03:40 +0000299 assert(ptrIndex == PtrArgTys.size());
300 assert(podIndex != 0);
301 assert(podIndex == PodArgTys.size());
David Netodd992212017-06-23 17:47:55 -0400302
303 auto Call = Builder.CreateCall(F, CalleeArgs);
304 Call->setCallingConv(F->getCallingConv());
David Netod5b3f982017-09-28 14:49:49 -0400305 CallList.push_back(Call);
David Netodd992212017-06-23 17:47:55 -0400306
307 Builder.CreateRetVoid();
308 }
309
David Neto482550a2018-03-24 05:21:07 -0700310 // Inline the inner function. It's cleaner to do this.
311 for (CallInst *C : CallList) {
312 InlineFunctionInfo info;
313 Changed |= InlineFunction(C, info);
David Netod5b3f982017-09-28 14:49:49 -0400314 }
315
David Netodd992212017-06-23 17:47:55 -0400316 return Changed;
317}