01ced45c13bc5da4c08646ecc8de91d6cab64257
[mesa.git] / src / gallium / frontends / clover / spirv / invocation.cpp
1 //
2 // Copyright 2018 Pierre Moreau
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a
5 // copy of this software and associated documentation files (the "Software"),
6 // to deal in the Software without restriction, including without limitation
7 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 // and/or sell copies of the Software, and to permit persons to whom the
9 // Software is furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
18 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20 // OTHER DEALINGS IN THE SOFTWARE.
21 //
22
23 #include "invocation.hpp"
24
25 #include <tuple>
26 #include <unordered_map>
27 #include <unordered_set>
28 #include <vector>
29
30 #ifdef HAVE_CLOVER_SPIRV
31 #include <spirv-tools/libspirv.hpp>
32 #include <spirv-tools/linker.hpp>
33 #endif
34
35 #include "core/error.hpp"
36 #include "core/platform.hpp"
37 #include "invocation.hpp"
38 #include "llvm/util.hpp"
39 #include "pipe/p_state.h"
40 #include "util/algorithm.hpp"
41 #include "util/functional.hpp"
42 #include "util/u_math.h"
43
44 #include "compiler/spirv/spirv.h"
45
46 #define SPIRV_HEADER_WORD_SIZE 5
47
48 using namespace clover;
49
50 #ifdef HAVE_CLOVER_SPIRV
51 namespace {
52
53 template<typename T>
54 T get(const char *source, size_t index) {
55 const uint32_t *word_ptr = reinterpret_cast<const uint32_t *>(source);
56 return static_cast<T>(word_ptr[index]);
57 }
58
59 enum module::argument::type
60 convert_storage_class(SpvStorageClass storage_class, std::string &err) {
61 switch (storage_class) {
62 case SpvStorageClassFunction:
63 return module::argument::scalar;
64 case SpvStorageClassUniformConstant:
65 return module::argument::global;
66 case SpvStorageClassWorkgroup:
67 return module::argument::local;
68 case SpvStorageClassCrossWorkgroup:
69 return module::argument::global;
70 default:
71 err += "Invalid storage type " + std::to_string(storage_class) + "\n";
72 throw build_error();
73 }
74 }
75
76 enum module::argument::type
77 convert_image_type(SpvId id, SpvDim dim, SpvAccessQualifier access,
78 std::string &err) {
79 if (dim == SpvDim2D && access == SpvAccessQualifierReadOnly)
80 return module::argument::image2d_rd;
81 else if (dim == SpvDim2D && access == SpvAccessQualifierWriteOnly)
82 return module::argument::image2d_wr;
83 else if (dim == SpvDim3D && access == SpvAccessQualifierReadOnly)
84 return module::argument::image3d_rd;
85 else if (dim == SpvDim3D && access == SpvAccessQualifierWriteOnly)
86 return module::argument::image3d_wr;
87 else {
88 err += "Unknown access qualifier " + std::to_string(access)
89 + " or dimension " + std::to_string(dim) + " for image "
90 + std::to_string(id) + ".\n";
91 throw build_error();
92 }
93 }
94
95 module::section
96 make_text_section(const std::vector<char> &code,
97 enum module::section::type section_type) {
98 const pipe_binary_program_header header { uint32_t(code.size()) };
99 module::section text { 0, section_type, header.num_bytes, {} };
100
101 text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),
102 reinterpret_cast<const char *>(&header) + sizeof(header));
103 text.data.insert(text.data.end(), code.begin(), code.end());
104
105 return text;
106 }
107
108 module
109 create_module_from_spirv(const std::vector<char> &source,
110 size_t pointer_byte_size,
111 std::string &err) {
112 const size_t length = source.size() / sizeof(uint32_t);
113 size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
114
115 std::string kernel_name;
116 size_t kernel_nb = 0u;
117 std::vector<module::argument> args;
118
119 module m;
120
121 std::unordered_map<SpvId, std::string> kernels;
122 std::unordered_map<SpvId, module::argument> types;
123 std::unordered_map<SpvId, SpvId> pointer_types;
124 std::unordered_map<SpvId, unsigned int> constants;
125 std::unordered_set<SpvId> packed_structures;
126 std::unordered_map<SpvId, std::vector<SpvFunctionParameterAttribute>>
127 func_param_attr_map;
128
129 while (i < length) {
130 const auto inst = &source[i * sizeof(uint32_t)];
131 const auto desc_word = get<uint32_t>(inst, 0);
132 const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
133 const unsigned int num_operands = desc_word >> SpvWordCountShift;
134
135 switch (opcode) {
136 case SpvOpEntryPoint:
137 if (get<SpvExecutionModel>(inst, 1) == SpvExecutionModelKernel)
138 kernels.emplace(get<SpvId>(inst, 2),
139 source.data() + (i + 3u) * sizeof(uint32_t));
140 break;
141
142 case SpvOpDecorate: {
143 const auto id = get<SpvId>(inst, 1);
144 const auto decoration = get<SpvDecoration>(inst, 2);
145 if (decoration == SpvDecorationCPacked)
146 packed_structures.emplace(id);
147 else if (decoration == SpvDecorationFuncParamAttr) {
148 const auto attribute =
149 get<SpvFunctionParameterAttribute>(inst, 3u);
150 func_param_attr_map[id].push_back(attribute);
151 }
152 break;
153 }
154
155 case SpvOpGroupDecorate: {
156 const auto group_id = get<SpvId>(inst, 1);
157 if (packed_structures.count(group_id)) {
158 for (unsigned int i = 2u; i < num_operands; ++i)
159 packed_structures.emplace(get<SpvId>(inst, i));
160 }
161 const auto func_param_attr_iter =
162 func_param_attr_map.find(group_id);
163 if (func_param_attr_iter != func_param_attr_map.end()) {
164 for (unsigned int i = 2u; i < num_operands; ++i)
165 func_param_attr_map.emplace(get<SpvId>(inst, i),
166 func_param_attr_iter->second);
167 }
168 break;
169 }
170
171 case SpvOpConstant:
172 // We only care about constants that represent the size of arrays.
173 // If they are passed as argument, they will never be more than
174 // 4GB-wide, and even if they did, a clover::module::argument size
175 // is represented by an int.
176 constants[get<SpvId>(inst, 2)] = get<unsigned int>(inst, 3u);
177 break;
178
179 case SpvOpTypeInt: // FALLTHROUGH
180 case SpvOpTypeFloat: {
181 const auto size = get<uint32_t>(inst, 2) / 8u;
182 types[get<SpvId>(inst, 1)] = { module::argument::scalar, size,
183 size, size,
184 module::argument::zero_ext };
185 break;
186 }
187
188 case SpvOpTypeArray: {
189 const auto id = get<SpvId>(inst, 1);
190 const auto type_id = get<SpvId>(inst, 2);
191 const auto types_iter = types.find(type_id);
192 if (types_iter == types.end())
193 break;
194
195 const auto constant_id = get<SpvId>(inst, 3);
196 const auto constants_iter = constants.find(constant_id);
197 if (constants_iter == constants.end()) {
198 err += "Constant " + std::to_string(constant_id) +
199 " is missing\n";
200 throw build_error();
201 }
202 const auto elem_size = types_iter->second.size;
203 const auto elem_nbs = constants_iter->second;
204 const auto size = elem_size * elem_nbs;
205 types[id] = { module::argument::scalar, size, size,
206 types_iter->second.target_align,
207 module::argument::zero_ext };
208 break;
209 }
210
211 case SpvOpTypeStruct: {
212 const auto id = get<SpvId>(inst, 1);
213 const bool is_packed = packed_structures.count(id);
214
215 unsigned struct_size = 0u;
216 unsigned struct_align = 1u;
217 for (unsigned j = 2u; j < num_operands; ++j) {
218 const auto type_id = get<SpvId>(inst, j);
219 const auto types_iter = types.find(type_id);
220
221 // If a type was not found, that means it is not one of the
222 // types allowed as kernel arguments. And since the module has
223 // been validated, this means this type is not used for kernel
224 // arguments, and therefore can be ignored.
225 if (types_iter == types.end())
226 break;
227
228 const auto alignment = is_packed ? 1u
229 : types_iter->second.target_align;
230 const auto padding = (-struct_size) & (alignment - 1u);
231 struct_size += padding + types_iter->second.target_size;
232 struct_align = std::max(struct_align, alignment);
233 }
234 struct_size += (-struct_size) & (struct_align - 1u);
235 types[id] = { module::argument::scalar, struct_size, struct_size,
236 struct_align, module::argument::zero_ext };
237 break;
238 }
239
240 case SpvOpTypeVector: {
241 const auto id = get<SpvId>(inst, 1);
242 const auto type_id = get<SpvId>(inst, 2);
243 const auto types_iter = types.find(type_id);
244
245 // If a type was not found, that means it is not one of the
246 // types allowed as kernel arguments. And since the module has
247 // been validated, this means this type is not used for kernel
248 // arguments, and therefore can be ignored.
249 if (types_iter == types.end())
250 break;
251
252 const auto elem_size = types_iter->second.size;
253 const auto elem_nbs = get<uint32_t>(inst, 3);
254 const auto size = elem_size * elem_nbs;
255 types[id] = { module::argument::scalar, size, size, size,
256 module::argument::zero_ext };
257 break;
258 }
259
260 case SpvOpTypeForwardPointer: // FALLTHROUGH
261 case SpvOpTypePointer: {
262 const auto id = get<SpvId>(inst, 1);
263 const auto storage_class = get<SpvStorageClass>(inst, 2);
264 // Input means this is for a builtin variable, which can not be
265 // passed as an argument to a kernel.
266 if (storage_class == SpvStorageClassInput)
267 break;
268 types[id] = { convert_storage_class(storage_class, err),
269 sizeof(cl_mem),
270 static_cast<module::size_t>(pointer_byte_size),
271 static_cast<module::size_t>(pointer_byte_size),
272 module::argument::zero_ext };
273 if (opcode == SpvOpTypePointer)
274 pointer_types[id] = get<SpvId>(inst, 3);
275 break;
276 }
277
278 case SpvOpTypeSampler:
279 types[get<SpvId>(inst, 1)] = { module::argument::sampler,
280 sizeof(cl_sampler) };
281 break;
282
283 case SpvOpTypeImage: {
284 const auto id = get<SpvId>(inst, 1);
285 const auto dim = get<SpvDim>(inst, 3);
286 const auto access = get<SpvAccessQualifier>(inst, 9);
287 types[id] = { convert_image_type(id, dim, access, err),
288 sizeof(cl_mem), sizeof(cl_mem), sizeof(cl_mem),
289 module::argument::zero_ext };
290 break;
291 }
292
293 case SpvOpTypePipe: // FALLTHROUGH
294 case SpvOpTypeQueue: {
295 err += "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are "
296 "not available in the currently supported OpenCL C version."
297 "\n";
298 throw build_error();
299 }
300
301 case SpvOpFunction: {
302 const auto kernels_iter = kernels.find(get<SpvId>(inst, 2));
303 if (kernels_iter != kernels.end())
304 kernel_name = kernels_iter->second;
305 break;
306 }
307
308 case SpvOpFunctionParameter: {
309 if (kernel_name.empty())
310 break;
311
312 const auto type_id = get<SpvId>(inst, 1);
313 auto arg = types.find(type_id)->second;
314 const auto &func_param_attr_iter =
315 func_param_attr_map.find(get<SpvId>(inst, 2));
316 if (func_param_attr_iter != func_param_attr_map.end()) {
317 for (auto &i : func_param_attr_iter->second) {
318 switch (i) {
319 case SpvFunctionParameterAttributeSext:
320 arg.ext_type = module::argument::sign_ext;
321 break;
322 case SpvFunctionParameterAttributeZext:
323 arg.ext_type = module::argument::zero_ext;
324 break;
325 case SpvFunctionParameterAttributeByVal: {
326 const SpvId ptr_type_id =
327 pointer_types.find(type_id)->second;
328 arg = types.find(ptr_type_id)->second;
329 break;
330 }
331 default:
332 break;
333 }
334 }
335 }
336 args.emplace_back(arg);
337 break;
338 }
339
340 case SpvOpFunctionEnd:
341 if (kernel_name.empty())
342 break;
343 m.syms.emplace_back(kernel_name, 0, kernel_nb, args);
344 ++kernel_nb;
345 kernel_name.clear();
346 args.clear();
347 break;
348
349 default:
350 break;
351 }
352
353 i += num_operands;
354 }
355
356 m.secs.push_back(make_text_section(source,
357 module::section::text_intermediate));
358 return m;
359 }
360
361 bool
362 check_capabilities(const device &dev, const std::vector<char> &source,
363 std::string &r_log) {
364 const size_t length = source.size() / sizeof(uint32_t);
365 size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
366
367 while (i < length) {
368 const auto desc_word = get<uint32_t>(source.data(), i);
369 const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
370 const unsigned int num_operands = desc_word >> SpvWordCountShift;
371
372 if (opcode != SpvOpCapability)
373 break;
374
375 const auto capability = get<SpvCapability>(source.data(), i + 1u);
376 switch (capability) {
377 // Mandatory capabilities
378 case SpvCapabilityAddresses:
379 case SpvCapabilityFloat16Buffer:
380 case SpvCapabilityGroups:
381 case SpvCapabilityInt64:
382 case SpvCapabilityInt16:
383 case SpvCapabilityInt8:
384 case SpvCapabilityKernel:
385 case SpvCapabilityLinkage:
386 case SpvCapabilityVector16:
387 break;
388 // Optional capabilities
389 case SpvCapabilityImageBasic:
390 case SpvCapabilityLiteralSampler:
391 case SpvCapabilitySampled1D:
392 case SpvCapabilityImage1D:
393 case SpvCapabilitySampledBuffer:
394 case SpvCapabilityImageBuffer:
395 if (!dev.image_support()) {
396 r_log += "Capability 'ImageBasic' is not supported.\n";
397 return false;
398 }
399 break;
400 case SpvCapabilityFloat64:
401 if (!dev.has_doubles()) {
402 r_log += "Capability 'Float64' is not supported.\n";
403 return false;
404 }
405 break;
406 // Enabled through extensions
407 case SpvCapabilityFloat16:
408 if (!dev.has_halves()) {
409 r_log += "Capability 'Float16' is not supported.\n";
410 return false;
411 }
412 break;
413 case SpvCapabilityInt64Atomics:
414 if (!dev.has_int64_atomics()) {
415 r_log += "Capability 'Int64Atomics' is not supported.\n";
416 return false;
417 }
418 break;
419 default:
420 r_log += "Capability '" + std::to_string(capability) +
421 "' is not supported.\n";
422 return false;
423 }
424
425 i += num_operands;
426 }
427
428 return true;
429 }
430
431 bool
432 check_extensions(const device &dev, const std::vector<char> &source,
433 std::string &r_log) {
434 const size_t length = source.size() / sizeof(uint32_t);
435 size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
436
437 while (i < length) {
438 const auto desc_word = get<uint32_t>(source.data(), i);
439 const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
440 const unsigned int num_operands = desc_word >> SpvWordCountShift;
441
442 if (opcode == SpvOpCapability) {
443 i += num_operands;
444 continue;
445 }
446 if (opcode != SpvOpExtension)
447 break;
448
449 const char *extension = source.data() + (i + 1u) * sizeof(uint32_t);
450 const std::string device_extensions = dev.supported_extensions();
451 const std::string platform_extensions =
452 dev.platform.supported_extensions();
453 if (device_extensions.find(extension) == std::string::npos &&
454 platform_extensions.find(extension) == std::string::npos) {
455 r_log += "Extension '" + std::string(extension) +
456 "' is not supported.\n";
457 return false;
458 }
459
460 i += num_operands;
461 }
462
463 return true;
464 }
465
466 bool
467 check_memory_model(const device &dev, const std::vector<char> &source,
468 std::string &r_log) {
469 const size_t length = source.size() / sizeof(uint32_t);
470 size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
471
472 while (i < length) {
473 const auto desc_word = get<uint32_t>(source.data(), i);
474 const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
475 const unsigned int num_operands = desc_word >> SpvWordCountShift;
476
477 switch (opcode) {
478 case SpvOpMemoryModel:
479 switch (get<SpvAddressingModel>(source.data(), i + 1u)) {
480 case SpvAddressingModelPhysical32:
481 return dev.address_bits() == 32;
482 case SpvAddressingModelPhysical64:
483 return dev.address_bits() == 64;
484 default:
485 unreachable("Only Physical32 and Physical64 are valid for OpenCL, and the binary was already validated");
486 return false;
487 }
488 break;
489 default:
490 break;
491 }
492
493 i += num_operands;
494 }
495
496 return false;
497 }
498
499 // Copies the input binary and convert it to the endianness of the host CPU.
500 std::vector<char>
501 spirv_to_cpu(const std::vector<char> &binary)
502 {
503 const uint32_t first_word = get<uint32_t>(binary.data(), 0u);
504 if (first_word == SpvMagicNumber)
505 return binary;
506
507 std::vector<char> cpu_endianness_binary(binary.size());
508 for (size_t i = 0; i < (binary.size() / 4u); ++i) {
509 const uint32_t word = get<uint32_t>(binary.data(), i);
510 reinterpret_cast<uint32_t *>(cpu_endianness_binary.data())[i] =
511 util_bswap32(word);
512 }
513
514 return cpu_endianness_binary;
515 }
516
517 #ifdef HAVE_CLOVER_SPIRV
518 std::string
519 format_validator_msg(spv_message_level_t level, const char * /* source */,
520 const spv_position_t &position, const char *message) {
521 std::string level_str;
522 switch (level) {
523 case SPV_MSG_FATAL:
524 level_str = "Fatal";
525 break;
526 case SPV_MSG_INTERNAL_ERROR:
527 level_str = "Internal error";
528 break;
529 case SPV_MSG_ERROR:
530 level_str = "Error";
531 break;
532 case SPV_MSG_WARNING:
533 level_str = "Warning";
534 break;
535 case SPV_MSG_INFO:
536 level_str = "Info";
537 break;
538 case SPV_MSG_DEBUG:
539 level_str = "Debug";
540 break;
541 }
542 return "[" + level_str + "] At word No." +
543 std::to_string(position.index) + ": \"" + message + "\"\n";
544 }
545
546 spv_target_env
547 convert_opencl_str_to_target_env(const std::string &opencl_version) {
548 if (opencl_version == "2.2") {
549 return SPV_ENV_OPENCL_2_2;
550 } else if (opencl_version == "2.1") {
551 return SPV_ENV_OPENCL_2_1;
552 } else if (opencl_version == "2.0") {
553 return SPV_ENV_OPENCL_2_0;
554 } else if (opencl_version == "1.2" ||
555 opencl_version == "1.1" ||
556 opencl_version == "1.0") {
557 // SPIR-V is only defined for OpenCL >= 1.2, however some drivers
558 // might use it with OpenCL 1.0 and 1.1.
559 return SPV_ENV_OPENCL_1_2;
560 } else {
561 throw build_error("Invalid OpenCL version");
562 }
563 }
564 #endif
565
566 }
567
568 module
569 clover::spirv::compile_program(const std::vector<char> &binary,
570 const device &dev, std::string &r_log) {
571 std::vector<char> source = spirv_to_cpu(binary);
572
573 if (!is_valid_spirv(source, dev.device_version(), r_log))
574 throw build_error();
575
576 if (!check_capabilities(dev, source, r_log))
577 throw build_error();
578 if (!check_extensions(dev, source, r_log))
579 throw build_error();
580 if (!check_memory_model(dev, source, r_log))
581 throw build_error();
582
583 return create_module_from_spirv(source,
584 dev.address_bits() == 32 ? 4u : 8u, r_log);
585 }
586
587 module
588 clover::spirv::link_program(const std::vector<module> &modules,
589 const device &dev, const std::string &opts,
590 std::string &r_log) {
591 std::vector<std::string> options = clover::llvm::tokenize(opts);
592
593 bool create_library = false;
594
595 std::string ignored_options;
596 for (const std::string &option : options) {
597 if (option == "-create-library") {
598 create_library = true;
599 } else {
600 ignored_options += "'" + option + "' ";
601 }
602 }
603 if (!ignored_options.empty()) {
604 r_log += "Ignoring the following link options: " + ignored_options
605 + "\n";
606 }
607
608 spvtools::LinkerOptions linker_options;
609 linker_options.SetCreateLibrary(create_library);
610
611 module m;
612
613 const auto section_type = create_library ? module::section::text_library :
614 module::section::text_executable;
615
616 std::vector<const uint32_t *> sections;
617 sections.reserve(modules.size());
618 std::vector<size_t> lengths;
619 lengths.reserve(modules.size());
620
621 auto const validator_consumer = [&r_log](spv_message_level_t level,
622 const char *source,
623 const spv_position_t &position,
624 const char *message) {
625 r_log += format_validator_msg(level, source, position, message);
626 };
627
628 for (const auto &mod : modules) {
629 const auto &msec = find([](const module::section &sec) {
630 return sec.type == module::section::text_intermediate ||
631 sec.type == module::section::text_library;
632 }, mod.secs);
633
634 const auto c_il = ((struct pipe_binary_program_header*)msec.data.data())->blob;
635 const auto length = msec.size;
636
637 sections.push_back(reinterpret_cast<const uint32_t *>(c_il));
638 lengths.push_back(length / sizeof(uint32_t));
639 }
640
641 std::vector<uint32_t> linked_binary;
642
643 const std::string opencl_version = dev.device_version();
644 const spv_target_env target_env =
645 convert_opencl_str_to_target_env(opencl_version);
646
647 const spvtools::MessageConsumer consumer = validator_consumer;
648 spvtools::Context context(target_env);
649 context.SetMessageConsumer(std::move(consumer));
650
651 if (Link(context, sections.data(), lengths.data(), sections.size(),
652 &linked_binary, linker_options) != SPV_SUCCESS)
653 throw error(CL_LINK_PROGRAM_FAILURE);
654
655 std::vector<char> final_binary{
656 reinterpret_cast<char *>(linked_binary.data()),
657 reinterpret_cast<char *>(linked_binary.data() +
658 linked_binary.size()) };
659 if (!is_valid_spirv(final_binary, opencl_version, r_log))
660 throw error(CL_LINK_PROGRAM_FAILURE);
661
662 for (const auto &mod : modules)
663 m.syms.insert(m.syms.end(), mod.syms.begin(), mod.syms.end());
664
665 m.secs.emplace_back(make_text_section(final_binary, section_type));
666
667 return m;
668 }
669
670 bool
671 clover::spirv::is_valid_spirv(const std::vector<char> &binary,
672 const std::string &opencl_version,
673 std::string &r_log) {
674 auto const validator_consumer =
675 [&r_log](spv_message_level_t level, const char *source,
676 const spv_position_t &position, const char *message) {
677 r_log += format_validator_msg(level, source, position, message);
678 };
679
680 const spv_target_env target_env =
681 convert_opencl_str_to_target_env(opencl_version);
682 spvtools::SpirvTools spvTool(target_env);
683 spvTool.SetMessageConsumer(validator_consumer);
684
685 return spvTool.Validate(reinterpret_cast<const uint32_t *>(binary.data()),
686 binary.size() / 4u);
687 }
688
689 std::string
690 clover::spirv::print_module(const std::vector<char> &binary,
691 const std::string &opencl_version) {
692 const spv_target_env target_env =
693 convert_opencl_str_to_target_env(opencl_version);
694 spvtools::SpirvTools spvTool(target_env);
695 spv_context spvContext = spvContextCreate(target_env);
696 if (!spvContext)
697 return "Failed to create an spv_context for disassembling the module.";
698
699 spv_text disassembly;
700 spvBinaryToText(spvContext,
701 reinterpret_cast<const uint32_t *>(binary.data()),
702 binary.size() / 4u, SPV_BINARY_TO_TEXT_OPTION_NONE,
703 &disassembly, nullptr);
704 spvContextDestroy(spvContext);
705
706 const std::string disassemblyStr = disassembly->str;
707 spvTextDestroy(disassembly);
708
709 return disassemblyStr;
710 }
711
712 #else
713 bool
714 clover::spirv::is_valid_spirv(const std::vector<char> &/*binary*/,
715 const std::string &/*opencl_version*/,
716 std::string &/*r_log*/) {
717 return false;
718 }
719
720 module
721 clover::spirv::compile_program(const std::vector<char> &binary,
722 const device &dev, std::string &r_log) {
723 r_log += "SPIR-V support in clover is not enabled.\n";
724 throw build_error();
725 }
726
727 module
728 clover::spirv::link_program(const std::vector<module> &/*modules*/,
729 const device &/*dev*/, const std::string &/*opts*/,
730 std::string &r_log) {
731 r_log += "SPIR-V support in clover is not enabled.\n";
732 throw error(CL_LINKER_NOT_AVAILABLE);
733 }
734
735 std::string
736 clover::spirv::print_module(const std::vector<char> &binary,
737 const std::string &opencl_version) {
738 return std::string();
739 }
740 #endif