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