2 // Copyright 2018 Pierre Moreau
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:
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
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.
23 #include "invocation.hpp"
25 #include <unordered_map>
26 #include <unordered_set>
29 #ifdef HAVE_CLOVER_SPIRV
30 #include <spirv-tools/libspirv.hpp>
31 #include <spirv-tools/linker.hpp>
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"
43 #include "compiler/spirv/spirv.h"
45 #define SPIRV_HEADER_WORD_SIZE 5
47 using namespace clover
;
49 #ifdef HAVE_CLOVER_SPIRV
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);
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
]);
64 enum module::argument::type
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
;
76 err
+= "Invalid storage type " + std::to_string(storage_class
) + "\n";
81 enum module::argument::type
82 convert_image_type(SpvId id
, SpvDim dim
, SpvAccessQualifier access
,
84 if (dim
== SpvDim2D
&& access
== SpvAccessQualifierReadOnly
)
85 return module::argument::image2d_rd
;
86 else if (dim
== SpvDim2D
&& access
== SpvAccessQualifierWriteOnly
)
87 return module::argument::image2d_wr
;
88 else if (dim
== SpvDim3D
&& access
== SpvAccessQualifierReadOnly
)
89 return module::argument::image3d_rd
;
90 else if (dim
== SpvDim3D
&& access
== SpvAccessQualifierWriteOnly
)
91 return module::argument::image3d_wr
;
93 err
+= "Unknown access qualifier " + std::to_string(access
)
94 + " or dimension " + std::to_string(dim
) + " for image "
95 + std::to_string(id
) + ".\n";
101 make_text_section(const std::vector
<char> &code
,
102 enum module::section::type section_type
) {
103 const pipe_binary_program_header header
{ uint32_t(code
.size()) };
104 module::section text
{ 0, section_type
, header
.num_bytes
, {} };
106 text
.data
.insert(text
.data
.end(), reinterpret_cast<const char *>(&header
),
107 reinterpret_cast<const char *>(&header
) + sizeof(header
));
108 text
.data
.insert(text
.data
.end(), code
.begin(), code
.end());
114 create_module_from_spirv(const std::vector
<char> &source
,
115 size_t pointer_byte_size
,
117 const size_t length
= source
.size() / sizeof(uint32_t);
118 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
120 std::string kernel_name
;
121 size_t kernel_nb
= 0u;
122 std::vector
<module::argument
> args
;
126 std::unordered_map
<SpvId
, std::string
> kernels
;
127 std::unordered_map
<SpvId
, module::argument
> types
;
128 std::unordered_map
<SpvId
, SpvId
> pointer_types
;
129 std::unordered_map
<SpvId
, unsigned int> constants
;
130 std::unordered_set
<SpvId
> packed_structures
;
131 std::unordered_map
<SpvId
, std::vector
<SpvFunctionParameterAttribute
>>
135 const auto inst
= &source
[i
* sizeof(uint32_t)];
136 const auto desc_word
= get
<uint32_t>(inst
, 0);
137 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
138 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
141 case SpvOpEntryPoint
:
142 if (get
<SpvExecutionModel
>(inst
, 1) == SpvExecutionModelKernel
)
143 kernels
.emplace(get
<SpvId
>(inst
, 2),
144 source
.data() + (i
+ 3u) * sizeof(uint32_t));
147 case SpvOpDecorate
: {
148 const auto id
= get
<SpvId
>(inst
, 1);
149 const auto decoration
= get
<SpvDecoration
>(inst
, 2);
150 if (decoration
== SpvDecorationCPacked
)
151 packed_structures
.emplace(id
);
152 else if (decoration
== SpvDecorationFuncParamAttr
) {
153 const auto attribute
=
154 get
<SpvFunctionParameterAttribute
>(inst
, 3u);
155 func_param_attr_map
[id
].push_back(attribute
);
160 case SpvOpGroupDecorate
: {
161 const auto group_id
= get
<SpvId
>(inst
, 1);
162 if (packed_structures
.count(group_id
)) {
163 for (unsigned int i
= 2u; i
< num_operands
; ++i
)
164 packed_structures
.emplace(get
<SpvId
>(inst
, i
));
166 const auto func_param_attr_iter
=
167 func_param_attr_map
.find(group_id
);
168 if (func_param_attr_iter
!= func_param_attr_map
.end()) {
169 for (unsigned int i
= 2u; i
< num_operands
; ++i
)
170 func_param_attr_map
.emplace(get
<SpvId
>(inst
, i
),
171 func_param_attr_iter
->second
);
177 // We only care about constants that represent the size of arrays.
178 // If they are passed as argument, they will never be more than
179 // 4GB-wide, and even if they did, a clover::module::argument size
180 // is represented by an int.
181 constants
[get
<SpvId
>(inst
, 2)] = get
<unsigned int>(inst
, 3u);
184 case SpvOpTypeInt
: // FALLTHROUGH
185 case SpvOpTypeFloat
: {
186 const auto size
= get
<uint32_t>(inst
, 2) / 8u;
187 types
[get
<SpvId
>(inst
, 1)] = { module::argument::scalar
, size
,
189 module::argument::zero_ext
};
193 case SpvOpTypeArray
: {
194 const auto id
= get
<SpvId
>(inst
, 1);
195 const auto type_id
= get
<SpvId
>(inst
, 2);
196 const auto types_iter
= types
.find(type_id
);
197 if (types_iter
== types
.end())
200 const auto constant_id
= get
<SpvId
>(inst
, 3);
201 const auto constants_iter
= constants
.find(constant_id
);
202 if (constants_iter
== constants
.end()) {
203 err
+= "Constant " + std::to_string(constant_id
) +
207 const auto elem_size
= types_iter
->second
.size
;
208 const auto elem_nbs
= constants_iter
->second
;
209 const auto size
= elem_size
* elem_nbs
;
210 types
[id
] = { module::argument::scalar
, size
, size
,
211 types_iter
->second
.target_align
,
212 module::argument::zero_ext
};
216 case SpvOpTypeStruct
: {
217 const auto id
= get
<SpvId
>(inst
, 1);
218 const bool is_packed
= packed_structures
.count(id
);
220 unsigned struct_size
= 0u;
221 unsigned struct_align
= 1u;
222 for (unsigned j
= 2u; j
< num_operands
; ++j
) {
223 const auto type_id
= get
<SpvId
>(inst
, j
);
224 const auto types_iter
= types
.find(type_id
);
226 // If a type was not found, that means it is not one of the
227 // types allowed as kernel arguments. And since the module has
228 // been validated, this means this type is not used for kernel
229 // arguments, and therefore can be ignored.
230 if (types_iter
== types
.end())
233 const auto alignment
= is_packed
? 1u
234 : types_iter
->second
.target_align
;
235 const auto padding
= (-struct_size
) & (alignment
- 1u);
236 struct_size
+= padding
+ types_iter
->second
.target_size
;
237 struct_align
= std::max(struct_align
, alignment
);
239 struct_size
+= (-struct_size
) & (struct_align
- 1u);
240 types
[id
] = { module::argument::scalar
, struct_size
, struct_size
,
241 struct_align
, module::argument::zero_ext
};
245 case SpvOpTypeVector
: {
246 const auto id
= get
<SpvId
>(inst
, 1);
247 const auto type_id
= get
<SpvId
>(inst
, 2);
248 const auto types_iter
= types
.find(type_id
);
250 // If a type was not found, that means it is not one of the
251 // types allowed as kernel arguments. And since the module has
252 // been validated, this means this type is not used for kernel
253 // arguments, and therefore can be ignored.
254 if (types_iter
== types
.end())
257 const auto elem_size
= types_iter
->second
.size
;
258 const auto elem_nbs
= get
<uint32_t>(inst
, 3);
259 const auto size
= elem_size
* elem_nbs
;
260 types
[id
] = { module::argument::scalar
, size
, size
, size
,
261 module::argument::zero_ext
};
265 case SpvOpTypeForwardPointer
: // FALLTHROUGH
266 case SpvOpTypePointer
: {
267 const auto id
= get
<SpvId
>(inst
, 1);
268 const auto storage_class
= get
<SpvStorageClass
>(inst
, 2);
269 // Input means this is for a builtin variable, which can not be
270 // passed as an argument to a kernel.
271 if (storage_class
== SpvStorageClassInput
)
273 types
[id
] = { convert_storage_class(storage_class
, err
),
275 static_cast<module::size_t>(pointer_byte_size
),
276 static_cast<module::size_t>(pointer_byte_size
),
277 module::argument::zero_ext
};
278 if (opcode
== SpvOpTypePointer
)
279 pointer_types
[id
] = get
<SpvId
>(inst
, 3);
283 case SpvOpTypeSampler
:
284 types
[get
<SpvId
>(inst
, 1)] = { module::argument::sampler
,
285 sizeof(cl_sampler
) };
288 case SpvOpTypeImage
: {
289 const auto id
= get
<SpvId
>(inst
, 1);
290 const auto dim
= get
<SpvDim
>(inst
, 3);
291 const auto access
= get
<SpvAccessQualifier
>(inst
, 9);
292 types
[id
] = { convert_image_type(id
, dim
, access
, err
),
293 sizeof(cl_mem
), sizeof(cl_mem
), sizeof(cl_mem
),
294 module::argument::zero_ext
};
298 case SpvOpTypePipe
: // FALLTHROUGH
299 case SpvOpTypeQueue
: {
300 err
+= "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are "
301 "not available in the currently supported OpenCL C version."
306 case SpvOpFunction
: {
307 const auto kernels_iter
= kernels
.find(get
<SpvId
>(inst
, 2));
308 if (kernels_iter
!= kernels
.end())
309 kernel_name
= kernels_iter
->second
;
313 case SpvOpFunctionParameter
: {
314 if (kernel_name
.empty())
317 const auto type_id
= get
<SpvId
>(inst
, 1);
318 auto arg
= types
.find(type_id
)->second
;
319 const auto &func_param_attr_iter
=
320 func_param_attr_map
.find(get
<SpvId
>(inst
, 2));
321 if (func_param_attr_iter
!= func_param_attr_map
.end()) {
322 for (auto &i
: func_param_attr_iter
->second
) {
324 case SpvFunctionParameterAttributeSext
:
325 arg
.ext_type
= module::argument::sign_ext
;
327 case SpvFunctionParameterAttributeZext
:
328 arg
.ext_type
= module::argument::zero_ext
;
330 case SpvFunctionParameterAttributeByVal
: {
331 const SpvId ptr_type_id
=
332 pointer_types
.find(type_id
)->second
;
333 arg
= types
.find(ptr_type_id
)->second
;
341 args
.emplace_back(arg
);
345 case SpvOpFunctionEnd
:
346 if (kernel_name
.empty())
348 m
.syms
.emplace_back(kernel_name
, 0, kernel_nb
, args
);
361 m
.secs
.push_back(make_text_section(source
,
362 module::section::text_intermediate
));
367 check_capabilities(const device
&dev
, const std::vector
<char> &source
,
368 std::string
&r_log
) {
369 const size_t length
= source
.size() / sizeof(uint32_t);
370 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
373 const auto desc_word
= get
<uint32_t>(source
.data(), i
);
374 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
375 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
377 if (opcode
!= SpvOpCapability
)
380 const auto capability
= get
<SpvCapability
>(source
.data(), i
+ 1u);
381 switch (capability
) {
382 // Mandatory capabilities
383 case SpvCapabilityAddresses
:
384 case SpvCapabilityFloat16Buffer
:
385 case SpvCapabilityGroups
:
386 case SpvCapabilityInt64
:
387 case SpvCapabilityInt16
:
388 case SpvCapabilityInt8
:
389 case SpvCapabilityKernel
:
390 case SpvCapabilityLinkage
:
391 case SpvCapabilityVector16
:
393 // Optional capabilities
394 case SpvCapabilityImageBasic
:
395 case SpvCapabilityLiteralSampler
:
396 case SpvCapabilitySampled1D
:
397 case SpvCapabilityImage1D
:
398 case SpvCapabilitySampledBuffer
:
399 case SpvCapabilityImageBuffer
:
400 if (!dev
.image_support()) {
401 r_log
+= "Capability 'ImageBasic' is not supported.\n";
405 case SpvCapabilityFloat64
:
406 if (!dev
.has_doubles()) {
407 r_log
+= "Capability 'Float64' is not supported.\n";
411 // Enabled through extensions
412 case SpvCapabilityFloat16
:
413 if (!dev
.has_halves()) {
414 r_log
+= "Capability 'Float16' is not supported.\n";
418 case SpvCapabilityInt64Atomics
:
419 if (!dev
.has_int64_atomics()) {
420 r_log
+= "Capability 'Int64Atomics' is not supported.\n";
425 r_log
+= "Capability '" + std::to_string(capability
) +
426 "' is not supported.\n";
437 check_extensions(const device
&dev
, const std::vector
<char> &source
,
438 std::string
&r_log
) {
439 const size_t length
= source
.size() / sizeof(uint32_t);
440 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
441 const auto spirv_extensions
= spirv::supported_extensions();
444 const auto desc_word
= get
<uint32_t>(source
.data(), i
);
445 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
446 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
448 if (opcode
== SpvOpCapability
) {
452 if (opcode
!= SpvOpExtension
)
455 const std::string extension
= source
.data() + (i
+ 1u) * sizeof(uint32_t);
456 if (spirv_extensions
.count(extension
) == 0) {
457 r_log
+= "Extension '" + extension
+ "' is not supported.\n";
468 check_memory_model(const device
&dev
, const std::vector
<char> &source
,
469 std::string
&r_log
) {
470 const size_t length
= source
.size() / sizeof(uint32_t);
471 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
474 const auto desc_word
= get
<uint32_t>(source
.data(), i
);
475 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
476 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
479 case SpvOpMemoryModel
:
480 switch (get
<SpvAddressingModel
>(source
.data(), i
+ 1u)) {
481 case SpvAddressingModelPhysical32
:
482 return dev
.address_bits() == 32;
483 case SpvAddressingModelPhysical64
:
484 return dev
.address_bits() == 64;
486 unreachable("Only Physical32 and Physical64 are valid for OpenCL, and the binary was already validated");
500 // Copies the input binary and convert it to the endianness of the host CPU.
502 spirv_to_cpu(const std::vector
<char> &binary
)
504 const uint32_t first_word
= get
<uint32_t>(binary
.data(), 0u);
505 if (first_word
== SpvMagicNumber
)
508 std::vector
<char> cpu_endianness_binary(binary
.size());
509 for (size_t i
= 0; i
< (binary
.size() / 4u); ++i
) {
510 const uint32_t word
= get
<uint32_t>(binary
.data(), i
);
511 reinterpret_cast<uint32_t *>(cpu_endianness_binary
.data())[i
] =
515 return cpu_endianness_binary
;
518 #ifdef HAVE_CLOVER_SPIRV
520 format_validator_msg(spv_message_level_t level
, const char * /* source */,
521 const spv_position_t
&position
, const char *message
) {
522 std::string level_str
;
527 case SPV_MSG_INTERNAL_ERROR
:
528 level_str
= "Internal error";
533 case SPV_MSG_WARNING
:
534 level_str
= "Warning";
543 return "[" + level_str
+ "] At word No." +
544 std::to_string(position
.index
) + ": \"" + message
+ "\"\n";
548 convert_opencl_str_to_target_env(const std::string
&opencl_version
) {
549 if (opencl_version
== "2.2") {
550 return SPV_ENV_OPENCL_2_2
;
551 } else if (opencl_version
== "2.1") {
552 return SPV_ENV_OPENCL_2_1
;
553 } else if (opencl_version
== "2.0") {
554 return SPV_ENV_OPENCL_2_0
;
555 } else if (opencl_version
== "1.2" ||
556 opencl_version
== "1.1" ||
557 opencl_version
== "1.0") {
558 // SPIR-V is only defined for OpenCL >= 1.2, however some drivers
559 // might use it with OpenCL 1.0 and 1.1.
560 return SPV_ENV_OPENCL_1_2
;
562 throw build_error("Invalid OpenCL version");
570 clover::spirv::compile_program(const std::vector
<char> &binary
,
571 const device
&dev
, std::string
&r_log
) {
572 std::vector
<char> source
= spirv_to_cpu(binary
);
574 if (!is_valid_spirv(source
, dev
.device_version(), r_log
))
577 if (!check_capabilities(dev
, source
, r_log
))
579 if (!check_extensions(dev
, source
, r_log
))
581 if (!check_memory_model(dev
, source
, r_log
))
584 return create_module_from_spirv(source
,
585 dev
.address_bits() == 32 ? 4u : 8u, r_log
);
589 clover::spirv::link_program(const std::vector
<module
> &modules
,
590 const device
&dev
, const std::string
&opts
,
591 std::string
&r_log
) {
592 std::vector
<std::string
> options
= clover::llvm::tokenize(opts
);
594 bool create_library
= false;
596 std::string ignored_options
;
597 for (const std::string
&option
: options
) {
598 if (option
== "-create-library") {
599 create_library
= true;
601 ignored_options
+= "'" + option
+ "' ";
604 if (!ignored_options
.empty()) {
605 r_log
+= "Ignoring the following link options: " + ignored_options
609 spvtools::LinkerOptions linker_options
;
610 linker_options
.SetCreateLibrary(create_library
);
614 const auto section_type
= create_library
? module::section::text_library
:
615 module::section::text_executable
;
617 std::vector
<const uint32_t *> sections
;
618 sections
.reserve(modules
.size());
619 std::vector
<size_t> lengths
;
620 lengths
.reserve(modules
.size());
622 auto const validator_consumer
= [&r_log
](spv_message_level_t level
,
624 const spv_position_t
&position
,
625 const char *message
) {
626 r_log
+= format_validator_msg(level
, source
, position
, message
);
629 for (const auto &mod
: modules
) {
630 const auto &msec
= find([](const module::section
&sec
) {
631 return sec
.type
== module::section::text_intermediate
||
632 sec
.type
== module::section::text_library
;
635 const auto c_il
= ((struct pipe_binary_program_header
*)msec
.data
.data())->blob
;
636 const auto length
= msec
.size
;
638 sections
.push_back(reinterpret_cast<const uint32_t *>(c_il
));
639 lengths
.push_back(length
/ sizeof(uint32_t));
642 std::vector
<uint32_t> linked_binary
;
644 const std::string opencl_version
= dev
.device_version();
645 const spv_target_env target_env
=
646 convert_opencl_str_to_target_env(opencl_version
);
648 const spvtools::MessageConsumer consumer
= validator_consumer
;
649 spvtools::Context
context(target_env
);
650 context
.SetMessageConsumer(std::move(consumer
));
652 if (Link(context
, sections
.data(), lengths
.data(), sections
.size(),
653 &linked_binary
, linker_options
) != SPV_SUCCESS
)
654 throw error(CL_LINK_PROGRAM_FAILURE
);
656 std::vector
<char> final_binary
{
657 reinterpret_cast<char *>(linked_binary
.data()),
658 reinterpret_cast<char *>(linked_binary
.data() +
659 linked_binary
.size()) };
660 if (!is_valid_spirv(final_binary
, opencl_version
, r_log
))
661 throw error(CL_LINK_PROGRAM_FAILURE
);
663 if (has_flag(llvm::debug::spirv
))
664 llvm::debug::log(".spvasm", spirv::print_module(final_binary
, dev
.device_version()));
666 for (const auto &mod
: modules
)
667 m
.syms
.insert(m
.syms
.end(), mod
.syms
.begin(), mod
.syms
.end());
669 m
.secs
.emplace_back(make_text_section(final_binary
, section_type
));
675 clover::spirv::is_valid_spirv(const std::vector
<char> &binary
,
676 const std::string
&opencl_version
,
677 std::string
&r_log
) {
678 auto const validator_consumer
=
679 [&r_log
](spv_message_level_t level
, const char *source
,
680 const spv_position_t
&position
, const char *message
) {
681 r_log
+= format_validator_msg(level
, source
, position
, message
);
684 const spv_target_env target_env
=
685 convert_opencl_str_to_target_env(opencl_version
);
686 spvtools::SpirvTools
spvTool(target_env
);
687 spvTool
.SetMessageConsumer(validator_consumer
);
689 return spvTool
.Validate(reinterpret_cast<const uint32_t *>(binary
.data()),
694 clover::spirv::print_module(const std::vector
<char> &binary
,
695 const std::string
&opencl_version
) {
696 const spv_target_env target_env
=
697 convert_opencl_str_to_target_env(opencl_version
);
698 spvtools::SpirvTools
spvTool(target_env
);
699 spv_context spvContext
= spvContextCreate(target_env
);
701 return "Failed to create an spv_context for disassembling the module.";
703 spv_text disassembly
;
704 spvBinaryToText(spvContext
,
705 reinterpret_cast<const uint32_t *>(binary
.data()),
706 binary
.size() / 4u, SPV_BINARY_TO_TEXT_OPTION_NONE
,
707 &disassembly
, nullptr);
708 spvContextDestroy(spvContext
);
710 const std::string disassemblyStr
= disassembly
->str
;
711 spvTextDestroy(disassembly
);
713 return disassemblyStr
;
716 std::unordered_set
<std::string
>
717 clover::spirv::supported_extensions() {
719 /* this is only a hint so all devices support that */
720 "SPV_KHR_no_integer_wrap_decoration"
724 std::vector
<uint32_t>
725 clover::spirv::supported_versions() {
726 return { make_spirv_version(1u, 0u) };
731 clover::spirv::is_valid_spirv(const std::vector
<char> &/*binary*/,
732 const std::string
&/*opencl_version*/,
733 std::string
&/*r_log*/) {
738 clover::spirv::compile_program(const std::vector
<char> &binary
,
739 const device
&dev
, std::string
&r_log
) {
740 r_log
+= "SPIR-V support in clover is not enabled.\n";
745 clover::spirv::link_program(const std::vector
<module
> &/*modules*/,
746 const device
&/*dev*/, const std::string
&/*opts*/,
747 std::string
&r_log
) {
748 r_log
+= "SPIR-V support in clover is not enabled.\n";
749 throw error(CL_LINKER_NOT_AVAILABLE
);
753 clover::spirv::print_module(const std::vector
<char> &binary
,
754 const std::string
&opencl_version
) {
755 return std::string();
758 std::unordered_set
<std::string
>
759 clover::spirv::supported_extensions() {
763 std::vector
<uint32_t>
764 clover::spirv::supported_versions() {