blob: 7550c71bb42d70db0bc9f7d8c159a2cb44ca5004 [file] [log] [blame]
alan-bakerc4579bb2020-04-29 14:15:50 -04001// Copyright 2020 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/Module.h"
19#include "llvm/Pass.h"
20
21#include "spirv/unified1/spirv.hpp"
22
23#include "clspv/Option.h"
24
25#include "ArgKind.h"
26#include "Constants.h"
27#include "Layout.h"
28#include "Passes.h"
29#include "PushConstant.h"
30
31#define DEBUG_TYPE "autopodargs"
32
33using namespace llvm;
34
35namespace {
36class AutoPodArgsPass : public ModulePass {
37public:
38 static char ID;
39 AutoPodArgsPass() : ModulePass(ID) {}
40
41 bool runOnModule(Module &M) override;
42
43private:
44 // Decides the pod args implementation for each kernel individually.
45 void runOnFunction(Function &F);
46
47 // Makes all kernels use |impl| for pod args.
48 void AnnotateAllKernels(Module &M, clspv::PodArgImpl impl);
49
50 // Makes kernel |F| use |impl| as the pod arg implementation.
51 void AddMetadata(Function &F, clspv::PodArgImpl impl);
alan-baker7efcaaa2020-05-06 19:33:27 -040052
53 // Returns true if |type| contains an array. Does not look through pointers
54 // since we are dealing with pod args.
55 bool ContainsArrayType(Type *type) const;
56
57 // Returns true if |type| contains a |width|-bit integer or floating-point
58 // type. Does not look through pointer since we are dealing with pod args.
59 bool ContainsSizedType(Type *type, uint32_t width) const;
alan-bakerc4579bb2020-04-29 14:15:50 -040060};
61} // namespace
62
63char AutoPodArgsPass::ID = 0;
64INITIALIZE_PASS(AutoPodArgsPass, "AutoPodArgs",
65 "Mark pod arg implementation as metadata on kernels", false,
66 false)
67
68namespace clspv {
69ModulePass *createAutoPodArgsPass() { return new AutoPodArgsPass(); }
70} // namespace clspv
71
72bool AutoPodArgsPass::runOnModule(Module &M) {
73 if (clspv::Option::PodArgsInUniformBuffer()) {
74 AnnotateAllKernels(M, clspv::PodArgImpl::kUBO);
75 return true;
76 } else if (clspv::Option::PodArgsInPushConstants()) {
77 AnnotateAllKernels(M, clspv::PodArgImpl::kPushConstant);
78 return true;
79 }
80
81 for (auto &F : M) {
82 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL)
83 continue;
84
85 runOnFunction(F);
86 }
87
88 return true;
89}
90
91void AutoPodArgsPass::runOnFunction(Function &F) {
92 auto &M = *F.getParent();
93 const auto &DL = M.getDataLayout();
94 SmallVector<Type *, 8> pod_types;
95 bool satisfies_ubo = true;
96 for (auto &Arg : F.args()) {
97 auto arg_type = Arg.getType();
98 if (isa<PointerType>(arg_type))
99 continue;
100
101 pod_types.push_back(arg_type);
102
alan-baker7efcaaa2020-05-06 19:33:27 -0400103 // If the type contains an 8- or 16-bit type UBO storage must be supported.
104 satisfies_ubo &= !ContainsSizedType(arg_type, 16) ||
105 clspv::Option::Supports16BitStorageClass(
106 clspv::Option::StorageClass::kUBO);
107 satisfies_ubo &= !ContainsSizedType(arg_type, 8) ||
108 clspv::Option::Supports8BitStorageClass(
109 clspv::Option::StorageClass::kUBO);
alan-bakerc4579bb2020-04-29 14:15:50 -0400110 if (auto struct_ty = dyn_cast<StructType>(arg_type)) {
111 // Only check individual arguments as clustering will fix the layout with
112 // padding if necessary.
113 satisfies_ubo &=
114 clspv::isValidExplicitLayout(M, struct_ty, spv::StorageClassUniform);
115 }
116 }
117
118 // Per-kernel push constant interface requires:
119 // 1. Clustered pod args.
120 // 2. No global push constants.
121 // 3. Args must fit in push constant size limit.
alan-baker7efcaaa2020-05-06 19:33:27 -0400122 // 4. No arrays.
123 // 5. If 16-bit types are used, 16-bit push constants are supported.
124 // 6. If 8-bit types are used, 8-bit push constants are supported.
alan-bakerc4579bb2020-04-29 14:15:50 -0400125 const auto pod_struct_ty = StructType::get(M.getContext(), pod_types);
alan-baker7efcaaa2020-05-06 19:33:27 -0400126 const bool contains_array = ContainsArrayType(pod_struct_ty);
127 const bool support_16bit_pc = !ContainsSizedType(pod_struct_ty, 16) ||
128 clspv::Option::Supports16BitStorageClass(
129 clspv::Option::StorageClass::kPushConstant);
130 const bool support_8bit_pc = !ContainsSizedType(pod_struct_ty, 8) ||
131 clspv::Option::Supports8BitStorageClass(
132 clspv::Option::StorageClass::kPushConstant);
133 const bool fits_push_constant =
134 DL.getTypeSizeInBits(pod_struct_ty).getFixedSize() / 8 <=
135 clspv::Option::MaxPushConstantsSize();
alan-bakerc4579bb2020-04-29 14:15:50 -0400136 const bool satisfies_push_constant =
alan-baker7efcaaa2020-05-06 19:33:27 -0400137 clspv::Option::ClusterPodKernelArgs() && support_16bit_pc &&
138 support_8bit_pc && fits_push_constant &&
139 !clspv::UsesGlobalPushConstants(M) && !contains_array;
alan-bakerc4579bb2020-04-29 14:15:50 -0400140
141 // Priority:
142 // 1. Per-kernel push constant interface.
143 // 2. NYI: global type mangled push constant interface.
144 // 3. UBO
145 // 4. SSBO
146 clspv::PodArgImpl impl = clspv::PodArgImpl::kSSBO;
147 if (satisfies_push_constant) {
148 impl = clspv::PodArgImpl::kPushConstant;
149 } else if (satisfies_ubo) {
150 impl = clspv::PodArgImpl::kUBO;
151 }
152 AddMetadata(F, impl);
153}
154
155void AutoPodArgsPass::AnnotateAllKernels(Module &M, clspv::PodArgImpl impl) {
156 for (auto &F : M) {
157 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL)
158 continue;
159
160 AddMetadata(F, impl);
161 }
162}
163
164void AutoPodArgsPass::AddMetadata(Function &F, clspv::PodArgImpl impl) {
165 auto md = MDTuple::get(
166 F.getContext(),
167 ConstantAsMetadata::get(ConstantInt::get(
168 IntegerType::get(F.getContext(), 32), static_cast<uint32_t>(impl))));
169 F.setMetadata(clspv::PodArgsImplMetadataName(), md);
170}
alan-baker7efcaaa2020-05-06 19:33:27 -0400171
172bool AutoPodArgsPass::ContainsArrayType(Type *type) const {
173 if (isa<ArrayType>(type)) {
174 return true;
175 } else if (auto struct_ty = dyn_cast<StructType>(type)) {
176 for (auto sub_type : struct_ty->elements()) {
177 if (ContainsArrayType(sub_type))
178 return true;
179 }
180 }
181
182 return false;
183}
184
185bool AutoPodArgsPass::ContainsSizedType(Type *type, uint32_t width) const {
186 if (auto int_ty = dyn_cast<IntegerType>(type)) {
187 return int_ty->getBitWidth() == width;
188 } else if (type->isHalfTy()) {
189 return width == 16;
190 } else if (auto array_ty = dyn_cast<ArrayType>(type)) {
191 return ContainsSizedType(array_ty->getElementType(), width);
192 } else if (auto vec_ty = dyn_cast<VectorType>(type)) {
193 return ContainsSizedType(vec_ty->getElementType(), width);
194 } else if (auto struct_ty = dyn_cast<StructType>(type)) {
195 for (auto sub_type : struct_ty->elements()) {
196 if (ContainsSizedType(sub_type, width))
197 return true;
198 }
199 }
200
201 return false;
202}