Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/gallium/frontends/clover/spirv/invocation.cpp
4573 views
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>
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
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
cl_kernel_arg_address_qualifier
76
convert_storage_class_to_cl(SpvStorageClass storage_class) {
77
switch (storage_class) {
78
case SpvStorageClassUniformConstant:
79
return CL_KERNEL_ARG_ADDRESS_CONSTANT;
80
case SpvStorageClassWorkgroup:
81
return CL_KERNEL_ARG_ADDRESS_LOCAL;
82
case SpvStorageClassCrossWorkgroup:
83
return CL_KERNEL_ARG_ADDRESS_GLOBAL;
84
case SpvStorageClassFunction:
85
default:
86
return CL_KERNEL_ARG_ADDRESS_PRIVATE;
87
}
88
}
89
90
enum module::argument::type
91
convert_image_type(SpvId id, SpvDim dim, SpvAccessQualifier access,
92
std::string &err) {
93
switch (dim) {
94
case SpvDim1D:
95
case SpvDim2D:
96
case SpvDim3D:
97
case SpvDimBuffer:
98
switch (access) {
99
case SpvAccessQualifierReadOnly:
100
return module::argument::image_rd;
101
case SpvAccessQualifierWriteOnly:
102
return module::argument::image_wr;
103
default:
104
err += "Unknown access qualifier " + std::to_string(access) + " for image "
105
+ std::to_string(id) + ".\n";
106
throw build_error();
107
}
108
default:
109
err += "Unknown dimension " + std::to_string(dim) + " for image "
110
+ std::to_string(id) + ".\n";
111
throw build_error();
112
}
113
}
114
115
module::section
116
make_text_section(const std::string &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
129
create_module_from_spirv(const std::string &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 != 3 ? elem_nbs : 4);
334
types[id] = { module::argument::scalar, size, size, size,
335
module::argument::zero_ext };
336
types[id].info.address_qualifier = CL_KERNEL_ARG_ADDRESS_PRIVATE;
337
break;
338
}
339
340
case SpvOpTypeForwardPointer: // FALLTHROUGH
341
case SpvOpTypePointer: {
342
const auto id = get<SpvId>(inst, 1);
343
const auto storage_class = get<SpvStorageClass>(inst, 2);
344
// Input means this is for a builtin variable, which can not be
345
// passed as an argument to a kernel.
346
if (storage_class == SpvStorageClassInput)
347
break;
348
349
if (opcode == SpvOpTypePointer)
350
pointer_types[id] = get<SpvId>(inst, 3);
351
352
types[id] = { convert_storage_class(storage_class, err),
353
sizeof(cl_mem),
354
static_cast<module::size_t>(pointer_byte_size),
355
static_cast<module::size_t>(pointer_byte_size),
356
module::argument::zero_ext };
357
types[id].info.address_qualifier = convert_storage_class_to_cl(storage_class);
358
break;
359
}
360
361
case SpvOpTypeSampler:
362
types[get<SpvId>(inst, 1)] = { module::argument::sampler,
363
sizeof(cl_sampler) };
364
break;
365
366
case SpvOpTypeImage: {
367
const auto id = get<SpvId>(inst, 1);
368
const auto dim = get<SpvDim>(inst, 3);
369
const auto access = get<SpvAccessQualifier>(inst, 9);
370
types[id] = { convert_image_type(id, dim, access, err),
371
sizeof(cl_mem), sizeof(cl_mem), sizeof(cl_mem),
372
module::argument::zero_ext };
373
break;
374
}
375
376
case SpvOpTypePipe: // FALLTHROUGH
377
case SpvOpTypeQueue: {
378
err += "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are "
379
"not available in the currently supported OpenCL C version."
380
"\n";
381
throw build_error();
382
}
383
384
case SpvOpFunction: {
385
auto id = get<SpvId>(inst, 2);
386
const auto kernels_iter = kernels.find(id);
387
if (kernels_iter != kernels.end())
388
kernel_name = kernels_iter->second;
389
390
const auto req_local_size_iter = req_local_sizes.find(id);
391
if (req_local_size_iter != req_local_sizes.end())
392
req_local_size = (*req_local_size_iter).second;
393
else
394
req_local_size = { 0, 0, 0 };
395
396
break;
397
}
398
399
case SpvOpFunctionParameter: {
400
if (kernel_name.empty())
401
break;
402
403
const auto id = get<SpvId>(inst, 2);
404
const auto type_id = get<SpvId>(inst, 1);
405
auto arg = types.find(type_id)->second;
406
const auto &func_param_attr_iter =
407
func_param_attr_map.find(get<SpvId>(inst, 2));
408
if (func_param_attr_iter != func_param_attr_map.end()) {
409
for (auto &i : func_param_attr_iter->second) {
410
switch (i) {
411
case SpvFunctionParameterAttributeSext:
412
arg.ext_type = module::argument::sign_ext;
413
break;
414
case SpvFunctionParameterAttributeZext:
415
arg.ext_type = module::argument::zero_ext;
416
break;
417
case SpvFunctionParameterAttributeByVal: {
418
const SpvId ptr_type_id =
419
pointer_types.find(type_id)->second;
420
arg = types.find(ptr_type_id)->second;
421
break;
422
}
423
case SpvFunctionParameterAttributeNoAlias:
424
arg.info.type_qualifier |= CL_KERNEL_ARG_TYPE_RESTRICT;
425
break;
426
case SpvFunctionParameterAttributeNoWrite:
427
arg.info.type_qualifier |= CL_KERNEL_ARG_TYPE_CONST;
428
break;
429
default:
430
break;
431
}
432
}
433
}
434
435
auto name_it = names.find(id);
436
if (name_it != names.end())
437
arg.info.arg_name = (*name_it).second;
438
439
arg.info.type_qualifier |= qualifiers[id];
440
arg.info.address_qualifier = types[type_id].info.address_qualifier;
441
arg.info.access_qualifier = CL_KERNEL_ARG_ACCESS_NONE;
442
args.emplace_back(arg);
443
break;
444
}
445
446
case SpvOpFunctionEnd:
447
if (kernel_name.empty())
448
break;
449
450
for (size_t i = 0; i < param_type_names[kernel_name].size(); i++)
451
args[i].info.type_name = param_type_names[kernel_name][i];
452
453
m.syms.emplace_back(kernel_name, std::string(),
454
req_local_size, 0, kernel_nb, args);
455
++kernel_nb;
456
kernel_name.clear();
457
args.clear();
458
break;
459
460
default:
461
break;
462
}
463
464
i += num_operands;
465
}
466
467
m.secs.push_back(make_text_section(source,
468
module::section::text_intermediate));
469
return m;
470
}
471
472
bool
473
check_spirv_version(const device &dev, const char *binary,
474
std::string &r_log) {
475
const auto spirv_version = get<uint32_t>(binary, 1u);
476
const auto supported_spirv_versions = clover::spirv::supported_versions();
477
const auto compare_versions =
478
[module_version =
479
clover::spirv::to_opencl_version_encoding(spirv_version)](const cl_name_version &supported){
480
return supported.version == module_version;
481
};
482
483
if (std::find_if(supported_spirv_versions.cbegin(),
484
supported_spirv_versions.cend(),
485
compare_versions) != supported_spirv_versions.cend())
486
return true;
487
488
r_log += "SPIR-V version " +
489
clover::spirv::version_to_string(spirv_version) +
490
" is not supported; supported versions:";
491
for (const auto &version : supported_spirv_versions) {
492
r_log += " " + clover::spirv::version_to_string(version.version);
493
}
494
r_log += "\n";
495
return false;
496
}
497
498
bool
499
check_capabilities(const device &dev, const std::string &source,
500
std::string &r_log) {
501
const size_t length = source.size() / sizeof(uint32_t);
502
size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
503
504
while (i < length) {
505
const auto desc_word = get<uint32_t>(source.data(), i);
506
const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
507
const unsigned int num_operands = desc_word >> SpvWordCountShift;
508
509
if (opcode != SpvOpCapability)
510
break;
511
512
const auto capability = get<SpvCapability>(source.data(), i + 1u);
513
switch (capability) {
514
// Mandatory capabilities
515
case SpvCapabilityAddresses:
516
case SpvCapabilityFloat16Buffer:
517
case SpvCapabilityGroups:
518
case SpvCapabilityInt64:
519
case SpvCapabilityInt16:
520
case SpvCapabilityInt8:
521
case SpvCapabilityKernel:
522
case SpvCapabilityLinkage:
523
case SpvCapabilityVector16:
524
break;
525
// Optional capabilities
526
case SpvCapabilityImageBasic:
527
case SpvCapabilityLiteralSampler:
528
case SpvCapabilitySampled1D:
529
case SpvCapabilityImage1D:
530
case SpvCapabilitySampledBuffer:
531
case SpvCapabilityImageBuffer:
532
if (!dev.image_support()) {
533
r_log += "Capability 'ImageBasic' is not supported.\n";
534
return false;
535
}
536
break;
537
case SpvCapabilityFloat64:
538
if (!dev.has_doubles()) {
539
r_log += "Capability 'Float64' is not supported.\n";
540
return false;
541
}
542
break;
543
// Enabled through extensions
544
case SpvCapabilityFloat16:
545
if (!dev.has_halves()) {
546
r_log += "Capability 'Float16' is not supported.\n";
547
return false;
548
}
549
break;
550
case SpvCapabilityInt64Atomics:
551
if (!dev.has_int64_atomics()) {
552
r_log += "Capability 'Int64Atomics' is not supported.\n";
553
return false;
554
}
555
break;
556
default:
557
r_log += "Capability '" + std::to_string(capability) +
558
"' is not supported.\n";
559
return false;
560
}
561
562
i += num_operands;
563
}
564
565
return true;
566
}
567
568
bool
569
check_extensions(const device &dev, const std::string &source,
570
std::string &r_log) {
571
const size_t length = source.size() / sizeof(uint32_t);
572
size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
573
const auto spirv_extensions = spirv::supported_extensions();
574
575
while (i < length) {
576
const auto desc_word = get<uint32_t>(source.data(), i);
577
const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
578
const unsigned int num_operands = desc_word >> SpvWordCountShift;
579
580
if (opcode == SpvOpCapability) {
581
i += num_operands;
582
continue;
583
}
584
if (opcode != SpvOpExtension)
585
break;
586
587
const std::string extension = source.data() + (i + 1u) * sizeof(uint32_t);
588
if (spirv_extensions.count(extension) == 0) {
589
r_log += "Extension '" + extension + "' is not supported.\n";
590
return false;
591
}
592
593
i += num_operands;
594
}
595
596
return true;
597
}
598
599
bool
600
check_memory_model(const device &dev, const std::string &source,
601
std::string &r_log) {
602
const size_t length = source.size() / sizeof(uint32_t);
603
size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header
604
605
while (i < length) {
606
const auto desc_word = get<uint32_t>(source.data(), i);
607
const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);
608
const unsigned int num_operands = desc_word >> SpvWordCountShift;
609
610
switch (opcode) {
611
case SpvOpMemoryModel:
612
switch (get<SpvAddressingModel>(source.data(), i + 1u)) {
613
case SpvAddressingModelPhysical32:
614
return dev.address_bits() == 32;
615
case SpvAddressingModelPhysical64:
616
return dev.address_bits() == 64;
617
default:
618
unreachable("Only Physical32 and Physical64 are valid for OpenCL, and the binary was already validated");
619
return false;
620
}
621
break;
622
default:
623
break;
624
}
625
626
i += num_operands;
627
}
628
629
return false;
630
}
631
632
// Copies the input binary and convert it to the endianness of the host CPU.
633
std::string
634
spirv_to_cpu(const std::string &binary)
635
{
636
const uint32_t first_word = get<uint32_t>(binary.data(), 0u);
637
if (first_word == SpvMagicNumber)
638
return binary;
639
640
std::vector<char> cpu_endianness_binary(binary.size());
641
for (size_t i = 0; i < (binary.size() / 4u); ++i) {
642
const uint32_t word = get<uint32_t>(binary.data(), i);
643
reinterpret_cast<uint32_t *>(cpu_endianness_binary.data())[i] =
644
util_bswap32(word);
645
}
646
647
return std::string(cpu_endianness_binary.begin(),
648
cpu_endianness_binary.end());
649
}
650
651
#ifdef HAVE_CLOVER_SPIRV
652
std::string
653
format_validator_msg(spv_message_level_t level, const char * /* source */,
654
const spv_position_t &position, const char *message) {
655
std::string level_str;
656
switch (level) {
657
case SPV_MSG_FATAL:
658
level_str = "Fatal";
659
break;
660
case SPV_MSG_INTERNAL_ERROR:
661
level_str = "Internal error";
662
break;
663
case SPV_MSG_ERROR:
664
level_str = "Error";
665
break;
666
case SPV_MSG_WARNING:
667
level_str = "Warning";
668
break;
669
case SPV_MSG_INFO:
670
level_str = "Info";
671
break;
672
case SPV_MSG_DEBUG:
673
level_str = "Debug";
674
break;
675
}
676
return "[" + level_str + "] At word No." +
677
std::to_string(position.index) + ": \"" + message + "\"\n";
678
}
679
680
spv_target_env
681
convert_opencl_version_to_target_env(const cl_version opencl_version) {
682
// Pick 1.2 for 3.0 for now
683
if (opencl_version == CL_MAKE_VERSION(3, 0, 0)) {
684
return SPV_ENV_OPENCL_1_2;
685
} else if (opencl_version == CL_MAKE_VERSION(2, 2, 0)) {
686
return SPV_ENV_OPENCL_2_2;
687
} else if (opencl_version == CL_MAKE_VERSION(2, 1, 0)) {
688
return SPV_ENV_OPENCL_2_1;
689
} else if (opencl_version == CL_MAKE_VERSION(2, 0, 0)) {
690
return SPV_ENV_OPENCL_2_0;
691
} else if (opencl_version == CL_MAKE_VERSION(1, 2, 0) ||
692
opencl_version == CL_MAKE_VERSION(1, 1, 0) ||
693
opencl_version == CL_MAKE_VERSION(1, 0, 0)) {
694
// SPIR-V is only defined for OpenCL >= 1.2, however some drivers
695
// might use it with OpenCL 1.0 and 1.1.
696
return SPV_ENV_OPENCL_1_2;
697
} else {
698
throw build_error("Invalid OpenCL version");
699
}
700
}
701
#endif
702
703
}
704
705
bool
706
clover::spirv::is_binary_spirv(const std::string &binary)
707
{
708
// A SPIR-V binary is at the very least 5 32-bit words, which represent the
709
// SPIR-V header.
710
if (binary.size() < 20u)
711
return false;
712
713
const uint32_t first_word =
714
reinterpret_cast<const uint32_t *>(binary.data())[0u];
715
return (first_word == SpvMagicNumber) ||
716
(util_bswap32(first_word) == SpvMagicNumber);
717
}
718
719
std::string
720
clover::spirv::version_to_string(uint32_t version) {
721
const uint32_t major_version = (version >> 16) & 0xff;
722
const uint32_t minor_version = (version >> 8) & 0xff;
723
return std::to_string(major_version) + '.' +
724
std::to_string(minor_version);
725
}
726
727
module
728
clover::spirv::compile_program(const std::string &binary,
729
const device &dev, std::string &r_log,
730
bool validate) {
731
std::string source = spirv_to_cpu(binary);
732
733
if (validate && !is_valid_spirv(source, dev.device_version(), r_log))
734
throw build_error();
735
736
if (!check_spirv_version(dev, source.data(), r_log))
737
throw build_error();
738
if (!check_capabilities(dev, source, r_log))
739
throw build_error();
740
if (!check_extensions(dev, source, r_log))
741
throw build_error();
742
if (!check_memory_model(dev, source, r_log))
743
throw build_error();
744
745
return create_module_from_spirv(source,
746
dev.address_bits() == 32 ? 4u : 8u, r_log);
747
}
748
749
module
750
clover::spirv::link_program(const std::vector<module> &modules,
751
const device &dev, const std::string &opts,
752
std::string &r_log) {
753
std::vector<std::string> options = tokenize(opts);
754
755
bool create_library = false;
756
757
std::string ignored_options;
758
for (const std::string &option : options) {
759
if (option == "-create-library") {
760
create_library = true;
761
} else {
762
ignored_options += "'" + option + "' ";
763
}
764
}
765
if (!ignored_options.empty()) {
766
r_log += "Ignoring the following link options: " + ignored_options
767
+ "\n";
768
}
769
770
spvtools::LinkerOptions linker_options;
771
linker_options.SetCreateLibrary(create_library);
772
773
module m;
774
775
const auto section_type = create_library ? module::section::text_library :
776
module::section::text_executable;
777
778
std::vector<const uint32_t *> sections;
779
sections.reserve(modules.size());
780
std::vector<size_t> lengths;
781
lengths.reserve(modules.size());
782
783
auto const validator_consumer = [&r_log](spv_message_level_t level,
784
const char *source,
785
const spv_position_t &position,
786
const char *message) {
787
r_log += format_validator_msg(level, source, position, message);
788
};
789
790
for (const auto &mod : modules) {
791
const auto &msec = find([](const module::section &sec) {
792
return sec.type == module::section::text_intermediate ||
793
sec.type == module::section::text_library;
794
}, mod.secs);
795
796
const auto c_il = ((struct pipe_binary_program_header*)msec.data.data())->blob;
797
const auto length = msec.size;
798
799
if (!check_spirv_version(dev, c_il, r_log))
800
throw error(CL_LINK_PROGRAM_FAILURE);
801
802
sections.push_back(reinterpret_cast<const uint32_t *>(c_il));
803
lengths.push_back(length / sizeof(uint32_t));
804
}
805
806
std::vector<uint32_t> linked_binary;
807
808
const cl_version opencl_version = dev.device_version();
809
const spv_target_env target_env =
810
convert_opencl_version_to_target_env(opencl_version);
811
812
const spvtools::MessageConsumer consumer = validator_consumer;
813
spvtools::Context context(target_env);
814
context.SetMessageConsumer(std::move(consumer));
815
816
if (Link(context, sections.data(), lengths.data(), sections.size(),
817
&linked_binary, linker_options) != SPV_SUCCESS)
818
throw error(CL_LINK_PROGRAM_FAILURE);
819
820
std::string final_binary{
821
reinterpret_cast<char *>(linked_binary.data()),
822
reinterpret_cast<char *>(linked_binary.data() +
823
linked_binary.size()) };
824
if (!is_valid_spirv(final_binary, opencl_version, r_log))
825
throw error(CL_LINK_PROGRAM_FAILURE);
826
827
if (has_flag(llvm::debug::spirv))
828
llvm::debug::log(".spvasm", spirv::print_module(final_binary, dev.device_version()));
829
830
for (const auto &mod : modules)
831
m.syms.insert(m.syms.end(), mod.syms.begin(), mod.syms.end());
832
833
m.secs.emplace_back(make_text_section(final_binary, section_type));
834
835
return m;
836
}
837
838
bool
839
clover::spirv::is_valid_spirv(const std::string &binary,
840
const cl_version opencl_version,
841
std::string &r_log) {
842
auto const validator_consumer =
843
[&r_log](spv_message_level_t level, const char *source,
844
const spv_position_t &position, const char *message) {
845
r_log += format_validator_msg(level, source, position, message);
846
};
847
848
const spv_target_env target_env =
849
convert_opencl_version_to_target_env(opencl_version);
850
spvtools::SpirvTools spvTool(target_env);
851
spvTool.SetMessageConsumer(validator_consumer);
852
853
return spvTool.Validate(reinterpret_cast<const uint32_t *>(binary.data()),
854
binary.size() / 4u);
855
}
856
857
std::string
858
clover::spirv::print_module(const std::string &binary,
859
const cl_version opencl_version) {
860
const spv_target_env target_env =
861
convert_opencl_version_to_target_env(opencl_version);
862
spvtools::SpirvTools spvTool(target_env);
863
spv_context spvContext = spvContextCreate(target_env);
864
if (!spvContext)
865
return "Failed to create an spv_context for disassembling the module.";
866
867
spv_text disassembly;
868
spvBinaryToText(spvContext,
869
reinterpret_cast<const uint32_t *>(binary.data()),
870
binary.size() / 4u, SPV_BINARY_TO_TEXT_OPTION_NONE,
871
&disassembly, nullptr);
872
spvContextDestroy(spvContext);
873
874
const std::string disassemblyStr = disassembly->str;
875
spvTextDestroy(disassembly);
876
877
return disassemblyStr;
878
}
879
880
std::unordered_set<std::string>
881
clover::spirv::supported_extensions() {
882
return {
883
/* this is only a hint so all devices support that */
884
"SPV_KHR_no_integer_wrap_decoration"
885
};
886
}
887
888
std::vector<cl_name_version>
889
clover::spirv::supported_versions() {
890
return { cl_name_version { CL_MAKE_VERSION(1u, 0u, 0u), "SPIR-V" } };
891
}
892
893
cl_version
894
clover::spirv::to_opencl_version_encoding(uint32_t version) {
895
return CL_MAKE_VERSION((version >> 16u) & 0xff,
896
(version >> 8u) & 0xff, 0u);
897
}
898
899
uint32_t
900
clover::spirv::to_spirv_version_encoding(cl_version version) {
901
return ((CL_VERSION_MAJOR(version) & 0xff) << 16u) |
902
((CL_VERSION_MINOR(version) & 0xff) << 8u);
903
}
904
905
#else
906
bool
907
clover::spirv::is_binary_spirv(const std::string &binary)
908
{
909
return false;
910
}
911
912
bool
913
clover::spirv::is_valid_spirv(const std::string &/*binary*/,
914
const cl_version opencl_version,
915
std::string &/*r_log*/) {
916
return false;
917
}
918
919
std::string
920
clover::spirv::version_to_string(uint32_t version) {
921
return "";
922
}
923
924
module
925
clover::spirv::compile_program(const std::string &binary,
926
const device &dev, std::string &r_log,
927
bool validate) {
928
r_log += "SPIR-V support in clover is not enabled.\n";
929
throw build_error();
930
}
931
932
module
933
clover::spirv::link_program(const std::vector<module> &/*modules*/,
934
const device &/*dev*/, const std::string &/*opts*/,
935
std::string &r_log) {
936
r_log += "SPIR-V support in clover is not enabled.\n";
937
throw error(CL_LINKER_NOT_AVAILABLE);
938
}
939
940
std::string
941
clover::spirv::print_module(const std::string &binary,
942
const cl_version opencl_version) {
943
return std::string();
944
}
945
946
std::unordered_set<std::string>
947
clover::spirv::supported_extensions() {
948
return {};
949
}
950
951
std::vector<cl_name_version>
952
clover::spirv::supported_versions() {
953
return {};
954
}
955
956
cl_version
957
clover::spirv::to_opencl_version_encoding(uint32_t version) {
958
return CL_MAKE_VERSION(0u, 0u, 0u);
959
}
960
961
uint32_t
962
clover::spirv::to_spirv_version_encoding(cl_version version) {
963
return 0u;
964
}
965
#endif
966
967