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