blob: 07202c8153f90dcf11e8e01866b443f76b4d4e14 [file] [log] [blame]
alan-baker86ce19c2020-08-05 13:09:19 -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 <cassert>
16#include <ostream>
17#include <unordered_map>
18
19#include "spirv-tools/libspirv.hpp"
20#include "spirv/unified1/spirv.hpp"
21
22#include "clspv/ArgKind.h"
23#include "clspv/PushConstant.h"
24#include "clspv/Sampler.h"
25#include "clspv/SpecConstant.h"
26#include "clspv/spirv_reflection.hpp"
27
28#include "ReflectionParser.h"
29
30namespace {
31class ReflectionParser {
32public:
33 ReflectionParser(std::ostream *ostr) : str(ostr) {}
34
35 // Parses |inst| and emits descriptor map entries as necessary.
36 spv_result_t ParseInstruction(const spv_parsed_instruction_t *inst);
37
38private:
39 // Converts the extended instruction to ArgKind.
40 clspv::ArgKind GetArgKindFromExtInst(uint32_t value);
41
42 // Converts the extended instruction to PushConstant.
43 clspv::PushConstant GetPushConstantFromExtInst(uint32_t value);
44
45 // Descriptor map output stream.
46 std::ostream *str;
47
48 // Tracks OpTypeInt 32 0 result id.
49 uint32_t int_id = 0;
50
51 // String mappings. Includes OpString value to result id, Kernel name to
52 // result id and argument name to result id.
53 std::unordered_map<uint32_t, std::string> strings;
54
55 // Maps u32 constant result ids to their values.
56 std::unordered_map<uint32_t, uint32_t> constants;
57};
58
59spv_result_t ParseInstruction(void *user_data,
60 const spv_parsed_instruction_t *inst) {
61 ReflectionParser *parser = reinterpret_cast<ReflectionParser *>(user_data);
62 return parser->ParseInstruction(inst);
63}
64
65clspv::ArgKind ReflectionParser::GetArgKindFromExtInst(uint32_t value) {
66 clspv::reflection::ExtInst ext_inst =
67 static_cast<clspv::reflection::ExtInst>(value);
68 switch (ext_inst) {
69 case clspv::reflection::ExtInstArgumentStorageBuffer:
70 case clspv::reflection::ExtInstConstantDataStorageBuffer:
71 return clspv::ArgKind::Buffer;
72 case clspv::reflection::ExtInstArgumentUniform:
73 case clspv::reflection::ExtInstConstantDataUniform:
74 return clspv::ArgKind::BufferUBO;
75 case clspv::reflection::ExtInstArgumentPodStorageBuffer:
76 return clspv::ArgKind::Pod;
77 case clspv::reflection::ExtInstArgumentPodUniform:
78 return clspv::ArgKind::PodUBO;
79 case clspv::reflection::ExtInstArgumentPodPushConstant:
80 return clspv::ArgKind::PodPushConstant;
81 case clspv::reflection::ExtInstArgumentSampledImage:
82 return clspv::ArgKind::ReadOnlyImage;
83 case clspv::reflection::ExtInstArgumentStorageImage:
84 return clspv::ArgKind::WriteOnlyImage;
85 case clspv::reflection::ExtInstArgumentSampler:
86 return clspv::ArgKind::Sampler;
87 case clspv::reflection::ExtInstArgumentWorkgroup:
88 return clspv::ArgKind::Local;
89 default:
90 assert(false && "Unexpected extended instruction");
91 return clspv::ArgKind::Buffer;
92 }
93}
94
95clspv::PushConstant
96ReflectionParser::GetPushConstantFromExtInst(uint32_t value) {
97 clspv::reflection::ExtInst ext_inst =
98 static_cast<clspv::reflection::ExtInst>(value);
99 switch (ext_inst) {
100 case clspv::reflection::ExtInstPushConstantGlobalOffset:
101 return clspv::PushConstant::GlobalOffset;
102 case clspv::reflection::ExtInstPushConstantEnqueuedLocalSize:
103 return clspv::PushConstant::EnqueuedLocalSize;
104 case clspv::reflection::ExtInstPushConstantGlobalSize:
105 return clspv::PushConstant::GlobalSize;
106 case clspv::reflection::ExtInstPushConstantRegionOffset:
107 return clspv::PushConstant::RegionOffset;
108 case clspv::reflection::ExtInstPushConstantNumWorkgroups:
109 return clspv::PushConstant::NumWorkgroups;
110 case clspv::reflection::ExtInstPushConstantRegionGroupOffset:
111 return clspv::PushConstant::RegionGroupOffset;
112 default:
113 assert(false && "Unexpected push constant");
114 return clspv::PushConstant::KernelArgument;
115 }
116}
117
118spv_result_t
119ReflectionParser::ParseInstruction(const spv_parsed_instruction_t *inst) {
120 switch (inst->opcode) {
121 case spv::OpTypeInt:
122 if (inst->words[inst->operands[1].offset] == 32 &&
123 inst->words[inst->operands[2].offset] == 0) {
124 int_id = inst->result_id;
125 }
126 break;
127 case spv::OpConstant:
128 if (inst->words[inst->operands[0].offset] == int_id) {
129 uint32_t value = inst->words[inst->operands[2].offset];
130 constants[inst->result_id] = value;
131 }
132 break;
133 case spv::OpString: {
134 std::string value =
135 reinterpret_cast<const char *>(inst->words + inst->operands[1].offset);
136 strings[inst->result_id] = value;
137 break;
138 }
139 case spv::OpExtInst:
140 if (inst->ext_inst_type == SPV_EXT_INST_TYPE_NONSEMANTIC_CLSPVREFLECTION) {
141 // Reflection specific instruction.
142 auto ext_inst = inst->words[inst->operands[3].offset];
143 switch (ext_inst) {
144 case clspv::reflection::ExtInstKernel: {
145 // Record the name and emit a kernel_decl entry.
146 const auto &name = strings[inst->words[inst->operands[5].offset]];
147 strings[inst->result_id] = name;
148 *str << "kernel_decl," << name << "\n";
149 break;
150 }
151 case clspv::reflection::ExtInstArgumentInfo: {
152 // Record the argument name.
153 const auto &name = strings[inst->words[inst->operands[4].offset]];
154 strings[inst->result_id] = name;
155 break;
156 }
157 case clspv::reflection::ExtInstArgumentStorageBuffer:
158 case clspv::reflection::ExtInstArgumentUniform:
159 case clspv::reflection::ExtInstArgumentSampledImage:
160 case clspv::reflection::ExtInstArgumentStorageImage:
161 case clspv::reflection::ExtInstArgumentSampler: {
162 // Emit an argument entry. Descriptor set and binding, no size.
163 auto kernel_id = inst->words[inst->operands[4].offset];
164 auto ordinal_id = inst->words[inst->operands[5].offset];
165 auto ds_id = inst->words[inst->operands[6].offset];
166 auto binding_id = inst->words[inst->operands[7].offset];
167 std::string arg_name;
168 if (inst->num_operands == 9) {
169 arg_name = strings[inst->words[inst->operands[8].offset]];
170 }
171 auto kind = GetArgKindFromExtInst(ext_inst);
172 *str << "kernel," << strings[kernel_id] << ",arg," << arg_name
173 << ",argOrdinal," << constants[ordinal_id] << ",descriptorSet,"
174 << constants[ds_id] << ",binding," << constants[binding_id]
175 << ",offset,0,argKind," << clspv::GetArgKindName(kind) << "\n";
176 break;
177 }
178 case clspv::reflection::ExtInstArgumentPodStorageBuffer:
179 case clspv::reflection::ExtInstArgumentPodUniform: {
180 // Emit an argument entry. Descriptor set, binding and size.
181 auto kernel_id = inst->words[inst->operands[4].offset];
182 auto ordinal_id = inst->words[inst->operands[5].offset];
183 auto ds_id = inst->words[inst->operands[6].offset];
184 auto binding_id = inst->words[inst->operands[7].offset];
185 auto offset_id = inst->words[inst->operands[8].offset];
186 auto size_id = inst->words[inst->operands[9].offset];
187 std::string arg_name;
188 if (inst->num_operands == 11) {
189 arg_name = strings[inst->words[inst->operands[10].offset]];
190 }
191 auto kind = GetArgKindFromExtInst(ext_inst);
192 *str << "kernel," << strings[kernel_id] << ",arg," << arg_name
193 << ",argOrdinal," << constants[ordinal_id] << ",descriptorSet,"
194 << constants[ds_id] << ",binding," << constants[binding_id]
195 << ",offset," << constants[offset_id] << ",argKind,"
196 << clspv::GetArgKindName(kind) << ",argSize," << constants[size_id]
197 << "\n";
198 break;
199 }
200 case clspv::reflection::ExtInstArgumentPodPushConstant: {
201 // Emit an argument entry. No descriptor set or binding, but has
202 // size.
203 auto kernel_id = inst->words[inst->operands[4].offset];
204 auto ordinal_id = inst->words[inst->operands[5].offset];
205 auto offset_id = inst->words[inst->operands[6].offset];
206 auto size_id = inst->words[inst->operands[7].offset];
207 std::string arg_name;
208 if (inst->num_operands == 9) {
209 arg_name = strings[inst->words[inst->operands[8].offset]];
210 }
211 auto kind = GetArgKindFromExtInst(ext_inst);
212 *str << "kernel," << strings[kernel_id] << ",arg," << arg_name
213 << ",argOrdinal," << constants[ordinal_id] << ",offset,"
214 << constants[offset_id] << ",argKind,"
215 << clspv::GetArgKindName(kind) << ",argSize," << constants[size_id]
216 << "\n";
217 break;
218 }
219 case clspv::reflection::ExtInstArgumentWorkgroup: {
220 // Emit an argument entry. No descriptor set or binding, but has
221 // spec id and size.
222 auto kernel_id = inst->words[inst->operands[4].offset];
223 auto ordinal_id = inst->words[inst->operands[5].offset];
224 auto spec_id = inst->words[inst->operands[6].offset];
225 auto size_id = inst->words[inst->operands[7].offset];
226 std::string arg_name;
227 if (inst->num_operands == 9) {
228 arg_name = strings[inst->words[inst->operands[8].offset]];
229 }
230 auto kind = GetArgKindFromExtInst(ext_inst);
231 *str << "kernel," << strings[kernel_id] << ",arg," << arg_name
232 << ",argOrdinal," << constants[ordinal_id] << ",argKind,"
233 << clspv::GetArgKindName(kind) << ",arrayElemSize,"
234 << constants[size_id] << ",arrayNumElemSpecId,"
235 << constants[spec_id] << "\n";
236 break;
237 }
238 case clspv::reflection::ExtInstConstantDataStorageBuffer:
239 case clspv::reflection::ExtInstConstantDataUniform: {
240 // Emit constant data entry.
241 auto ds_id = inst->words[inst->operands[4].offset];
242 auto binding_id = inst->words[inst->operands[5].offset];
243 auto data_id = inst->words[inst->operands[6].offset];
244 auto kind = GetArgKindFromExtInst(ext_inst);
245 *str << "constant,descriptorSet," << constants[ds_id] << ",binding,"
246 << constants[binding_id] << ",kind," << clspv::GetArgKindName(kind)
247 << ",hexbytes," << strings[data_id] << "\n";
248 break;
249 }
250 case clspv::reflection::ExtInstSpecConstantWorkgroupSize: {
251 // WorkgroupSize is emitted as three separate entries.
252 auto x_id = inst->words[inst->operands[4].offset];
253 auto y_id = inst->words[inst->operands[5].offset];
254 auto z_id = inst->words[inst->operands[6].offset];
255 *str << "spec_constant,"
256 << clspv::GetSpecConstantName(clspv::SpecConstant::kWorkgroupSizeX)
257 << ",spec_id," << constants[x_id] << "\n";
258 *str << "spec_constant,"
259 << clspv::GetSpecConstantName(clspv::SpecConstant::kWorkgroupSizeY)
260 << ",spec_id," << constants[y_id] << "\n";
261 *str << "spec_constant,"
262 << clspv::GetSpecConstantName(clspv::SpecConstant::kWorkgroupSizeZ)
263 << ",spec_id," << constants[z_id] << "\n";
264 break;
265 }
266 case clspv::reflection::ExtInstSpecConstantGlobalOffset: {
267 // GlobalOffset is emitted as three separate entries.
268 auto x_id = inst->words[inst->operands[4].offset];
269 auto y_id = inst->words[inst->operands[5].offset];
270 auto z_id = inst->words[inst->operands[6].offset];
271 *str << "spec_constant,"
272 << clspv::GetSpecConstantName(clspv::SpecConstant::kGlobalOffsetX)
273 << ",spec_id," << constants[x_id] << "\n";
274 *str << "spec_constant,"
275 << clspv::GetSpecConstantName(clspv::SpecConstant::kGlobalOffsetY)
276 << ",spec_id," << constants[y_id] << "\n";
277 *str << "spec_constant,"
278 << clspv::GetSpecConstantName(clspv::SpecConstant::kGlobalOffsetZ)
279 << ",spec_id," << constants[z_id] << "\n";
280 break;
281 }
282 case clspv::reflection::ExtInstSpecConstantWorkDim: {
283 auto dim_id = inst->words[inst->operands[4].offset];
284 *str << "spec_constant,"
285 << clspv::GetSpecConstantName(clspv::SpecConstant::kWorkDim)
286 << ",spec_id," << constants[dim_id] << "\n";
287 break;
288 }
289 case clspv::reflection::ExtInstPushConstantGlobalOffset:
290 case clspv::reflection::ExtInstPushConstantEnqueuedLocalSize:
291 case clspv::reflection::ExtInstPushConstantGlobalSize:
292 case clspv::reflection::ExtInstPushConstantRegionOffset:
293 case clspv::reflection::ExtInstPushConstantNumWorkgroups:
294 case clspv::reflection::ExtInstPushConstantRegionGroupOffset: {
295 auto offset_id = inst->words[inst->operands[4].offset];
296 auto size_id = inst->words[inst->operands[5].offset];
297 auto kind = GetPushConstantFromExtInst(ext_inst);
298 *str << "pushconstant,name," << clspv::GetPushConstantName(kind)
299 << ",offset," << constants[offset_id] << ",size,"
300 << constants[size_id] << "\n";
301 break;
302 }
303 case clspv::reflection::ExtInstLiteralSampler: {
304 auto ds_id = inst->words[inst->operands[4].offset];
305 auto binding_id = inst->words[inst->operands[5].offset];
306 auto mask_id = inst->words[inst->operands[6].offset];
307 auto mask = constants[mask_id];
308 *str << "sampler," << mask << ",samplerExpr,\""
309 << clspv::GetSamplerCoordsName(mask) << "|"
310 << clspv::GetSamplerAddressingModeName(mask) << "|"
311 << clspv::GetSamplerFilteringModeName(mask) << "\",descriptorSet,"
312 << constants[ds_id] << ",binding," << constants[binding_id]
313 << "\n";
314 break;
315 }
316 default:
317 break;
318 }
319 break;
320 }
321 default:
322 break;
323 }
324
325 return SPV_SUCCESS;
326}
327} // namespace
328
329namespace clspv {
330
331bool ParseReflection(const std::vector<uint32_t> &binary, spv_target_env env,
332 std::ostream *str) {
333 ReflectionParser parser(str);
334 auto MessageConsumer = [](spv_message_level_t, const char *,
335 const spv_position_t, const char *) {};
336 spvtools::Context context(env);
337 context.SetMessageConsumer(MessageConsumer);
338
339 spv_result_t result =
340 spvBinaryParse(context.CContext(), &parser, binary.data(), binary.size(),
341 nullptr, ParseInstruction, nullptr);
342
343 return result == SPV_SUCCESS;
344}
345} // namespace clspv