blob: 3c272b92ad8dfaaf2b6cd5965247d8e0c29f4cd2 [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"
David Netoc6f3ab22018-04-06 18:02:31 -040033#include "llvm/IR/IRBuilder.h"
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040034#include "llvm/IR/Instructions.h"
David Netoc6f3ab22018-04-06 18:02:31 -040035#include "llvm/IR/Metadata.h"
36#include "llvm/IR/Module.h"
37#include "llvm/Pass.h"
38#include "llvm/Support/CommandLine.h"
alan-baker038e9242019-04-19 22:14:41 -040039#include "llvm/Support/MathExtras.h"
David Netoc6f3ab22018-04-06 18:02:31 -040040#include "llvm/Support/raw_ostream.h"
41#include "llvm/Transforms/Utils/Cloning.h"
David Netodd992212017-06-23 17:47:55 -040042
alan-baker038e9242019-04-19 22:14:41 -040043#include "clspv/Option.h"
44
David Neto4feb7a42017-10-06 17:29:42 -040045#include "ArgKind.h"
alan-bakerc4579bb2020-04-29 14:15:50 -040046#include "Constants.h"
Diego Novilloa4c44fa2019-04-11 10:56:15 -040047#include "Passes.h"
David Netodd992212017-06-23 17:47:55 -040048
49using namespace llvm;
50
51#define DEBUG_TYPE "clusterpodkernelargs"
52
53namespace {
54struct ClusterPodKernelArgumentsPass : public ModulePass {
55 static char ID;
56 ClusterPodKernelArgumentsPass() : ModulePass(ID) {}
57
58 bool runOnModule(Module &M) override;
59};
David Neto48f56a42017-10-06 16:44:25 -040060
David Netodd992212017-06-23 17:47:55 -040061} // namespace
62
63char ClusterPodKernelArgumentsPass::ID = 0;
Diego Novilloa4c44fa2019-04-11 10:56:15 -040064INITIALIZE_PASS(ClusterPodKernelArgumentsPass, "ClusterPodKernelArgumentsPass",
65 "Cluster POD Kernel Arguments Pass", false, false)
David Netodd992212017-06-23 17:47:55 -040066
67namespace clspv {
68llvm::ModulePass *createClusterPodKernelArgumentsPass() {
69 return new ClusterPodKernelArgumentsPass();
70}
71} // namespace clspv
72
73bool ClusterPodKernelArgumentsPass::runOnModule(Module &M) {
74 bool Changed = false;
75 LLVMContext &Context = M.getContext();
76
77 SmallVector<Function *, 8> WorkList;
78
79 for (Function &F : M) {
80 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL) {
81 continue;
82 }
83 for (Argument &Arg : F.args()) {
84 if (!isa<PointerType>(Arg.getType())) {
85 WorkList.push_back(&F);
86 break;
87 }
88 }
89 }
90
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040091 SmallVector<CallInst *, 8> CallList;
David Netod5b3f982017-09-28 14:49:49 -040092
David Netoc6f3ab22018-04-06 18:02:31 -040093 // Note: The transformation done in this pass preserves the pointer-to-local
94 // arg to spec-id mapping.
95 clspv::ArgIdMapType arg_spec_id_map = clspv::AllocateArgSpecIds(M);
96
Diego Novillo3cc8d7a2019-04-10 13:30:34 -040097 for (Function *F : WorkList) {
David Netodd992212017-06-23 17:47:55 -040098 Changed = true;
99
alan-bakerc4579bb2020-04-29 14:15:50 -0400100 auto pod_arg_impl = clspv::GetPodArgsImpl(*F);
101 auto pod_arg_kind = clspv::GetArgKindForPodArgs(*F);
David Neto156783e2017-07-05 15:39:41 -0400102 // An ArgMapping describes how a kernel argument is remapped.
103 struct ArgMapping {
104 std::string name;
105 // 0-based argument index in the old kernel function.
106 unsigned old_index;
107 // 0-based argument index in the new kernel function.
David Netoc6f3ab22018-04-06 18:02:31 -0400108 int new_index;
David Neto156783e2017-07-05 15:39:41 -0400109 // Offset of the argument value within the new kernel argument.
110 // This is always zero for non-POD arguments. For a POD argument,
111 // this is the byte offset within the POD arguments struct.
112 unsigned offset;
Kévin PETITa353c832018-03-20 23:21:21 +0000113 // Size of the argument
114 unsigned arg_size;
Kévin Petit8bea15e2019-04-09 14:05:17 +0100115 // Argument type.
116 clspv::ArgKind arg_kind;
David Netoc6f3ab22018-04-06 18:02:31 -0400117 // If non-negative, this argument is a pointer-to-local, and the value
118 // here is the specialization constant id for the array size.
119 int spec_id;
David Neto156783e2017-07-05 15:39:41 -0400120 };
121
David Netodd992212017-06-23 17:47:55 -0400122 // In OpenCL, kernel arguments are either pointers or POD. A composite with
Kévin Petit921c1ab2019-03-19 21:25:44 +0000123 // an element or member that is a pointer is not allowed. So we'll use POD
David Netodd992212017-06-23 17:47:55 -0400124 // as a shorthand for non-pointer.
125
126 SmallVector<Type *, 8> PtrArgTys;
127 SmallVector<Type *, 8> PodArgTys;
David Neto156783e2017-07-05 15:39:41 -0400128 SmallVector<ArgMapping, 8> RemapInfo;
alan-baker038e9242019-04-19 22:14:41 -0400129 DenseMap<Argument *, unsigned> PodIndexMap;
David Neto156783e2017-07-05 15:39:41 -0400130 unsigned arg_index = 0;
David Netoc6f3ab22018-04-06 18:02:31 -0400131 int new_index = 0;
alan-baker038e9242019-04-19 22:14:41 -0400132 unsigned pod_index = 0;
David Netodd992212017-06-23 17:47:55 -0400133 for (Argument &Arg : F->args()) {
134 Type *ArgTy = Arg.getType();
135 if (isa<PointerType>(ArgTy)) {
136 PtrArgTys.push_back(ArgTy);
alan-bakerc4579bb2020-04-29 14:15:50 -0400137 const auto kind = clspv::GetArgKind(Arg);
David Netoc6f3ab22018-04-06 18:02:31 -0400138 int spec_id = -1;
David Neto862b7d82018-06-14 18:48:37 -0400139 if (kind == clspv::ArgKind::Local) {
David Netoc6f3ab22018-04-06 18:02:31 -0400140 spec_id = arg_spec_id_map[&Arg];
141 assert(spec_id > 0);
142 }
David Neto862b7d82018-06-14 18:48:37 -0400143 RemapInfo.push_back({std::string(Arg.getName()), arg_index, new_index++,
Kévin Petit8bea15e2019-04-09 14:05:17 +0100144 0u, 0u, kind, spec_id});
David Netodd992212017-06-23 17:47:55 -0400145 } else {
alan-baker038e9242019-04-19 22:14:41 -0400146 PodIndexMap[&Arg] = pod_index++;
David Netodd992212017-06-23 17:47:55 -0400147 PodArgTys.push_back(ArgTy);
148 }
David Neto156783e2017-07-05 15:39:41 -0400149 arg_index++;
David Netodd992212017-06-23 17:47:55 -0400150 }
151
David Netodd992212017-06-23 17:47:55 -0400152 // Put the pointer arguments first, and then POD arguments struct last.
David Neto2ded02e2017-10-23 15:30:59 -0400153 // Use StructType::get so we reuse types where possible.
154 auto PodArgsStructTy = StructType::get(Context, PodArgTys);
David Netodd992212017-06-23 17:47:55 -0400155 SmallVector<Type *, 8> NewFuncParamTys(PtrArgTys);
alan-baker038e9242019-04-19 22:14:41 -0400156
alan-bakerc4579bb2020-04-29 14:15:50 -0400157 if (pod_arg_impl == clspv::PodArgImpl::kUBO &&
alan-baker038e9242019-04-19 22:14:41 -0400158 !clspv::Option::Std430UniformBufferLayout()) {
159 SmallVector<Type *, 16> PaddedPodArgTys;
160 const DataLayout DL(&M);
161 const auto StructLayout = DL.getStructLayout(PodArgsStructTy);
162 unsigned pod_index = 0;
163 for (auto &Arg : F->args()) {
164 auto arg_type = Arg.getType();
165 if (arg_type->isPointerTy())
166 continue;
167
168 // The frontend has validated individual POD arguments. When the
169 // unified struct is constructed, pad struct and array elements as
170 // necessary to achieve a 16-byte alignment.
171 if (arg_type->isStructTy() || arg_type->isArrayTy()) {
172 auto offset = StructLayout->getElementOffset(pod_index);
173 auto aligned = alignTo(offset, 16);
174 if (offset < aligned) {
175 auto int_ty = IntegerType::get(Context, 32);
176 auto char_ty = IntegerType::get(Context, 8);
177 size_t num_ints = (aligned - offset) / 4;
178 size_t num_chars = (aligned - offset) - (num_ints * 4);
179 assert((num_chars == 0 || clspv::Option::Int8Support()) &&
180 "Char in UBO struct without char support");
181 // Fix the index for the offset of the argument.
182 // Add char padding first.
183 PodIndexMap[&Arg] += num_ints + num_chars;
184 for (size_t i = 0; i < num_chars; ++i) {
185 PaddedPodArgTys.push_back(char_ty);
186 }
187 for (size_t i = 0; i < num_ints; ++i) {
188 PaddedPodArgTys.push_back(int_ty);
189 }
190 }
191 }
192 ++pod_index;
193 PaddedPodArgTys.push_back(arg_type);
194 }
195 PodArgsStructTy = StructType::get(Context, PaddedPodArgTys);
196 }
David Netodd992212017-06-23 17:47:55 -0400197 NewFuncParamTys.push_back(PodArgsStructTy);
198
David Neto156783e2017-07-05 15:39:41 -0400199 // We've recorded the remapping for pointer arguments. Now record the
200 // remapping for POD arguments.
201 {
Kévin PETITa353c832018-03-20 23:21:21 +0000202 const DataLayout DL(&M);
203 const auto StructLayout = DL.getStructLayout(PodArgsStructTy);
David Neto156783e2017-07-05 15:39:41 -0400204 arg_index = 0;
David Neto156783e2017-07-05 15:39:41 -0400205 for (Argument &Arg : F->args()) {
206 Type *ArgTy = Arg.getType();
207 if (!isa<PointerType>(ArgTy)) {
Kévin PETITa353c832018-03-20 23:21:21 +0000208 unsigned arg_size = DL.getTypeStoreSize(ArgTy);
David Neto156783e2017-07-05 15:39:41 -0400209 RemapInfo.push_back(
David Netoc6f3ab22018-04-06 18:02:31 -0400210 {std::string(Arg.getName()), arg_index, new_index,
alan-baker038e9242019-04-19 22:14:41 -0400211 unsigned(StructLayout->getElementOffset(PodIndexMap[&Arg])),
alan-bakerc4579bb2020-04-29 14:15:50 -0400212 arg_size, pod_arg_kind, -1});
David Neto156783e2017-07-05 15:39:41 -0400213 }
214 arg_index++;
215 }
216 }
217
David Netodd992212017-06-23 17:47:55 -0400218 FunctionType *NewFuncTy =
219 FunctionType::get(F->getReturnType(), NewFuncParamTys, false);
220
221 // Create the new function and set key properties.
222 auto NewFunc = Function::Create(NewFuncTy, F->getLinkage());
223 // The new function adopts the real name so that linkage to the outside
224 // world remains the same.
225 NewFunc->setName(F->getName());
226 F->setName(NewFunc->getName().str() + ".inner");
227
228 NewFunc->setCallingConv(F->getCallingConv());
229 F->setCallingConv(CallingConv::SPIR_FUNC);
230
Kévin Petit921c1ab2019-03-19 21:25:44 +0000231 // Transfer attributes that don't apply to the POD arguments
232 // to the new functions.
233 auto Attributes = F->getAttributes();
234 SmallVector<std::pair<unsigned, AttributeSet>, 8> AttrBuildInfo;
235
236 // Return attributes have to come first
237 if (Attributes.hasAttributes(AttributeList::ReturnIndex)) {
238 auto idx = AttributeList::ReturnIndex;
239 auto attrs = Attributes.getRetAttributes();
240 AttrBuildInfo.push_back(std::make_pair(idx, attrs));
241 }
242
Kévin Petit8bea15e2019-04-09 14:05:17 +0100243 // Then attributes for non-POD parameters
Kévin Petit921c1ab2019-03-19 21:25:44 +0000244 for (auto &rinfo : RemapInfo) {
Kévin Petit8bea15e2019-04-09 14:05:17 +0100245 bool argIsPod = rinfo.arg_kind == clspv::ArgKind::Pod ||
alan-baker9b0ec3c2020-04-06 14:45:34 -0400246 rinfo.arg_kind == clspv::ArgKind::PodUBO ||
247 rinfo.arg_kind == clspv::ArgKind::PodPushConstant;
Kévin Petit8bea15e2019-04-09 14:05:17 +0100248 if (!argIsPod && Attributes.hasParamAttrs(rinfo.old_index)) {
Kévin Petit921c1ab2019-03-19 21:25:44 +0000249 auto idx = rinfo.new_index + AttributeList::FirstArgIndex;
250 auto attrs = Attributes.getParamAttributes(rinfo.old_index);
251 AttrBuildInfo.push_back(std::make_pair(idx, attrs));
252 }
253 }
254
alan-bakerbccf62c2019-03-29 10:32:41 -0400255 // And finally function attributes.
Kévin Petit921c1ab2019-03-19 21:25:44 +0000256 if (Attributes.hasAttributes(AttributeList::FunctionIndex)) {
Kévin Petit921c1ab2019-03-19 21:25:44 +0000257 auto idx = AttributeList::FunctionIndex;
alan-bakerbccf62c2019-03-29 10:32:41 -0400258 auto attrs = Attributes.getFnAttributes();
259 AttrBuildInfo.push_back(std::make_pair(idx, attrs));
Kévin Petit921c1ab2019-03-19 21:25:44 +0000260 }
alan-bakerbccf62c2019-03-29 10:32:41 -0400261 auto newAttributes = AttributeList::get(M.getContext(), AttrBuildInfo);
Kévin Petit921c1ab2019-03-19 21:25:44 +0000262 NewFunc->setAttributes(newAttributes);
263
David Netodd992212017-06-23 17:47:55 -0400264 // Move OpenCL kernel named attributes.
265 // TODO(dneto): Attributes starting with kernel_arg_* should be rewritten
266 // to reflect change in the argument shape.
alan-bakerc4579bb2020-04-29 14:15:50 -0400267 auto pod_md_name = clspv::PodArgsImplMetadataName();
David Netodd992212017-06-23 17:47:55 -0400268 std::vector<const char *> Metadatas{
269 "reqd_work_group_size", "kernel_arg_addr_space",
270 "kernel_arg_access_qual", "kernel_arg_type",
alan-bakerc4579bb2020-04-29 14:15:50 -0400271 "kernel_arg_base_type", "kernel_arg_type_qual",
272 pod_md_name.c_str()};
David Netodd992212017-06-23 17:47:55 -0400273 for (auto name : Metadatas) {
274 NewFunc->setMetadata(name, F->getMetadata(name));
275 F->setMetadata(name, nullptr);
276 }
277
David Neto156783e2017-07-05 15:39:41 -0400278 IRBuilder<> Builder(BasicBlock::Create(Context, "entry", NewFunc));
279
280 // Set kernel argument mapping metadata.
281 {
282 // Attach a metadata node named "kernel_arg_map" to the new kernel
283 // function. It is a tuple of nodes, each of which is a tuple for
284 // each argument, with members:
285 // - Argument name
286 // - Ordinal index in the original kernel function
287 // - Ordinal index in the new kernel function
288 // - Byte offset within the argument. This is always 0 for pointer
289 // arguments. For POD arguments this is the offest within the POD
290 // argument struct.
David Neto48f56a42017-10-06 16:44:25 -0400291 // - Argument type
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400292 LLVMContext &Context = M.getContext();
293 SmallVector<Metadata *, 8> mappings;
David Neto156783e2017-07-05 15:39:41 -0400294 for (auto &arg_mapping : RemapInfo) {
295 auto *name_md = MDString::get(Context, arg_mapping.name);
296 auto *old_index_md =
297 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.old_index));
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400298 auto *new_index_md =
299 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.new_index));
David Netoc6f3ab22018-04-06 18:02:31 -0400300 auto *offset_md =
David Neto156783e2017-07-05 15:39:41 -0400301 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.offset));
Kévin PETITa353c832018-03-20 23:21:21 +0000302 auto *arg_size_md =
303 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.arg_size));
Kévin Petit8bea15e2019-04-09 14:05:17 +0100304 auto argKindName = GetArgKindName(arg_mapping.arg_kind);
305 auto *argtype_md = MDString::get(Context, argKindName);
Diego Novillo3cc8d7a2019-04-10 13:30:34 -0400306 auto *spec_id_md =
307 ConstantAsMetadata::get(Builder.getInt32(arg_mapping.spec_id));
308 auto *arg_md = MDNode::get(
309 Context, {name_md, old_index_md, new_index_md, offset_md,
310 arg_size_md, argtype_md, spec_id_md});
David Neto156783e2017-07-05 15:39:41 -0400311 mappings.push_back(arg_md);
312 }
313
314 NewFunc->setMetadata("kernel_arg_map", MDNode::get(Context, mappings));
315 }
316
David Netodd992212017-06-23 17:47:55 -0400317 // Insert the function after the original, to preserve ordering
318 // in the module as much as possible.
319 auto &FunctionList = M.getFunctionList();
320 for (auto Iter = FunctionList.begin(), IterEnd = FunctionList.end();
321 Iter != IterEnd; ++Iter) {
322 if (&*Iter == F) {
323 FunctionList.insertAfter(Iter, NewFunc);
324 break;
325 }
326 }
327
328 // The body of the wrapper is essentially a call to the original function,
329 // but we have to unwrap the non-pointer arguments from the struct.
David Netodd992212017-06-23 17:47:55 -0400330
331 // Map the wrapper's arguments to the callee's arguments.
332 SmallVector<Argument *, 8> CallerArgs;
333 for (Argument &Arg : NewFunc->args()) {
334 CallerArgs.push_back(&Arg);
335 }
336 Argument *PodArg = CallerArgs.back();
337 PodArg->setName("podargs");
338
339 SmallVector<Value *, 8> CalleeArgs;
alan-baker038e9242019-04-19 22:14:41 -0400340 unsigned podCount = 0;
David Netodd992212017-06-23 17:47:55 -0400341 unsigned ptrIndex = 0;
alan-baker038e9242019-04-19 22:14:41 -0400342 for (Argument &Arg : F->args()) {
David Netodd992212017-06-23 17:47:55 -0400343 if (isa<PointerType>(Arg.getType())) {
344 CalleeArgs.push_back(CallerArgs[ptrIndex++]);
345 } else {
alan-baker038e9242019-04-19 22:14:41 -0400346 podCount++;
347 unsigned podIndex = PodIndexMap[&Arg];
348 CalleeArgs.push_back(Builder.CreateExtractValue(PodArg, {podIndex}));
David Netodd992212017-06-23 17:47:55 -0400349 }
350 CalleeArgs.back()->setName(Arg.getName());
351 }
alan-baker038e9242019-04-19 22:14:41 -0400352 assert(ptrIndex + podCount == F->arg_size());
Kévin Petit98d9c332019-03-13 15:03:40 +0000353 assert(ptrIndex == PtrArgTys.size());
alan-baker038e9242019-04-19 22:14:41 -0400354 assert(podCount != 0);
355 assert(podCount == PodArgTys.size());
David Netodd992212017-06-23 17:47:55 -0400356
357 auto Call = Builder.CreateCall(F, CalleeArgs);
358 Call->setCallingConv(F->getCallingConv());
David Netod5b3f982017-09-28 14:49:49 -0400359 CallList.push_back(Call);
David Netodd992212017-06-23 17:47:55 -0400360
361 Builder.CreateRetVoid();
362 }
363
David Neto482550a2018-03-24 05:21:07 -0700364 // Inline the inner function. It's cleaner to do this.
365 for (CallInst *C : CallList) {
366 InlineFunctionInfo info;
alan-baker741fd1f2020-04-14 17:38:15 -0400367 Changed |= InlineFunction(*C, info).isSuccess();
David Netod5b3f982017-09-28 14:49:49 -0400368 }
369
David Netodd992212017-06-23 17:47:55 -0400370 return Changed;
371}