Path: blob/21.2-virgl/src/gallium/frontends/clover/spirv/invocation.cpp
4573 views
//1// Copyright 2018 Pierre Moreau2//3// Permission is hereby granted, free of charge, to any person obtaining a4// copy of this software and associated documentation files (the "Software"),5// to deal in the Software without restriction, including without limitation6// the rights to use, copy, modify, merge, publish, distribute, sublicense,7// and/or sell copies of the Software, and to permit persons to whom the8// Software is furnished to do so, subject to the following conditions:9//10// The above copyright notice and this permission notice shall be included in11// all copies or substantial portions of the Software.12//13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL16// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR17// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,18// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR19// OTHER DEALINGS IN THE SOFTWARE.20//2122#include "invocation.hpp"2324#include <unordered_map>25#include <unordered_set>26#include <vector>2728#ifdef HAVE_CLOVER_SPIRV29#include <spirv-tools/libspirv.hpp>30#include <spirv-tools/linker.hpp>31#endif3233#include "core/error.hpp"34#include "core/platform.hpp"35#include "invocation.hpp"36#include "llvm/util.hpp"37#include "pipe/p_state.h"38#include "util/algorithm.hpp"39#include "util/functional.hpp"40#include "util/u_math.h"4142#include "compiler/spirv/spirv.h"4344#define SPIRV_HEADER_WORD_SIZE 54546using namespace clover;4748#ifdef HAVE_CLOVER_SPIRV49namespace {5051template<typename T>52T get(const char *source, size_t index) {53const uint32_t *word_ptr = reinterpret_cast<const uint32_t *>(source);54return static_cast<T>(word_ptr[index]);55}5657enum module::argument::type58convert_storage_class(SpvStorageClass storage_class, std::string &err) {59switch (storage_class) {60case SpvStorageClassFunction:61return module::argument::scalar;62case SpvStorageClassUniformConstant:63return module::argument::global;64case SpvStorageClassWorkgroup:65return module::argument::local;66case SpvStorageClassCrossWorkgroup:67return module::argument::global;68default:69err += "Invalid storage type " + std::to_string(storage_class) + "\n";70throw build_error();71}72}7374cl_kernel_arg_address_qualifier75convert_storage_class_to_cl(SpvStorageClass storage_class) {76switch (storage_class) {77case SpvStorageClassUniformConstant:78return CL_KERNEL_ARG_ADDRESS_CONSTANT;79case SpvStorageClassWorkgroup:80return CL_KERNEL_ARG_ADDRESS_LOCAL;81case SpvStorageClassCrossWorkgroup:82return CL_KERNEL_ARG_ADDRESS_GLOBAL;83case SpvStorageClassFunction:84default:85return CL_KERNEL_ARG_ADDRESS_PRIVATE;86}87}8889enum module::argument::type90convert_image_type(SpvId id, SpvDim dim, SpvAccessQualifier access,91std::string &err) {92switch (dim) {93case SpvDim1D:94case SpvDim2D:95case SpvDim3D:96case SpvDimBuffer:97switch (access) {98case SpvAccessQualifierReadOnly:99return module::argument::image_rd;100case SpvAccessQualifierWriteOnly:101return module::argument::image_wr;102default:103err += "Unknown access qualifier " + std::to_string(access) + " for image "104+ std::to_string(id) + ".\n";105throw build_error();106}107default:108err += "Unknown dimension " + std::to_string(dim) + " for image "109+ std::to_string(id) + ".\n";110throw build_error();111}112}113114module::section115make_text_section(const std::string &code,116enum module::section::type section_type) {117const pipe_binary_program_header header { uint32_t(code.size()) };118module::section text { 0, section_type, header.num_bytes, {} };119120text.data.insert(text.data.end(), reinterpret_cast<const char *>(&header),121reinterpret_cast<const char *>(&header) + sizeof(header));122text.data.insert(text.data.end(), code.begin(), code.end());123124return text;125}126127module128create_module_from_spirv(const std::string &source,129size_t pointer_byte_size,130std::string &err) {131const size_t length = source.size() / sizeof(uint32_t);132size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header133134std::string kernel_name;135size_t kernel_nb = 0u;136std::vector<module::argument> args;137std::vector<size_t> req_local_size;138139module m;140141std::unordered_map<SpvId, std::vector<size_t> > req_local_sizes;142std::unordered_map<SpvId, std::string> kernels;143std::unordered_map<SpvId, module::argument> types;144std::unordered_map<SpvId, SpvId> pointer_types;145std::unordered_map<SpvId, unsigned int> constants;146std::unordered_set<SpvId> packed_structures;147std::unordered_map<SpvId, std::vector<SpvFunctionParameterAttribute>>148func_param_attr_map;149std::unordered_map<SpvId, std::string> names;150std::unordered_map<SpvId, cl_kernel_arg_type_qualifier> qualifiers;151std::unordered_map<std::string, std::vector<std::string> > param_type_names;152153while (i < length) {154const auto inst = &source[i * sizeof(uint32_t)];155const auto desc_word = get<uint32_t>(inst, 0);156const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);157const unsigned int num_operands = desc_word >> SpvWordCountShift;158159switch (opcode) {160case SpvOpName: {161names.emplace(get<SpvId>(inst, 1),162source.data() + (i + 2u) * sizeof(uint32_t));163break;164}165166case SpvOpString: {167// SPIRV-LLVM-Translator stores param type names as OpStrings168std::string str(source.data() + (i + 2u) * sizeof(uint32_t));169if (str.find("kernel_arg_type.") != 0)170break;171172std::string line;173std::istringstream istream(str.substr(16));174175std::getline(istream, line, '.');176177std::string k = line;178while (std::getline(istream, line, ','))179param_type_names[k].push_back(line);180break;181}182183case SpvOpEntryPoint:184if (get<SpvExecutionModel>(inst, 1) == SpvExecutionModelKernel)185kernels.emplace(get<SpvId>(inst, 2),186source.data() + (i + 3u) * sizeof(uint32_t));187break;188189case SpvOpExecutionMode:190switch (get<SpvExecutionMode>(inst, 2)) {191case SpvExecutionModeLocalSize:192req_local_sizes[get<SpvId>(inst, 1)] = {193get<uint32_t>(inst, 3),194get<uint32_t>(inst, 4),195get<uint32_t>(inst, 5)196};197break;198default:199break;200}201break;202203case SpvOpDecorate: {204const auto id = get<SpvId>(inst, 1);205const auto decoration = get<SpvDecoration>(inst, 2);206switch (decoration) {207case SpvDecorationCPacked:208packed_structures.emplace(id);209break;210case SpvDecorationFuncParamAttr: {211const auto attribute =212get<SpvFunctionParameterAttribute>(inst, 3u);213func_param_attr_map[id].push_back(attribute);214break;215}216case SpvDecorationVolatile:217qualifiers[id] |= CL_KERNEL_ARG_TYPE_VOLATILE;218break;219default:220break;221}222break;223}224225case SpvOpGroupDecorate: {226const auto group_id = get<SpvId>(inst, 1);227if (packed_structures.count(group_id)) {228for (unsigned int i = 2u; i < num_operands; ++i)229packed_structures.emplace(get<SpvId>(inst, i));230}231const auto func_param_attr_iter =232func_param_attr_map.find(group_id);233if (func_param_attr_iter != func_param_attr_map.end()) {234for (unsigned int i = 2u; i < num_operands; ++i) {235auto &attrs = func_param_attr_map[get<SpvId>(inst, i)];236attrs.insert(attrs.begin(),237func_param_attr_iter->second.begin(),238func_param_attr_iter->second.end());239}240}241if (qualifiers.count(group_id)) {242for (unsigned int i = 2u; i < num_operands; ++i)243qualifiers[get<SpvId>(inst, i)] |= qualifiers[group_id];244}245break;246}247248case SpvOpConstant:249// We only care about constants that represent the size of arrays.250// If they are passed as argument, they will never be more than251// 4GB-wide, and even if they did, a clover::module::argument size252// is represented by an int.253constants[get<SpvId>(inst, 2)] = get<unsigned int>(inst, 3u);254break;255256case SpvOpTypeInt:257case SpvOpTypeFloat: {258const auto size = get<uint32_t>(inst, 2) / 8u;259const auto id = get<SpvId>(inst, 1);260types[id] = { module::argument::scalar, size, size, size,261module::argument::zero_ext };262types[id].info.address_qualifier = CL_KERNEL_ARG_ADDRESS_PRIVATE;263break;264}265266case SpvOpTypeArray: {267const auto id = get<SpvId>(inst, 1);268const auto type_id = get<SpvId>(inst, 2);269const auto types_iter = types.find(type_id);270if (types_iter == types.end())271break;272273const auto constant_id = get<SpvId>(inst, 3);274const auto constants_iter = constants.find(constant_id);275if (constants_iter == constants.end()) {276err += "Constant " + std::to_string(constant_id) +277" is missing\n";278throw build_error();279}280const auto elem_size = types_iter->second.size;281const auto elem_nbs = constants_iter->second;282const auto size = elem_size * elem_nbs;283types[id] = { module::argument::scalar, size, size,284types_iter->second.target_align,285module::argument::zero_ext };286break;287}288289case SpvOpTypeStruct: {290const auto id = get<SpvId>(inst, 1);291const bool is_packed = packed_structures.count(id);292293unsigned struct_size = 0u;294unsigned struct_align = 1u;295for (unsigned j = 2u; j < num_operands; ++j) {296const auto type_id = get<SpvId>(inst, j);297const auto types_iter = types.find(type_id);298299// If a type was not found, that means it is not one of the300// types allowed as kernel arguments. And since the module has301// been validated, this means this type is not used for kernel302// arguments, and therefore can be ignored.303if (types_iter == types.end())304break;305306const auto alignment = is_packed ? 1u307: types_iter->second.target_align;308const auto padding = (-struct_size) & (alignment - 1u);309struct_size += padding + types_iter->second.target_size;310struct_align = std::max(struct_align, alignment);311}312struct_size += (-struct_size) & (struct_align - 1u);313types[id] = { module::argument::scalar, struct_size, struct_size,314struct_align, module::argument::zero_ext };315break;316}317318case SpvOpTypeVector: {319const auto id = get<SpvId>(inst, 1);320const auto type_id = get<SpvId>(inst, 2);321const auto types_iter = types.find(type_id);322323// If a type was not found, that means it is not one of the324// types allowed as kernel arguments. And since the module has325// been validated, this means this type is not used for kernel326// arguments, and therefore can be ignored.327if (types_iter == types.end())328break;329330const auto elem_size = types_iter->second.size;331const auto elem_nbs = get<uint32_t>(inst, 3);332const auto size = elem_size * (elem_nbs != 3 ? elem_nbs : 4);333types[id] = { module::argument::scalar, size, size, size,334module::argument::zero_ext };335types[id].info.address_qualifier = CL_KERNEL_ARG_ADDRESS_PRIVATE;336break;337}338339case SpvOpTypeForwardPointer: // FALLTHROUGH340case SpvOpTypePointer: {341const auto id = get<SpvId>(inst, 1);342const auto storage_class = get<SpvStorageClass>(inst, 2);343// Input means this is for a builtin variable, which can not be344// passed as an argument to a kernel.345if (storage_class == SpvStorageClassInput)346break;347348if (opcode == SpvOpTypePointer)349pointer_types[id] = get<SpvId>(inst, 3);350351types[id] = { convert_storage_class(storage_class, err),352sizeof(cl_mem),353static_cast<module::size_t>(pointer_byte_size),354static_cast<module::size_t>(pointer_byte_size),355module::argument::zero_ext };356types[id].info.address_qualifier = convert_storage_class_to_cl(storage_class);357break;358}359360case SpvOpTypeSampler:361types[get<SpvId>(inst, 1)] = { module::argument::sampler,362sizeof(cl_sampler) };363break;364365case SpvOpTypeImage: {366const auto id = get<SpvId>(inst, 1);367const auto dim = get<SpvDim>(inst, 3);368const auto access = get<SpvAccessQualifier>(inst, 9);369types[id] = { convert_image_type(id, dim, access, err),370sizeof(cl_mem), sizeof(cl_mem), sizeof(cl_mem),371module::argument::zero_ext };372break;373}374375case SpvOpTypePipe: // FALLTHROUGH376case SpvOpTypeQueue: {377err += "TypePipe and TypeQueue are valid SPIR-V 1.0 types, but are "378"not available in the currently supported OpenCL C version."379"\n";380throw build_error();381}382383case SpvOpFunction: {384auto id = get<SpvId>(inst, 2);385const auto kernels_iter = kernels.find(id);386if (kernels_iter != kernels.end())387kernel_name = kernels_iter->second;388389const auto req_local_size_iter = req_local_sizes.find(id);390if (req_local_size_iter != req_local_sizes.end())391req_local_size = (*req_local_size_iter).second;392else393req_local_size = { 0, 0, 0 };394395break;396}397398case SpvOpFunctionParameter: {399if (kernel_name.empty())400break;401402const auto id = get<SpvId>(inst, 2);403const auto type_id = get<SpvId>(inst, 1);404auto arg = types.find(type_id)->second;405const auto &func_param_attr_iter =406func_param_attr_map.find(get<SpvId>(inst, 2));407if (func_param_attr_iter != func_param_attr_map.end()) {408for (auto &i : func_param_attr_iter->second) {409switch (i) {410case SpvFunctionParameterAttributeSext:411arg.ext_type = module::argument::sign_ext;412break;413case SpvFunctionParameterAttributeZext:414arg.ext_type = module::argument::zero_ext;415break;416case SpvFunctionParameterAttributeByVal: {417const SpvId ptr_type_id =418pointer_types.find(type_id)->second;419arg = types.find(ptr_type_id)->second;420break;421}422case SpvFunctionParameterAttributeNoAlias:423arg.info.type_qualifier |= CL_KERNEL_ARG_TYPE_RESTRICT;424break;425case SpvFunctionParameterAttributeNoWrite:426arg.info.type_qualifier |= CL_KERNEL_ARG_TYPE_CONST;427break;428default:429break;430}431}432}433434auto name_it = names.find(id);435if (name_it != names.end())436arg.info.arg_name = (*name_it).second;437438arg.info.type_qualifier |= qualifiers[id];439arg.info.address_qualifier = types[type_id].info.address_qualifier;440arg.info.access_qualifier = CL_KERNEL_ARG_ACCESS_NONE;441args.emplace_back(arg);442break;443}444445case SpvOpFunctionEnd:446if (kernel_name.empty())447break;448449for (size_t i = 0; i < param_type_names[kernel_name].size(); i++)450args[i].info.type_name = param_type_names[kernel_name][i];451452m.syms.emplace_back(kernel_name, std::string(),453req_local_size, 0, kernel_nb, args);454++kernel_nb;455kernel_name.clear();456args.clear();457break;458459default:460break;461}462463i += num_operands;464}465466m.secs.push_back(make_text_section(source,467module::section::text_intermediate));468return m;469}470471bool472check_spirv_version(const device &dev, const char *binary,473std::string &r_log) {474const auto spirv_version = get<uint32_t>(binary, 1u);475const auto supported_spirv_versions = clover::spirv::supported_versions();476const auto compare_versions =477[module_version =478clover::spirv::to_opencl_version_encoding(spirv_version)](const cl_name_version &supported){479return supported.version == module_version;480};481482if (std::find_if(supported_spirv_versions.cbegin(),483supported_spirv_versions.cend(),484compare_versions) != supported_spirv_versions.cend())485return true;486487r_log += "SPIR-V version " +488clover::spirv::version_to_string(spirv_version) +489" is not supported; supported versions:";490for (const auto &version : supported_spirv_versions) {491r_log += " " + clover::spirv::version_to_string(version.version);492}493r_log += "\n";494return false;495}496497bool498check_capabilities(const device &dev, const std::string &source,499std::string &r_log) {500const size_t length = source.size() / sizeof(uint32_t);501size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header502503while (i < length) {504const auto desc_word = get<uint32_t>(source.data(), i);505const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);506const unsigned int num_operands = desc_word >> SpvWordCountShift;507508if (opcode != SpvOpCapability)509break;510511const auto capability = get<SpvCapability>(source.data(), i + 1u);512switch (capability) {513// Mandatory capabilities514case SpvCapabilityAddresses:515case SpvCapabilityFloat16Buffer:516case SpvCapabilityGroups:517case SpvCapabilityInt64:518case SpvCapabilityInt16:519case SpvCapabilityInt8:520case SpvCapabilityKernel:521case SpvCapabilityLinkage:522case SpvCapabilityVector16:523break;524// Optional capabilities525case SpvCapabilityImageBasic:526case SpvCapabilityLiteralSampler:527case SpvCapabilitySampled1D:528case SpvCapabilityImage1D:529case SpvCapabilitySampledBuffer:530case SpvCapabilityImageBuffer:531if (!dev.image_support()) {532r_log += "Capability 'ImageBasic' is not supported.\n";533return false;534}535break;536case SpvCapabilityFloat64:537if (!dev.has_doubles()) {538r_log += "Capability 'Float64' is not supported.\n";539return false;540}541break;542// Enabled through extensions543case SpvCapabilityFloat16:544if (!dev.has_halves()) {545r_log += "Capability 'Float16' is not supported.\n";546return false;547}548break;549case SpvCapabilityInt64Atomics:550if (!dev.has_int64_atomics()) {551r_log += "Capability 'Int64Atomics' is not supported.\n";552return false;553}554break;555default:556r_log += "Capability '" + std::to_string(capability) +557"' is not supported.\n";558return false;559}560561i += num_operands;562}563564return true;565}566567bool568check_extensions(const device &dev, const std::string &source,569std::string &r_log) {570const size_t length = source.size() / sizeof(uint32_t);571size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header572const auto spirv_extensions = spirv::supported_extensions();573574while (i < length) {575const auto desc_word = get<uint32_t>(source.data(), i);576const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);577const unsigned int num_operands = desc_word >> SpvWordCountShift;578579if (opcode == SpvOpCapability) {580i += num_operands;581continue;582}583if (opcode != SpvOpExtension)584break;585586const std::string extension = source.data() + (i + 1u) * sizeof(uint32_t);587if (spirv_extensions.count(extension) == 0) {588r_log += "Extension '" + extension + "' is not supported.\n";589return false;590}591592i += num_operands;593}594595return true;596}597598bool599check_memory_model(const device &dev, const std::string &source,600std::string &r_log) {601const size_t length = source.size() / sizeof(uint32_t);602size_t i = SPIRV_HEADER_WORD_SIZE; // Skip header603604while (i < length) {605const auto desc_word = get<uint32_t>(source.data(), i);606const auto opcode = static_cast<SpvOp>(desc_word & SpvOpCodeMask);607const unsigned int num_operands = desc_word >> SpvWordCountShift;608609switch (opcode) {610case SpvOpMemoryModel:611switch (get<SpvAddressingModel>(source.data(), i + 1u)) {612case SpvAddressingModelPhysical32:613return dev.address_bits() == 32;614case SpvAddressingModelPhysical64:615return dev.address_bits() == 64;616default:617unreachable("Only Physical32 and Physical64 are valid for OpenCL, and the binary was already validated");618return false;619}620break;621default:622break;623}624625i += num_operands;626}627628return false;629}630631// Copies the input binary and convert it to the endianness of the host CPU.632std::string633spirv_to_cpu(const std::string &binary)634{635const uint32_t first_word = get<uint32_t>(binary.data(), 0u);636if (first_word == SpvMagicNumber)637return binary;638639std::vector<char> cpu_endianness_binary(binary.size());640for (size_t i = 0; i < (binary.size() / 4u); ++i) {641const uint32_t word = get<uint32_t>(binary.data(), i);642reinterpret_cast<uint32_t *>(cpu_endianness_binary.data())[i] =643util_bswap32(word);644}645646return std::string(cpu_endianness_binary.begin(),647cpu_endianness_binary.end());648}649650#ifdef HAVE_CLOVER_SPIRV651std::string652format_validator_msg(spv_message_level_t level, const char * /* source */,653const spv_position_t &position, const char *message) {654std::string level_str;655switch (level) {656case SPV_MSG_FATAL:657level_str = "Fatal";658break;659case SPV_MSG_INTERNAL_ERROR:660level_str = "Internal error";661break;662case SPV_MSG_ERROR:663level_str = "Error";664break;665case SPV_MSG_WARNING:666level_str = "Warning";667break;668case SPV_MSG_INFO:669level_str = "Info";670break;671case SPV_MSG_DEBUG:672level_str = "Debug";673break;674}675return "[" + level_str + "] At word No." +676std::to_string(position.index) + ": \"" + message + "\"\n";677}678679spv_target_env680convert_opencl_version_to_target_env(const cl_version opencl_version) {681// Pick 1.2 for 3.0 for now682if (opencl_version == CL_MAKE_VERSION(3, 0, 0)) {683return SPV_ENV_OPENCL_1_2;684} else if (opencl_version == CL_MAKE_VERSION(2, 2, 0)) {685return SPV_ENV_OPENCL_2_2;686} else if (opencl_version == CL_MAKE_VERSION(2, 1, 0)) {687return SPV_ENV_OPENCL_2_1;688} else if (opencl_version == CL_MAKE_VERSION(2, 0, 0)) {689return SPV_ENV_OPENCL_2_0;690} else if (opencl_version == CL_MAKE_VERSION(1, 2, 0) ||691opencl_version == CL_MAKE_VERSION(1, 1, 0) ||692opencl_version == CL_MAKE_VERSION(1, 0, 0)) {693// SPIR-V is only defined for OpenCL >= 1.2, however some drivers694// might use it with OpenCL 1.0 and 1.1.695return SPV_ENV_OPENCL_1_2;696} else {697throw build_error("Invalid OpenCL version");698}699}700#endif701702}703704bool705clover::spirv::is_binary_spirv(const std::string &binary)706{707// A SPIR-V binary is at the very least 5 32-bit words, which represent the708// SPIR-V header.709if (binary.size() < 20u)710return false;711712const uint32_t first_word =713reinterpret_cast<const uint32_t *>(binary.data())[0u];714return (first_word == SpvMagicNumber) ||715(util_bswap32(first_word) == SpvMagicNumber);716}717718std::string719clover::spirv::version_to_string(uint32_t version) {720const uint32_t major_version = (version >> 16) & 0xff;721const uint32_t minor_version = (version >> 8) & 0xff;722return std::to_string(major_version) + '.' +723std::to_string(minor_version);724}725726module727clover::spirv::compile_program(const std::string &binary,728const device &dev, std::string &r_log,729bool validate) {730std::string source = spirv_to_cpu(binary);731732if (validate && !is_valid_spirv(source, dev.device_version(), r_log))733throw build_error();734735if (!check_spirv_version(dev, source.data(), r_log))736throw build_error();737if (!check_capabilities(dev, source, r_log))738throw build_error();739if (!check_extensions(dev, source, r_log))740throw build_error();741if (!check_memory_model(dev, source, r_log))742throw build_error();743744return create_module_from_spirv(source,745dev.address_bits() == 32 ? 4u : 8u, r_log);746}747748module749clover::spirv::link_program(const std::vector<module> &modules,750const device &dev, const std::string &opts,751std::string &r_log) {752std::vector<std::string> options = tokenize(opts);753754bool create_library = false;755756std::string ignored_options;757for (const std::string &option : options) {758if (option == "-create-library") {759create_library = true;760} else {761ignored_options += "'" + option + "' ";762}763}764if (!ignored_options.empty()) {765r_log += "Ignoring the following link options: " + ignored_options766+ "\n";767}768769spvtools::LinkerOptions linker_options;770linker_options.SetCreateLibrary(create_library);771772module m;773774const auto section_type = create_library ? module::section::text_library :775module::section::text_executable;776777std::vector<const uint32_t *> sections;778sections.reserve(modules.size());779std::vector<size_t> lengths;780lengths.reserve(modules.size());781782auto const validator_consumer = [&r_log](spv_message_level_t level,783const char *source,784const spv_position_t &position,785const char *message) {786r_log += format_validator_msg(level, source, position, message);787};788789for (const auto &mod : modules) {790const auto &msec = find([](const module::section &sec) {791return sec.type == module::section::text_intermediate ||792sec.type == module::section::text_library;793}, mod.secs);794795const auto c_il = ((struct pipe_binary_program_header*)msec.data.data())->blob;796const auto length = msec.size;797798if (!check_spirv_version(dev, c_il, r_log))799throw error(CL_LINK_PROGRAM_FAILURE);800801sections.push_back(reinterpret_cast<const uint32_t *>(c_il));802lengths.push_back(length / sizeof(uint32_t));803}804805std::vector<uint32_t> linked_binary;806807const cl_version opencl_version = dev.device_version();808const spv_target_env target_env =809convert_opencl_version_to_target_env(opencl_version);810811const spvtools::MessageConsumer consumer = validator_consumer;812spvtools::Context context(target_env);813context.SetMessageConsumer(std::move(consumer));814815if (Link(context, sections.data(), lengths.data(), sections.size(),816&linked_binary, linker_options) != SPV_SUCCESS)817throw error(CL_LINK_PROGRAM_FAILURE);818819std::string final_binary{820reinterpret_cast<char *>(linked_binary.data()),821reinterpret_cast<char *>(linked_binary.data() +822linked_binary.size()) };823if (!is_valid_spirv(final_binary, opencl_version, r_log))824throw error(CL_LINK_PROGRAM_FAILURE);825826if (has_flag(llvm::debug::spirv))827llvm::debug::log(".spvasm", spirv::print_module(final_binary, dev.device_version()));828829for (const auto &mod : modules)830m.syms.insert(m.syms.end(), mod.syms.begin(), mod.syms.end());831832m.secs.emplace_back(make_text_section(final_binary, section_type));833834return m;835}836837bool838clover::spirv::is_valid_spirv(const std::string &binary,839const cl_version opencl_version,840std::string &r_log) {841auto const validator_consumer =842[&r_log](spv_message_level_t level, const char *source,843const spv_position_t &position, const char *message) {844r_log += format_validator_msg(level, source, position, message);845};846847const spv_target_env target_env =848convert_opencl_version_to_target_env(opencl_version);849spvtools::SpirvTools spvTool(target_env);850spvTool.SetMessageConsumer(validator_consumer);851852return spvTool.Validate(reinterpret_cast<const uint32_t *>(binary.data()),853binary.size() / 4u);854}855856std::string857clover::spirv::print_module(const std::string &binary,858const cl_version opencl_version) {859const spv_target_env target_env =860convert_opencl_version_to_target_env(opencl_version);861spvtools::SpirvTools spvTool(target_env);862spv_context spvContext = spvContextCreate(target_env);863if (!spvContext)864return "Failed to create an spv_context for disassembling the module.";865866spv_text disassembly;867spvBinaryToText(spvContext,868reinterpret_cast<const uint32_t *>(binary.data()),869binary.size() / 4u, SPV_BINARY_TO_TEXT_OPTION_NONE,870&disassembly, nullptr);871spvContextDestroy(spvContext);872873const std::string disassemblyStr = disassembly->str;874spvTextDestroy(disassembly);875876return disassemblyStr;877}878879std::unordered_set<std::string>880clover::spirv::supported_extensions() {881return {882/* this is only a hint so all devices support that */883"SPV_KHR_no_integer_wrap_decoration"884};885}886887std::vector<cl_name_version>888clover::spirv::supported_versions() {889return { cl_name_version { CL_MAKE_VERSION(1u, 0u, 0u), "SPIR-V" } };890}891892cl_version893clover::spirv::to_opencl_version_encoding(uint32_t version) {894return CL_MAKE_VERSION((version >> 16u) & 0xff,895(version >> 8u) & 0xff, 0u);896}897898uint32_t899clover::spirv::to_spirv_version_encoding(cl_version version) {900return ((CL_VERSION_MAJOR(version) & 0xff) << 16u) |901((CL_VERSION_MINOR(version) & 0xff) << 8u);902}903904#else905bool906clover::spirv::is_binary_spirv(const std::string &binary)907{908return false;909}910911bool912clover::spirv::is_valid_spirv(const std::string &/*binary*/,913const cl_version opencl_version,914std::string &/*r_log*/) {915return false;916}917918std::string919clover::spirv::version_to_string(uint32_t version) {920return "";921}922923module924clover::spirv::compile_program(const std::string &binary,925const device &dev, std::string &r_log,926bool validate) {927r_log += "SPIR-V support in clover is not enabled.\n";928throw build_error();929}930931module932clover::spirv::link_program(const std::vector<module> &/*modules*/,933const device &/*dev*/, const std::string &/*opts*/,934std::string &r_log) {935r_log += "SPIR-V support in clover is not enabled.\n";936throw error(CL_LINKER_NOT_AVAILABLE);937}938939std::string940clover::spirv::print_module(const std::string &binary,941const cl_version opencl_version) {942return std::string();943}944945std::unordered_set<std::string>946clover::spirv::supported_extensions() {947return {};948}949950std::vector<cl_name_version>951clover::spirv::supported_versions() {952return {};953}954955cl_version956clover::spirv::to_opencl_version_encoding(uint32_t version) {957return CL_MAKE_VERSION(0u, 0u, 0u);958}959960uint32_t961clover::spirv::to_spirv_version_encoding(cl_version version) {962return 0u;963}964#endif965966967