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