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"
26 #include <unordered_map>
27 #include <unordered_set>
30 #ifdef HAVE_CLOVER_SPIRV
31 #include <spirv-tools/libspirv.hpp>
32 #include <spirv-tools/linker.hpp>
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"
44 #include "compiler/spirv/spirv.h"
46 #define SPIRV_HEADER_WORD_SIZE 5
48 using namespace clover
;
50 #ifdef HAVE_CLOVER_SPIRV
54 T
get(const char *source
, size_t index
) {
55 const uint32_t *word_ptr
= reinterpret_cast<const uint32_t *>(source
);
56 return static_cast<T
>(word_ptr
[index
]);
59 enum module::argument::type
60 convert_storage_class(SpvStorageClass storage_class
, std::string
&err
) {
61 switch (storage_class
) {
62 case SpvStorageClassFunction
:
63 return module::argument::scalar
;
64 case SpvStorageClassUniformConstant
:
65 return module::argument::global
;
66 case SpvStorageClassWorkgroup
:
67 return module::argument::local
;
68 case SpvStorageClassCrossWorkgroup
:
69 return module::argument::global
;
71 err
+= "Invalid storage type " + std::to_string(storage_class
) + "\n";
76 enum module::argument::type
77 convert_image_type(SpvId id
, SpvDim dim
, SpvAccessQualifier access
,
79 if (dim
== SpvDim2D
&& access
== SpvAccessQualifierReadOnly
)
80 return module::argument::image2d_rd
;
81 else if (dim
== SpvDim2D
&& access
== SpvAccessQualifierWriteOnly
)
82 return module::argument::image2d_wr
;
83 else if (dim
== SpvDim3D
&& access
== SpvAccessQualifierReadOnly
)
84 return module::argument::image3d_rd
;
85 else if (dim
== SpvDim3D
&& access
== SpvAccessQualifierWriteOnly
)
86 return module::argument::image3d_wr
;
88 err
+= "Unknown access qualifier " + std::to_string(access
)
89 + " or dimension " + std::to_string(dim
) + " for image "
90 + std::to_string(id
) + ".\n";
96 make_text_section(const std::vector
<char> &code
,
97 enum module::section::type section_type
) {
98 const pipe_binary_program_header header
{ uint32_t(code
.size()) };
99 module::section text
{ 0, section_type
, header
.num_bytes
, {} };
101 text
.data
.insert(text
.data
.end(), reinterpret_cast<const char *>(&header
),
102 reinterpret_cast<const char *>(&header
) + sizeof(header
));
103 text
.data
.insert(text
.data
.end(), code
.begin(), code
.end());
109 create_module_from_spirv(const std::vector
<char> &source
,
110 size_t pointer_byte_size
,
112 const size_t length
= source
.size() / sizeof(uint32_t);
113 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
115 std::string kernel_name
;
116 size_t kernel_nb
= 0u;
117 std::vector
<module::argument
> args
;
121 std::unordered_map
<SpvId
, std::string
> kernels
;
122 std::unordered_map
<SpvId
, module::argument
> types
;
123 std::unordered_map
<SpvId
, SpvId
> pointer_types
;
124 std::unordered_map
<SpvId
, unsigned int> constants
;
125 std::unordered_set
<SpvId
> packed_structures
;
126 std::unordered_map
<SpvId
, std::vector
<SpvFunctionParameterAttribute
>>
130 const auto inst
= &source
[i
* sizeof(uint32_t)];
131 const auto desc_word
= get
<uint32_t>(inst
, 0);
132 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
133 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
136 case SpvOpEntryPoint
:
137 if (get
<SpvExecutionModel
>(inst
, 1) == SpvExecutionModelKernel
)
138 kernels
.emplace(get
<SpvId
>(inst
, 2),
139 source
.data() + (i
+ 3u) * sizeof(uint32_t));
142 case SpvOpDecorate
: {
143 const auto id
= get
<SpvId
>(inst
, 1);
144 const auto decoration
= get
<SpvDecoration
>(inst
, 2);
145 if (decoration
== SpvDecorationCPacked
)
146 packed_structures
.emplace(id
);
147 else if (decoration
== SpvDecorationFuncParamAttr
) {
148 const auto attribute
=
149 get
<SpvFunctionParameterAttribute
>(inst
, 3u);
150 func_param_attr_map
[id
].push_back(attribute
);
155 case SpvOpGroupDecorate
: {
156 const auto group_id
= get
<SpvId
>(inst
, 1);
157 if (packed_structures
.count(group_id
)) {
158 for (unsigned int i
= 2u; i
< num_operands
; ++i
)
159 packed_structures
.emplace(get
<SpvId
>(inst
, i
));
161 const auto func_param_attr_iter
=
162 func_param_attr_map
.find(group_id
);
163 if (func_param_attr_iter
!= func_param_attr_map
.end()) {
164 for (unsigned int i
= 2u; i
< num_operands
; ++i
)
165 func_param_attr_map
.emplace(get
<SpvId
>(inst
, i
),
166 func_param_attr_iter
->second
);
172 // We only care about constants that represent the size of arrays.
173 // If they are passed as argument, they will never be more than
174 // 4GB-wide, and even if they did, a clover::module::argument size
175 // is represented by an int.
176 constants
[get
<SpvId
>(inst
, 2)] = get
<unsigned int>(inst
, 3u);
179 case SpvOpTypeInt
: // FALLTHROUGH
180 case SpvOpTypeFloat
: {
181 const auto size
= get
<uint32_t>(inst
, 2) / 8u;
182 types
[get
<SpvId
>(inst
, 1)] = { module::argument::scalar
, size
,
184 module::argument::zero_ext
};
188 case SpvOpTypeArray
: {
189 const auto id
= get
<SpvId
>(inst
, 1);
190 const auto type_id
= get
<SpvId
>(inst
, 2);
191 const auto types_iter
= types
.find(type_id
);
192 if (types_iter
== types
.end())
195 const auto constant_id
= get
<SpvId
>(inst
, 3);
196 const auto constants_iter
= constants
.find(constant_id
);
197 if (constants_iter
== constants
.end()) {
198 err
+= "Constant " + std::to_string(constant_id
) +
202 const auto elem_size
= types_iter
->second
.size
;
203 const auto elem_nbs
= constants_iter
->second
;
204 const auto size
= elem_size
* elem_nbs
;
205 types
[id
] = { module::argument::scalar
, size
, size
,
206 types_iter
->second
.target_align
,
207 module::argument::zero_ext
};
211 case SpvOpTypeStruct
: {
212 const auto id
= get
<SpvId
>(inst
, 1);
213 const bool is_packed
= packed_structures
.count(id
);
215 unsigned struct_size
= 0u;
216 unsigned struct_align
= 1u;
217 for (unsigned j
= 2u; j
< num_operands
; ++j
) {
218 const auto type_id
= get
<SpvId
>(inst
, j
);
219 const auto types_iter
= types
.find(type_id
);
221 // If a type was not found, that means it is not one of the
222 // types allowed as kernel arguments. And since the module has
223 // been validated, this means this type is not used for kernel
224 // arguments, and therefore can be ignored.
225 if (types_iter
== types
.end())
228 const auto alignment
= is_packed
? 1u
229 : types_iter
->second
.target_align
;
230 const auto padding
= (-struct_size
) & (alignment
- 1u);
231 struct_size
+= padding
+ types_iter
->second
.target_size
;
232 struct_align
= std::max(struct_align
, alignment
);
234 struct_size
+= (-struct_size
) & (struct_align
- 1u);
235 types
[id
] = { module::argument::scalar
, struct_size
, struct_size
,
236 struct_align
, module::argument::zero_ext
};
240 case SpvOpTypeVector
: {
241 const auto id
= get
<SpvId
>(inst
, 1);
242 const auto type_id
= get
<SpvId
>(inst
, 2);
243 const auto types_iter
= types
.find(type_id
);
245 // If a type was not found, that means it is not one of the
246 // types allowed as kernel arguments. And since the module has
247 // been validated, this means this type is not used for kernel
248 // arguments, and therefore can be ignored.
249 if (types_iter
== types
.end())
252 const auto elem_size
= types_iter
->second
.size
;
253 const auto elem_nbs
= get
<uint32_t>(inst
, 3);
254 const auto size
= elem_size
* elem_nbs
;
255 types
[id
] = { module::argument::scalar
, size
, size
, size
,
256 module::argument::zero_ext
};
260 case SpvOpTypeForwardPointer
: // FALLTHROUGH
261 case SpvOpTypePointer
: {
262 const auto id
= get
<SpvId
>(inst
, 1);
263 const auto storage_class
= get
<SpvStorageClass
>(inst
, 2);
264 // Input means this is for a builtin variable, which can not be
265 // passed as an argument to a kernel.
266 if (storage_class
== SpvStorageClassInput
)
268 types
[id
] = { convert_storage_class(storage_class
, err
),
270 static_cast<module::size_t>(pointer_byte_size
),
271 static_cast<module::size_t>(pointer_byte_size
),
272 module::argument::zero_ext
};
273 if (opcode
== SpvOpTypePointer
)
274 pointer_types
[id
] = get
<SpvId
>(inst
, 3);
278 case SpvOpTypeSampler
:
279 types
[get
<SpvId
>(inst
, 1)] = { module::argument::sampler
,
280 sizeof(cl_sampler
) };
283 case SpvOpTypeImage
: {
284 const auto id
= get
<SpvId
>(inst
, 1);
285 const auto dim
= get
<SpvDim
>(inst
, 3);
286 const auto access
= get
<SpvAccessQualifier
>(inst
, 9);
287 types
[id
] = { convert_image_type(id
, dim
, access
, err
),
288 sizeof(cl_mem
), sizeof(cl_mem
), sizeof(cl_mem
),
289 module::argument::zero_ext
};
293 case SpvOpTypePipe
: // FALLTHROUGH
294 case SpvOpTypeQueue
: {
295 err
+= "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are "
296 "not available in the currently supported OpenCL C version."
301 case SpvOpFunction
: {
302 const auto kernels_iter
= kernels
.find(get
<SpvId
>(inst
, 2));
303 if (kernels_iter
!= kernels
.end())
304 kernel_name
= kernels_iter
->second
;
308 case SpvOpFunctionParameter
: {
309 if (kernel_name
.empty())
312 const auto type_id
= get
<SpvId
>(inst
, 1);
313 auto arg
= types
.find(type_id
)->second
;
314 const auto &func_param_attr_iter
=
315 func_param_attr_map
.find(get
<SpvId
>(inst
, 2));
316 if (func_param_attr_iter
!= func_param_attr_map
.end()) {
317 for (auto &i
: func_param_attr_iter
->second
) {
319 case SpvFunctionParameterAttributeSext
:
320 arg
.ext_type
= module::argument::sign_ext
;
322 case SpvFunctionParameterAttributeZext
:
323 arg
.ext_type
= module::argument::zero_ext
;
325 case SpvFunctionParameterAttributeByVal
: {
326 const SpvId ptr_type_id
=
327 pointer_types
.find(type_id
)->second
;
328 arg
= types
.find(ptr_type_id
)->second
;
336 args
.emplace_back(arg
);
340 case SpvOpFunctionEnd
:
341 if (kernel_name
.empty())
343 m
.syms
.emplace_back(kernel_name
, 0, kernel_nb
, args
);
356 m
.secs
.push_back(make_text_section(source
,
357 module::section::text_intermediate
));
362 check_capabilities(const device
&dev
, const std::vector
<char> &source
,
363 std::string
&r_log
) {
364 const size_t length
= source
.size() / sizeof(uint32_t);
365 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
368 const auto desc_word
= get
<uint32_t>(source
.data(), i
);
369 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
370 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
372 if (opcode
!= SpvOpCapability
)
375 const auto capability
= get
<SpvCapability
>(source
.data(), i
+ 1u);
376 switch (capability
) {
377 // Mandatory capabilities
378 case SpvCapabilityAddresses
:
379 case SpvCapabilityFloat16Buffer
:
380 case SpvCapabilityGroups
:
381 case SpvCapabilityInt64
:
382 case SpvCapabilityInt16
:
383 case SpvCapabilityInt8
:
384 case SpvCapabilityKernel
:
385 case SpvCapabilityLinkage
:
386 case SpvCapabilityVector16
:
388 // Optional capabilities
389 case SpvCapabilityImageBasic
:
390 case SpvCapabilityLiteralSampler
:
391 case SpvCapabilitySampled1D
:
392 case SpvCapabilityImage1D
:
393 case SpvCapabilitySampledBuffer
:
394 case SpvCapabilityImageBuffer
:
395 if (!dev
.image_support()) {
396 r_log
+= "Capability 'ImageBasic' is not supported.\n";
400 case SpvCapabilityFloat64
:
401 if (!dev
.has_doubles()) {
402 r_log
+= "Capability 'Float64' is not supported.\n";
406 // Enabled through extensions
407 case SpvCapabilityFloat16
:
408 if (!dev
.has_halves()) {
409 r_log
+= "Capability 'Float16' is not supported.\n";
413 case SpvCapabilityInt64Atomics
:
414 if (!dev
.has_int64_atomics()) {
415 r_log
+= "Capability 'Int64Atomics' is not supported.\n";
420 r_log
+= "Capability '" + std::to_string(capability
) +
421 "' is not supported.\n";
432 check_extensions(const device
&dev
, const std::vector
<char> &source
,
433 std::string
&r_log
) {
434 const size_t length
= source
.size() / sizeof(uint32_t);
435 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
438 const auto desc_word
= get
<uint32_t>(source
.data(), i
);
439 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
440 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
442 if (opcode
== SpvOpCapability
) {
446 if (opcode
!= SpvOpExtension
)
449 const char *extension
= source
.data() + (i
+ 1u) * sizeof(uint32_t);
450 const std::string device_extensions
= dev
.supported_extensions();
451 const std::string platform_extensions
=
452 dev
.platform
.supported_extensions();
453 if (device_extensions
.find(extension
) == std::string::npos
&&
454 platform_extensions
.find(extension
) == std::string::npos
) {
455 r_log
+= "Extension '" + std::string(extension
) +
456 "' is not supported.\n";
467 check_memory_model(const device
&dev
, const std::vector
<char> &source
,
468 std::string
&r_log
) {
469 const size_t length
= source
.size() / sizeof(uint32_t);
470 size_t i
= SPIRV_HEADER_WORD_SIZE
; // Skip header
473 const auto desc_word
= get
<uint32_t>(source
.data(), i
);
474 const auto opcode
= static_cast<SpvOp
>(desc_word
& SpvOpCodeMask
);
475 const unsigned int num_operands
= desc_word
>> SpvWordCountShift
;
478 case SpvOpMemoryModel
:
479 switch (get
<SpvAddressingModel
>(source
.data(), i
+ 1u)) {
480 case SpvAddressingModelPhysical32
:
481 return dev
.address_bits() == 32;
482 case SpvAddressingModelPhysical64
:
483 return dev
.address_bits() == 64;
485 unreachable("Only Physical32 and Physical64 are valid for OpenCL, and the binary was already validated");
499 // Copies the input binary and convert it to the endianness of the host CPU.
501 spirv_to_cpu(const std::vector
<char> &binary
)
503 const uint32_t first_word
= get
<uint32_t>(binary
.data(), 0u);
504 if (first_word
== SpvMagicNumber
)
507 std::vector
<char> cpu_endianness_binary(binary
.size());
508 for (size_t i
= 0; i
< (binary
.size() / 4u); ++i
) {
509 const uint32_t word
= get
<uint32_t>(binary
.data(), i
);
510 reinterpret_cast<uint32_t *>(cpu_endianness_binary
.data())[i
] =
514 return cpu_endianness_binary
;
517 #ifdef HAVE_CLOVER_SPIRV
519 format_validator_msg(spv_message_level_t level
, const char * /* source */,
520 const spv_position_t
&position
, const char *message
) {
521 std::string level_str
;
526 case SPV_MSG_INTERNAL_ERROR
:
527 level_str
= "Internal error";
532 case SPV_MSG_WARNING
:
533 level_str
= "Warning";
542 return "[" + level_str
+ "] At word No." +
543 std::to_string(position
.index
) + ": \"" + message
+ "\"\n";
547 convert_opencl_str_to_target_env(const std::string
&opencl_version
) {
548 if (opencl_version
== "2.2") {
549 return SPV_ENV_OPENCL_2_2
;
550 } else if (opencl_version
== "2.1") {
551 return SPV_ENV_OPENCL_2_1
;
552 } else if (opencl_version
== "2.0") {
553 return SPV_ENV_OPENCL_2_0
;
554 } else if (opencl_version
== "1.2" ||
555 opencl_version
== "1.1" ||
556 opencl_version
== "1.0") {
557 // SPIR-V is only defined for OpenCL >= 1.2, however some drivers
558 // might use it with OpenCL 1.0 and 1.1.
559 return SPV_ENV_OPENCL_1_2
;
561 throw build_error("Invalid OpenCL version");
569 clover::spirv::compile_program(const std::vector
<char> &binary
,
570 const device
&dev
, std::string
&r_log
) {
571 std::vector
<char> source
= spirv_to_cpu(binary
);
573 if (!is_valid_spirv(source
, dev
.device_version(), r_log
))
576 if (!check_capabilities(dev
, source
, r_log
))
578 if (!check_extensions(dev
, source
, r_log
))
580 if (!check_memory_model(dev
, source
, r_log
))
583 return create_module_from_spirv(source
,
584 dev
.address_bits() == 32 ? 4u : 8u, r_log
);
588 clover::spirv::link_program(const std::vector
<module
> &modules
,
589 const device
&dev
, const std::string
&opts
,
590 std::string
&r_log
) {
591 std::vector
<std::string
> options
= clover::llvm::tokenize(opts
);
593 bool create_library
= false;
595 std::string ignored_options
;
596 for (const std::string
&option
: options
) {
597 if (option
== "-create-library") {
598 create_library
= true;
600 ignored_options
+= "'" + option
+ "' ";
603 if (!ignored_options
.empty()) {
604 r_log
+= "Ignoring the following link options: " + ignored_options
608 spvtools::LinkerOptions linker_options
;
609 linker_options
.SetCreateLibrary(create_library
);
613 const auto section_type
= create_library
? module::section::text_library
:
614 module::section::text_executable
;
616 std::vector
<const uint32_t *> sections
;
617 sections
.reserve(modules
.size());
618 std::vector
<size_t> lengths
;
619 lengths
.reserve(modules
.size());
621 auto const validator_consumer
= [&r_log
](spv_message_level_t level
,
623 const spv_position_t
&position
,
624 const char *message
) {
625 r_log
+= format_validator_msg(level
, source
, position
, message
);
628 for (const auto &mod
: modules
) {
629 const auto &msec
= find([](const module::section
&sec
) {
630 return sec
.type
== module::section::text_intermediate
||
631 sec
.type
== module::section::text_library
;
634 const auto c_il
= ((struct pipe_binary_program_header
*)msec
.data
.data())->blob
;
635 const auto length
= msec
.size
;
637 sections
.push_back(reinterpret_cast<const uint32_t *>(c_il
));
638 lengths
.push_back(length
/ sizeof(uint32_t));
641 std::vector
<uint32_t> linked_binary
;
643 const std::string opencl_version
= dev
.device_version();
644 const spv_target_env target_env
=
645 convert_opencl_str_to_target_env(opencl_version
);
647 const spvtools::MessageConsumer consumer
= validator_consumer
;
648 spvtools::Context
context(target_env
);
649 context
.SetMessageConsumer(std::move(consumer
));
651 if (Link(context
, sections
.data(), lengths
.data(), sections
.size(),
652 &linked_binary
, linker_options
) != SPV_SUCCESS
)
653 throw error(CL_LINK_PROGRAM_FAILURE
);
655 std::vector
<char> final_binary
{
656 reinterpret_cast<char *>(linked_binary
.data()),
657 reinterpret_cast<char *>(linked_binary
.data() +
658 linked_binary
.size()) };
659 if (!is_valid_spirv(final_binary
, opencl_version
, r_log
))
660 throw error(CL_LINK_PROGRAM_FAILURE
);
662 for (const auto &mod
: modules
)
663 m
.syms
.insert(m
.syms
.end(), mod
.syms
.begin(), mod
.syms
.end());
665 m
.secs
.emplace_back(make_text_section(final_binary
, section_type
));
671 clover::spirv::is_valid_spirv(const std::vector
<char> &binary
,
672 const std::string
&opencl_version
,
673 std::string
&r_log
) {
674 auto const validator_consumer
=
675 [&r_log
](spv_message_level_t level
, const char *source
,
676 const spv_position_t
&position
, const char *message
) {
677 r_log
+= format_validator_msg(level
, source
, position
, message
);
680 const spv_target_env target_env
=
681 convert_opencl_str_to_target_env(opencl_version
);
682 spvtools::SpirvTools
spvTool(target_env
);
683 spvTool
.SetMessageConsumer(validator_consumer
);
685 return spvTool
.Validate(reinterpret_cast<const uint32_t *>(binary
.data()),
690 clover::spirv::print_module(const std::vector
<char> &binary
,
691 const std::string
&opencl_version
) {
692 const spv_target_env target_env
=
693 convert_opencl_str_to_target_env(opencl_version
);
694 spvtools::SpirvTools
spvTool(target_env
);
695 spv_context spvContext
= spvContextCreate(target_env
);
697 return "Failed to create an spv_context for disassembling the module.";
699 spv_text disassembly
;
700 spvBinaryToText(spvContext
,
701 reinterpret_cast<const uint32_t *>(binary
.data()),
702 binary
.size() / 4u, SPV_BINARY_TO_TEXT_OPTION_NONE
,
703 &disassembly
, nullptr);
704 spvContextDestroy(spvContext
);
706 const std::string disassemblyStr
= disassembly
->str
;
707 spvTextDestroy(disassembly
);
709 return disassemblyStr
;
714 clover::spirv::is_valid_spirv(const std::vector
<char> &/*binary*/,
715 const std::string
&/*opencl_version*/,
716 std::string
&/*r_log*/) {
721 clover::spirv::compile_program(const std::vector
<char> &binary
,
722 const device
&dev
, std::string
&r_log
) {
723 r_log
+= "SPIR-V support in clover is not enabled.\n";
728 clover::spirv::link_program(const std::vector
<module
> &/*modules*/,
729 const device
&/*dev*/, const std::string
&/*opts*/,
730 std::string
&r_log
) {
731 r_log
+= "SPIR-V support in clover is not enabled.\n";
732 throw error(CL_LINKER_NOT_AVAILABLE
);
736 clover::spirv::print_module(const std::vector
<char> &binary
,
737 const std::string
&opencl_version
) {
738 return std::string();