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