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