Path: blob/21.2-virgl/src/microsoft/clc/clc_helpers.cpp
4560 views
//1// Copyright 2012-2016 Francisco Jerez2// Copyright 2012-2016 Advanced Micro Devices, Inc.3// Copyright 2014-2016 Jan Vesely4// Copyright 2014-2015 Serge Martin5// Copyright 2015 Zoltan Gilian6//7// Permission is hereby granted, free of charge, to any person obtaining a8// copy of this software and associated documentation files (the "Software"),9// to deal in the Software without restriction, including without limitation10// the rights to use, copy, modify, merge, publish, distribute, sublicense,11// and/or sell copies of the Software, and to permit persons to whom the12// Software is furnished to do so, subject to the following conditions:13//14// The above copyright notice and this permission notice shall be included in15// all copies or substantial portions of the Software.16//17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR18// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,19// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL20// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR21// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,22// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR23// OTHER DEALINGS IN THE SOFTWARE.2425#include <sstream>2627#include <llvm/ADT/ArrayRef.h>28#include <llvm/IR/DiagnosticPrinter.h>29#include <llvm/IR/DiagnosticInfo.h>30#include <llvm/IR/LLVMContext.h>31#include <llvm/IR/Type.h>32#include <llvm/Support/raw_ostream.h>33#include <llvm-c/Core.h>34#include <llvm-c/Target.h>35#include <LLVMSPIRVLib/LLVMSPIRVLib.h>3637#include <clang/CodeGen/CodeGenAction.h>38#include <clang/Lex/PreprocessorOptions.h>39#include <clang/Frontend/CompilerInstance.h>40#include <clang/Frontend/TextDiagnosticBuffer.h>41#include <clang/Frontend/TextDiagnosticPrinter.h>42#include <clang/Basic/TargetInfo.h>4344#include <spirv-tools/libspirv.hpp>45#include <spirv-tools/linker.hpp>4647#include "util/macros.h"48#include "glsl_types.h"49#include "nir.h"50#include "nir_types.h"5152#include "clc_helpers.h"53#include "spirv.h"5455#include "opencl-c.h.h"56#include "opencl-c-base.h.h"5758using ::llvm::Function;59using ::llvm::LLVMContext;60using ::llvm::Module;61using ::llvm::raw_string_ostream;6263static void64llvm_log_handler(const ::llvm::DiagnosticInfo &di, void *data) {65raw_string_ostream os { *reinterpret_cast<std::string *>(data) };66::llvm::DiagnosticPrinterRawOStream printer { os };67di.print(printer);68}6970class SPIRVKernelArg {71public:72SPIRVKernelArg(uint32_t id, uint32_t typeId) : id(id), typeId(typeId),73addrQualifier(CLC_KERNEL_ARG_ADDRESS_PRIVATE),74accessQualifier(0),75typeQualifier(0) { }76~SPIRVKernelArg() { }7778uint32_t id;79uint32_t typeId;80std::string name;81std::string typeName;82enum clc_kernel_arg_address_qualifier addrQualifier;83unsigned accessQualifier;84unsigned typeQualifier;85};8687class SPIRVKernelInfo {88public:89SPIRVKernelInfo(uint32_t fid, const char *nm) : funcId(fid), name(nm), vecHint(0) { }90~SPIRVKernelInfo() { }9192uint32_t funcId;93std::string name;94std::vector<SPIRVKernelArg> args;95unsigned vecHint;96};9798class SPIRVKernelParser {99public:100SPIRVKernelParser() : curKernel(NULL)101{102ctx = spvContextCreate(SPV_ENV_UNIVERSAL_1_0);103}104105~SPIRVKernelParser()106{107spvContextDestroy(ctx);108}109110void parseEntryPoint(const spv_parsed_instruction_t *ins)111{112assert(ins->num_operands >= 3);113114const spv_parsed_operand_t *op = &ins->operands[1];115116assert(op->type == SPV_OPERAND_TYPE_ID);117118uint32_t funcId = ins->words[op->offset];119120for (auto &iter : kernels) {121if (funcId == iter.funcId)122return;123}124125op = &ins->operands[2];126assert(op->type == SPV_OPERAND_TYPE_LITERAL_STRING);127const char *name = reinterpret_cast<const char *>(ins->words + op->offset);128129kernels.push_back(SPIRVKernelInfo(funcId, name));130}131132void parseFunction(const spv_parsed_instruction_t *ins)133{134assert(ins->num_operands == 4);135136const spv_parsed_operand_t *op = &ins->operands[1];137138assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);139140uint32_t funcId = ins->words[op->offset];141142SPIRVKernelInfo *kernel = NULL;143144for (auto &kernel : kernels) {145if (funcId == kernel.funcId && !kernel.args.size()) {146curKernel = &kernel;147return;148}149}150}151152void parseFunctionParam(const spv_parsed_instruction_t *ins)153{154const spv_parsed_operand_t *op;155uint32_t id, typeId;156157if (!curKernel)158return;159160assert(ins->num_operands == 2);161op = &ins->operands[0];162assert(op->type == SPV_OPERAND_TYPE_TYPE_ID);163typeId = ins->words[op->offset];164op = &ins->operands[1];165assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);166id = ins->words[op->offset];167curKernel->args.push_back(SPIRVKernelArg(id, typeId));168}169170void parseName(const spv_parsed_instruction_t *ins)171{172const spv_parsed_operand_t *op;173const char *name;174uint32_t id;175176assert(ins->num_operands == 2);177178op = &ins->operands[0];179assert(op->type == SPV_OPERAND_TYPE_ID);180id = ins->words[op->offset];181op = &ins->operands[1];182assert(op->type == SPV_OPERAND_TYPE_LITERAL_STRING);183name = reinterpret_cast<const char *>(ins->words + op->offset);184185for (auto &kernel : kernels) {186for (auto &arg : kernel.args) {187if (arg.id == id && arg.name.empty()) {188arg.name = name;189break;190}191}192}193}194195void parseTypePointer(const spv_parsed_instruction_t *ins)196{197enum clc_kernel_arg_address_qualifier addrQualifier;198uint32_t typeId, storageClass;199const spv_parsed_operand_t *op;200201assert(ins->num_operands == 3);202203op = &ins->operands[0];204assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);205typeId = ins->words[op->offset];206207op = &ins->operands[1];208assert(op->type == SPV_OPERAND_TYPE_STORAGE_CLASS);209storageClass = ins->words[op->offset];210switch (storageClass) {211case SpvStorageClassCrossWorkgroup:212addrQualifier = CLC_KERNEL_ARG_ADDRESS_GLOBAL;213break;214case SpvStorageClassWorkgroup:215addrQualifier = CLC_KERNEL_ARG_ADDRESS_LOCAL;216break;217case SpvStorageClassUniformConstant:218addrQualifier = CLC_KERNEL_ARG_ADDRESS_CONSTANT;219break;220default:221addrQualifier = CLC_KERNEL_ARG_ADDRESS_PRIVATE;222break;223}224225for (auto &kernel : kernels) {226for (auto &arg : kernel.args) {227if (arg.typeId == typeId)228arg.addrQualifier = addrQualifier;229}230}231}232233void parseOpString(const spv_parsed_instruction_t *ins)234{235const spv_parsed_operand_t *op;236std::string str;237238assert(ins->num_operands == 2);239240op = &ins->operands[1];241assert(op->type == SPV_OPERAND_TYPE_LITERAL_STRING);242str = reinterpret_cast<const char *>(ins->words + op->offset);243244if (str.find("kernel_arg_type.") != 0)245return;246247size_t start = sizeof("kernel_arg_type.") - 1;248249for (auto &kernel : kernels) {250size_t pos;251252pos = str.find(kernel.name, start);253if (pos == std::string::npos ||254pos != start || str[start + kernel.name.size()] != '.')255continue;256257pos = start + kernel.name.size();258if (str[pos++] != '.')259continue;260261for (auto &arg : kernel.args) {262if (arg.name.empty())263break;264265size_t typeEnd = str.find(',', pos);266if (typeEnd == std::string::npos)267break;268269arg.typeName = str.substr(pos, typeEnd - pos);270pos = typeEnd + 1;271}272}273}274275void applyDecoration(uint32_t id, const spv_parsed_instruction_t *ins)276{277auto iter = decorationGroups.find(id);278if (iter != decorationGroups.end()) {279for (uint32_t entry : iter->second)280applyDecoration(entry, ins);281return;282}283284const spv_parsed_operand_t *op;285uint32_t decoration;286287assert(ins->num_operands >= 2);288289op = &ins->operands[1];290assert(op->type == SPV_OPERAND_TYPE_DECORATION);291decoration = ins->words[op->offset];292293for (auto &kernel : kernels) {294for (auto &arg : kernel.args) {295if (arg.id == id) {296switch (decoration) {297case SpvDecorationVolatile:298arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_VOLATILE;299break;300case SpvDecorationConstant:301arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_CONST;302break;303case SpvDecorationRestrict:304arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_RESTRICT;305break;306case SpvDecorationFuncParamAttr:307op = &ins->operands[2];308assert(op->type == SPV_OPERAND_TYPE_FUNCTION_PARAMETER_ATTRIBUTE);309switch (ins->words[op->offset]) {310case SpvFunctionParameterAttributeNoAlias:311arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_RESTRICT;312break;313case SpvFunctionParameterAttributeNoWrite:314arg.typeQualifier |= CLC_KERNEL_ARG_TYPE_CONST;315break;316}317break;318}319}320321}322}323}324325void parseOpDecorate(const spv_parsed_instruction_t *ins)326{327const spv_parsed_operand_t *op;328uint32_t id;329330assert(ins->num_operands >= 2);331332op = &ins->operands[0];333assert(op->type == SPV_OPERAND_TYPE_ID);334id = ins->words[op->offset];335336applyDecoration(id, ins);337}338339void parseOpGroupDecorate(const spv_parsed_instruction_t *ins)340{341assert(ins->num_operands >= 2);342343const spv_parsed_operand_t *op = &ins->operands[0];344assert(op->type == SPV_OPERAND_TYPE_ID);345uint32_t groupId = ins->words[op->offset];346347auto lowerBound = decorationGroups.lower_bound(groupId);348if (lowerBound != decorationGroups.end() &&349lowerBound->first == groupId)350// Group already filled out351return;352353auto iter = decorationGroups.emplace_hint(lowerBound, groupId, std::vector<uint32_t>{});354auto& vec = iter->second;355vec.reserve(ins->num_operands - 1);356for (uint32_t i = 1; i < ins->num_operands; ++i) {357op = &ins->operands[i];358assert(op->type == SPV_OPERAND_TYPE_ID);359vec.push_back(ins->words[op->offset]);360}361}362363void parseOpTypeImage(const spv_parsed_instruction_t *ins)364{365const spv_parsed_operand_t *op;366uint32_t typeId;367unsigned accessQualifier = CLC_KERNEL_ARG_ACCESS_READ;368369op = &ins->operands[0];370assert(op->type == SPV_OPERAND_TYPE_RESULT_ID);371typeId = ins->words[op->offset];372373if (ins->num_operands >= 9) {374op = &ins->operands[8];375assert(op->type == SPV_OPERAND_TYPE_ACCESS_QUALIFIER);376switch (ins->words[op->offset]) {377case SpvAccessQualifierReadOnly:378accessQualifier = CLC_KERNEL_ARG_ACCESS_READ;379break;380case SpvAccessQualifierWriteOnly:381accessQualifier = CLC_KERNEL_ARG_ACCESS_WRITE;382break;383case SpvAccessQualifierReadWrite:384accessQualifier = CLC_KERNEL_ARG_ACCESS_WRITE |385CLC_KERNEL_ARG_ACCESS_READ;386break;387}388}389390for (auto &kernel : kernels) {391for (auto &arg : kernel.args) {392if (arg.typeId == typeId) {393arg.accessQualifier = accessQualifier;394arg.addrQualifier = CLC_KERNEL_ARG_ADDRESS_GLOBAL;395}396}397}398}399400void parseExecutionMode(const spv_parsed_instruction_t *ins)401{402uint32_t executionMode = ins->words[ins->operands[1].offset];403if (executionMode != SpvExecutionModeVecTypeHint)404return;405406uint32_t funcId = ins->words[ins->operands[0].offset];407uint32_t vecHint = ins->words[ins->operands[2].offset];408for (auto& kernel : kernels) {409if (kernel.funcId == funcId)410kernel.vecHint = vecHint;411}412}413414static spv_result_t415parseInstruction(void *data, const spv_parsed_instruction_t *ins)416{417SPIRVKernelParser *parser = reinterpret_cast<SPIRVKernelParser *>(data);418419switch (ins->opcode) {420case SpvOpName:421parser->parseName(ins);422break;423case SpvOpEntryPoint:424parser->parseEntryPoint(ins);425break;426case SpvOpFunction:427parser->parseFunction(ins);428break;429case SpvOpFunctionParameter:430parser->parseFunctionParam(ins);431break;432case SpvOpFunctionEnd:433case SpvOpLabel:434parser->curKernel = NULL;435break;436case SpvOpTypePointer:437parser->parseTypePointer(ins);438break;439case SpvOpTypeImage:440parser->parseOpTypeImage(ins);441break;442case SpvOpString:443parser->parseOpString(ins);444break;445case SpvOpDecorate:446parser->parseOpDecorate(ins);447break;448case SpvOpGroupDecorate:449parser->parseOpGroupDecorate(ins);450break;451case SpvOpExecutionMode:452parser->parseExecutionMode(ins);453break;454default:455break;456}457458return SPV_SUCCESS;459}460461bool parsingComplete()462{463for (auto &kernel : kernels) {464if (kernel.name.empty())465return false;466467for (auto &arg : kernel.args) {468if (arg.name.empty() || arg.typeName.empty())469return false;470}471}472473return true;474}475476void parseBinary(const struct spirv_binary &spvbin)477{478/* 3 passes should be enough to retrieve all kernel information:479* 1st pass: all entry point name and number of args480* 2nd pass: argument names and type names481* 3rd pass: pointer type names482*/483for (unsigned pass = 0; pass < 3; pass++) {484spvBinaryParse(ctx, reinterpret_cast<void *>(this),485spvbin.data, spvbin.size / 4,486NULL, parseInstruction, NULL);487488if (parsingComplete())489return;490}491492assert(0);493}494495std::vector<SPIRVKernelInfo> kernels;496std::map<uint32_t, std::vector<uint32_t>> decorationGroups;497SPIRVKernelInfo *curKernel;498spv_context ctx;499};500501const struct clc_kernel_info *502clc_spirv_get_kernels_info(const struct spirv_binary *spvbin,503unsigned *num_kernels)504{505struct clc_kernel_info *kernels;506507SPIRVKernelParser parser;508509parser.parseBinary(*spvbin);510*num_kernels = parser.kernels.size();511if (!*num_kernels)512return NULL;513514kernels = reinterpret_cast<struct clc_kernel_info *>(calloc(*num_kernels,515sizeof(*kernels)));516assert(kernels);517for (unsigned i = 0; i < parser.kernels.size(); i++) {518kernels[i].name = strdup(parser.kernels[i].name.c_str());519kernels[i].num_args = parser.kernels[i].args.size();520kernels[i].vec_hint_size = parser.kernels[i].vecHint >> 16;521kernels[i].vec_hint_type = (enum clc_vec_hint_type)(parser.kernels[i].vecHint & 0xFFFF);522if (!kernels[i].num_args)523continue;524525struct clc_kernel_arg *args;526527args = reinterpret_cast<struct clc_kernel_arg *>(calloc(kernels[i].num_args,528sizeof(*kernels->args)));529kernels[i].args = args;530assert(args);531for (unsigned j = 0; j < kernels[i].num_args; j++) {532if (!parser.kernels[i].args[j].name.empty())533args[j].name = strdup(parser.kernels[i].args[j].name.c_str());534args[j].type_name = strdup(parser.kernels[i].args[j].typeName.c_str());535args[j].address_qualifier = parser.kernels[i].args[j].addrQualifier;536args[j].type_qualifier = parser.kernels[i].args[j].typeQualifier;537args[j].access_qualifier = parser.kernels[i].args[j].accessQualifier;538}539}540541return kernels;542}543544void545clc_free_kernels_info(const struct clc_kernel_info *kernels,546unsigned num_kernels)547{548if (!kernels)549return;550551for (unsigned i = 0; i < num_kernels; i++) {552if (kernels[i].args) {553for (unsigned j = 0; j < kernels[i].num_args; j++) {554free((void *)kernels[i].args[j].name);555free((void *)kernels[i].args[j].type_name);556}557}558free((void *)kernels[i].name);559}560561free((void *)kernels);562}563564int565clc_to_spirv(const struct clc_compile_args *args,566struct spirv_binary *spvbin,567const struct clc_logger *logger)568{569LLVMInitializeAllTargets();570LLVMInitializeAllTargetInfos();571LLVMInitializeAllTargetMCs();572LLVMInitializeAllAsmPrinters();573574std::string log;575std::unique_ptr<LLVMContext> llvm_ctx { new LLVMContext };576llvm_ctx->setDiagnosticHandlerCallBack(llvm_log_handler, &log);577578std::unique_ptr<clang::CompilerInstance> c { new clang::CompilerInstance };579clang::DiagnosticsEngine diag { new clang::DiagnosticIDs,580new clang::DiagnosticOptions,581new clang::TextDiagnosticPrinter(*new raw_string_ostream(log),582&c->getDiagnosticOpts(), true)};583584std::vector<const char *> clang_opts = {585args->source.name,586"-triple", "spir64-unknown-unknown",587// By default, clang prefers to use modules to pull in the default headers,588// which doesn't work with our technique of embedding the headers in our binary589"-finclude-default-header",590// Add a default CL compiler version. Clang will pick the last one specified591// on the command line, so the app can override this one.592"-cl-std=cl1.2",593// The LLVM-SPIRV-Translator doesn't support memset with variable size594"-fno-builtin-memset",595// LLVM's optimizations can produce code that the translator can't translate596"-O0",597// Ensure inline functions are actually emitted598"-fgnu89-inline"599};600// We assume there's appropriate defines for __OPENCL_VERSION__ and __IMAGE_SUPPORT__601// being provided by the caller here.602clang_opts.insert(clang_opts.end(), args->args, args->args + args->num_args);603604if (!clang::CompilerInvocation::CreateFromArgs(c->getInvocation(),605#if LLVM_VERSION_MAJOR >= 10606clang_opts,607#else608clang_opts.data(),609clang_opts.data() + clang_opts.size(),610#endif611diag)) {612log += "Couldn't create Clang invocation.\n";613clc_error(logger, log.c_str());614return -1;615}616617if (diag.hasErrorOccurred()) {618log += "Errors occurred during Clang invocation.\n";619clc_error(logger, log.c_str());620return -1;621}622623// This is a workaround for a Clang bug which causes the number624// of warnings and errors to be printed to stderr.625// http://www.llvm.org/bugs/show_bug.cgi?id=19735626c->getDiagnosticOpts().ShowCarets = false;627628c->createDiagnostics(new clang::TextDiagnosticPrinter(629*new raw_string_ostream(log),630&c->getDiagnosticOpts(), true));631632c->setTarget(clang::TargetInfo::CreateTargetInfo(633c->getDiagnostics(), c->getInvocation().TargetOpts));634635c->getFrontendOpts().ProgramAction = clang::frontend::EmitLLVMOnly;636c->getHeaderSearchOpts().UseBuiltinIncludes = false;637c->getHeaderSearchOpts().UseStandardSystemIncludes = false;638639// Add opencl-c generic search path640{641::llvm::SmallString<128> system_header_path;642::llvm::sys::path::system_temp_directory(true, system_header_path);643::llvm::sys::path::append(system_header_path, "openclon12");644c->getHeaderSearchOpts().AddPath(system_header_path.str(),645clang::frontend::Angled,646false, false);647648::llvm::sys::path::append(system_header_path, "opencl-c.h");649c->getPreprocessorOpts().addRemappedFile(system_header_path.str(),650::llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(opencl_c_source, _countof(opencl_c_source) - 1)).release());651652::llvm::sys::path::remove_filename(system_header_path);653::llvm::sys::path::append(system_header_path, "opencl-c-base.h");654c->getPreprocessorOpts().addRemappedFile(system_header_path.str(),655::llvm::MemoryBuffer::getMemBuffer(llvm::StringRef(opencl_c_base_source, _countof(opencl_c_base_source) - 1)).release());656}657658if (args->num_headers) {659::llvm::SmallString<128> tmp_header_path;660::llvm::sys::path::system_temp_directory(true, tmp_header_path);661::llvm::sys::path::append(tmp_header_path, "openclon12");662663c->getHeaderSearchOpts().AddPath(tmp_header_path.str(),664clang::frontend::Quoted,665false, false);666667for (size_t i = 0; i < args->num_headers; i++) {668auto path_copy = tmp_header_path;669::llvm::sys::path::append(path_copy, ::llvm::sys::path::convert_to_slash(args->headers[i].name));670c->getPreprocessorOpts().addRemappedFile(path_copy.str(),671::llvm::MemoryBuffer::getMemBufferCopy(args->headers[i].value).release());672}673}674675c->getPreprocessorOpts().addRemappedFile(676args->source.name,677::llvm::MemoryBuffer::getMemBufferCopy(std::string(args->source.value)).release());678679// Compile the code680clang::EmitLLVMOnlyAction act(llvm_ctx.get());681if (!c->ExecuteAction(act)) {682log += "Error executing LLVM compilation action.\n";683clc_error(logger, log.c_str());684return -1;685}686687auto mod = act.takeModule();688std::ostringstream spv_stream;689if (!::llvm::writeSpirv(mod.get(), spv_stream, log)) {690log += "Translation from LLVM IR to SPIR-V failed.\n";691clc_error(logger, log.c_str());692return -1;693}694695const std::string spv_out = spv_stream.str();696spvbin->size = spv_out.size();697spvbin->data = static_cast<uint32_t *>(malloc(spvbin->size));698memcpy(spvbin->data, spv_out.data(), spvbin->size);699700return 0;701}702703static const char *704spv_result_to_str(spv_result_t res)705{706switch (res) {707case SPV_SUCCESS: return "success";708case SPV_UNSUPPORTED: return "unsupported";709case SPV_END_OF_STREAM: return "end of stream";710case SPV_WARNING: return "warning";711case SPV_FAILED_MATCH: return "failed match";712case SPV_REQUESTED_TERMINATION: return "requested termination";713case SPV_ERROR_INTERNAL: return "internal error";714case SPV_ERROR_OUT_OF_MEMORY: return "out of memory";715case SPV_ERROR_INVALID_POINTER: return "invalid pointer";716case SPV_ERROR_INVALID_BINARY: return "invalid binary";717case SPV_ERROR_INVALID_TEXT: return "invalid text";718case SPV_ERROR_INVALID_TABLE: return "invalid table";719case SPV_ERROR_INVALID_VALUE: return "invalid value";720case SPV_ERROR_INVALID_DIAGNOSTIC: return "invalid diagnostic";721case SPV_ERROR_INVALID_LOOKUP: return "invalid lookup";722case SPV_ERROR_INVALID_ID: return "invalid id";723case SPV_ERROR_INVALID_CFG: return "invalid config";724case SPV_ERROR_INVALID_LAYOUT: return "invalid layout";725case SPV_ERROR_INVALID_CAPABILITY: return "invalid capability";726case SPV_ERROR_INVALID_DATA: return "invalid data";727case SPV_ERROR_MISSING_EXTENSION: return "missing extension";728case SPV_ERROR_WRONG_VERSION: return "wrong version";729default: return "unknown error";730}731}732733class SPIRVMessageConsumer {734public:735SPIRVMessageConsumer(const struct clc_logger *logger): logger(logger) {}736737void operator()(spv_message_level_t level, const char *src,738const spv_position_t &pos, const char *msg)739{740switch(level) {741case SPV_MSG_FATAL:742case SPV_MSG_INTERNAL_ERROR:743case SPV_MSG_ERROR:744clc_error(logger, "(file=%s,line=%ld,column=%ld,index=%ld): %s",745src, pos.line, pos.column, pos.index, msg);746break;747748case SPV_MSG_WARNING:749clc_warning(logger, "(file=%s,line=%ld,column=%ld,index=%ld): %s",750src, pos.line, pos.column, pos.index, msg);751break;752753default:754break;755}756}757758private:759const struct clc_logger *logger;760};761762int763clc_link_spirv_binaries(const struct clc_linker_args *args,764struct spirv_binary *dst_bin,765const struct clc_logger *logger)766{767std::vector<std::vector<uint32_t>> binaries;768769for (unsigned i = 0; i < args->num_in_objs; i++) {770std::vector<uint32_t> bin(args->in_objs[i]->spvbin.data,771args->in_objs[i]->spvbin.data +772(args->in_objs[i]->spvbin.size / 4));773binaries.push_back(bin);774}775776SPIRVMessageConsumer msgconsumer(logger);777spvtools::Context context(SPV_ENV_UNIVERSAL_1_0);778context.SetMessageConsumer(msgconsumer);779spvtools::LinkerOptions options;780options.SetAllowPartialLinkage(args->create_library);781options.SetCreateLibrary(args->create_library);782std::vector<uint32_t> linkingResult;783spv_result_t status = spvtools::Link(context, binaries, &linkingResult, options);784if (status != SPV_SUCCESS) {785return -1;786}787788dst_bin->size = linkingResult.size() * 4;789dst_bin->data = static_cast<uint32_t *>(malloc(dst_bin->size));790memcpy(dst_bin->data, linkingResult.data(), dst_bin->size);791792return 0;793}794795void796clc_dump_spirv(const struct spirv_binary *spvbin, FILE *f)797{798spvtools::SpirvTools tools(SPV_ENV_UNIVERSAL_1_0);799std::vector<uint32_t> bin(spvbin->data, spvbin->data + (spvbin->size / 4));800std::string out;801tools.Disassemble(bin, &out,802SPV_BINARY_TO_TEXT_OPTION_INDENT |803SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES);804fwrite(out.c_str(), out.size(), 1, f);805}806807void808clc_free_spirv_binary(struct spirv_binary *spvbin)809{810free(spvbin->data);811}812813814