blob: fdbe71cb865c4f86468d19df5a240585d4f13670 [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);
52};
53} // namespace
54
55char AutoPodArgsPass::ID = 0;
56INITIALIZE_PASS(AutoPodArgsPass, "AutoPodArgs",
57 "Mark pod arg implementation as metadata on kernels", false,
58 false)
59
60namespace clspv {
61ModulePass *createAutoPodArgsPass() { return new AutoPodArgsPass(); }
62} // namespace clspv
63
64bool AutoPodArgsPass::runOnModule(Module &M) {
65 if (clspv::Option::PodArgsInUniformBuffer()) {
66 AnnotateAllKernels(M, clspv::PodArgImpl::kUBO);
67 return true;
68 } else if (clspv::Option::PodArgsInPushConstants()) {
69 AnnotateAllKernels(M, clspv::PodArgImpl::kPushConstant);
70 return true;
71 }
72
73 for (auto &F : M) {
74 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL)
75 continue;
76
77 runOnFunction(F);
78 }
79
80 return true;
81}
82
83void AutoPodArgsPass::runOnFunction(Function &F) {
84 auto &M = *F.getParent();
85 const auto &DL = M.getDataLayout();
86 SmallVector<Type *, 8> pod_types;
87 bool satisfies_ubo = true;
88 for (auto &Arg : F.args()) {
89 auto arg_type = Arg.getType();
90 if (isa<PointerType>(arg_type))
91 continue;
92
93 pod_types.push_back(arg_type);
94
95 if (auto struct_ty = dyn_cast<StructType>(arg_type)) {
96 // Only check individual arguments as clustering will fix the layout with
97 // padding if necessary.
98 satisfies_ubo &=
99 clspv::isValidExplicitLayout(M, struct_ty, spv::StorageClassUniform);
100 }
101 }
102
103 // Per-kernel push constant interface requires:
104 // 1. Clustered pod args.
105 // 2. No global push constants.
106 // 3. Args must fit in push constant size limit.
107 const auto pod_struct_ty = StructType::get(M.getContext(), pod_types);
108 const bool satisfies_push_constant =
109 !(!clspv::Option::ClusterPodKernelArgs() ||
110 clspv::UsesGlobalPushConstants(M) ||
111 (DL.getTypeSizeInBits(pod_struct_ty).getFixedSize() / 8) >
112 clspv::Option::MaxPushConstantsSize());
113
114 // Priority:
115 // 1. Per-kernel push constant interface.
116 // 2. NYI: global type mangled push constant interface.
117 // 3. UBO
118 // 4. SSBO
119 clspv::PodArgImpl impl = clspv::PodArgImpl::kSSBO;
120 if (satisfies_push_constant) {
121 impl = clspv::PodArgImpl::kPushConstant;
122 } else if (satisfies_ubo) {
123 impl = clspv::PodArgImpl::kUBO;
124 }
125 AddMetadata(F, impl);
126}
127
128void AutoPodArgsPass::AnnotateAllKernels(Module &M, clspv::PodArgImpl impl) {
129 for (auto &F : M) {
130 if (F.isDeclaration() || F.getCallingConv() != CallingConv::SPIR_KERNEL)
131 continue;
132
133 AddMetadata(F, impl);
134 }
135}
136
137void AutoPodArgsPass::AddMetadata(Function &F, clspv::PodArgImpl impl) {
138 auto md = MDTuple::get(
139 F.getContext(),
140 ConstantAsMetadata::get(ConstantInt::get(
141 IntegerType::get(F.getContext(), 32), static_cast<uint32_t>(impl))));
142 F.setMetadata(clspv::PodArgsImplMetadataName(), md);
143}