/*
* Copyright © 2012 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see .
*
* Author: Benjamin Segovia
*/
/**
* \file program.cpp
* \author Benjamin Segovia
*/
#ifdef GBE_COMPILER_AVAILABLE
#include "llvm/Config/llvm-config.h"
#include "llvm/IR/LLVMContext.h"
#include "llvm/IR/Module.h"
#include "llvm/IR/DataLayout.h"
#include "llvm-c/Linker.h"
#include "llvm/Transforms/Utils/Cloning.h"
#if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 40
#include "llvm/Bitcode/BitcodeWriter.h"
#else
#include "llvm/Bitcode/ReaderWriter.h"
#endif /* LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 40 */
#include "llvm/Support/raw_ostream.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/IRReader/IRReader.h"
#endif
#include "backend/program.h"
#include "backend/gen_program.h"
#include "backend/gen_program.hpp"
#include "backend/gen_context.hpp"
#include "backend/gen75_context.hpp"
#include "backend/gen8_context.hpp"
#include "backend/gen9_context.hpp"
#include "backend/gen_defs.hpp"
#include "backend/gen/gen_mesa_disasm.h"
#include "backend/gen_reg_allocation.hpp"
#include "ir/unit.hpp"
#ifdef GBE_COMPILER_AVAILABLE
#include "llvm/llvm_to_gen.hpp"
#include "llvm/llvm_gen_backend.hpp"
#include
#endif
#include "sys/cvar.hpp"
#include
#include
#include
#include
#include
#include
#include
namespace gbe {
GenKernel::GenKernel(const std::string &name, uint32_t deviceID) :
Kernel(name), deviceID(deviceID), insns(NULL), insnNum(0)
{}
GenKernel::~GenKernel(void) { GBE_SAFE_DELETE_ARRAY(insns); }
const char *GenKernel::getCode(void) const { return (const char*) insns; }
void GenKernel::setCode(const char * ins, size_t size) {
insns = (GenInstruction *)ins;
insnNum = size / sizeof(GenInstruction);
}
uint32_t GenKernel::getCodeSize(void) const { return insnNum * sizeof(GenInstruction); }
void GenKernel::printStatus(int indent, std::ostream& outs) {
#ifdef GBE_COMPILER_AVAILABLE
Kernel::printStatus(indent, outs);
FILE *f = fopen("/dev/null", "w");
if(!f) {
outs << "could not open /dev/null !";
return;
}
char *buf = new char[4096];
setbuffer(f, buf, 4096);
GenCompactInstruction * pCom = NULL;
GenInstruction insn[2];
uint32_t insn_version = 0;
if (IS_GEN7(deviceID) || IS_GEN75(deviceID))
insn_version = 7;
else if (IS_GEN8(deviceID) || IS_GEN9(deviceID))
insn_version = 8;
for (uint32_t i = 0; i < insnNum;) {
pCom = (GenCompactInstruction*)(insns+i);
if(pCom->bits1.cmpt_control == 1) {
decompactInstruction(pCom, &insn, insn_version);
gen_disasm(f, &insn, deviceID, 1);
i++;
} else {
gen_disasm(f, insns+i, deviceID, 0);
i = i + 2;
}
outs << buf;
fflush(f);
setbuffer(f, NULL, 0);
setbuffer(f, buf, 4096);
}
setbuffer(f, NULL, 0);
delete [] buf;
fclose(f);
#endif
}
void GenProgram::CleanLlvmResource(void){
#ifdef GBE_COMPILER_AVAILABLE
if(module){
delete (llvm::Module*)module;
module = NULL;
}
if(llvm_ctx){
delete (llvm::LLVMContext*)llvm_ctx;
llvm_ctx = NULL;
}
#endif
}
/*! We must avoid spilling at all cost with Gen */
struct CodeGenStrategy {
uint32_t simdWidth;
uint32_t reservedSpillRegs;
bool limitRegisterPressure;
};
static const struct CodeGenStrategy codeGenStrategyDefault[] = {
{16, 0, false},
{8, 0, false},
{8, 8, false},
{8, 16, false},
};
static const struct CodeGenStrategy codeGenStrategySimd16[] = {
{16, 0, false},
{16, 8, false},
{16, 16, false},
};
IVAR(OCL_SIMD_WIDTH, 8, 15, 16);
Kernel *GenProgram::compileKernel(const ir::Unit &unit, const std::string &name,
bool relaxMath, int profiling) {
#ifdef GBE_COMPILER_AVAILABLE
// Be careful when the simdWidth is forced by the programmer. We can see it
// when the function already provides the simd width we need to use (i.e.
// non zero)
const ir::Function *fn = unit.getFunction(name);
const struct CodeGenStrategy* codeGenStrategy = codeGenStrategyDefault;
if(fn == NULL)
GBE_ASSERT(0);
uint32_t codeGenNum = sizeof(codeGenStrategyDefault) / sizeof(codeGenStrategyDefault[0]);
uint32_t codeGen = 0;
GenContext *ctx = NULL;
if ( fn->getSimdWidth() != 0 && OCL_SIMD_WIDTH != 15) {
GBE_ASSERTM(0, "unsupported SIMD width!");
}else if (fn->getSimdWidth() == 8 || OCL_SIMD_WIDTH == 8) {
codeGen = 1;
} else if (fn->getSimdWidth() == 16 || OCL_SIMD_WIDTH == 16){
codeGenStrategy = codeGenStrategySimd16;
codeGenNum = sizeof(codeGenStrategySimd16) / sizeof(codeGenStrategySimd16[0]);
} else if (fn->getSimdWidth() == 0 && OCL_SIMD_WIDTH == 15) {
codeGen = 0;
} else
GBE_ASSERTM(0, "unsupported SIMD width!");
Kernel *kernel = NULL;
// Stop when compilation is successful
if (IS_IVYBRIDGE(deviceID)) {
ctx = GBE_NEW(GenContext, unit, name, deviceID, relaxMath);
} else if (IS_HASWELL(deviceID)) {
ctx = GBE_NEW(Gen75Context, unit, name, deviceID, relaxMath);
} else if (IS_BROADWELL(deviceID)) {
ctx = GBE_NEW(Gen8Context, unit, name, deviceID, relaxMath);
} else if (IS_CHERRYVIEW(deviceID)) {
ctx = GBE_NEW(ChvContext, unit, name, deviceID, relaxMath);
} else if (IS_SKYLAKE(deviceID)) {
ctx = GBE_NEW(Gen9Context, unit, name, deviceID, relaxMath);
} else if (IS_BROXTON(deviceID)) {
ctx = GBE_NEW(BxtContext, unit, name, deviceID, relaxMath);
} else if (IS_KABYLAKE(deviceID)) {
ctx = GBE_NEW(KblContext, unit, name, deviceID, relaxMath);
} else if (IS_GEMINILAKE(deviceID)) {
ctx = GBE_NEW(GlkContext, unit, name, deviceID, relaxMath);
}
GBE_ASSERTM(ctx != NULL, "Fail to create the gen context\n");
if (profiling) {
ctx->setProfilingMode(true);
unit.getProfilingInfo()->setDeviceID(deviceID);
}
ctx->setASMFileName(this->asm_file_name);
for (; codeGen < codeGenNum; ++codeGen) {
const uint32_t simdWidth = codeGenStrategy[codeGen].simdWidth;
const bool limitRegisterPressure = codeGenStrategy[codeGen].limitRegisterPressure;
const uint32_t reservedSpillRegs = codeGenStrategy[codeGen].reservedSpillRegs;
// Force the SIMD width now and try to compile
ir::Function *simdFn = unit.getFunction(name);
if(simdFn == NULL)
GBE_ASSERT(0);
simdFn->setSimdWidth(simdWidth);
ctx->startNewCG(simdWidth, reservedSpillRegs, limitRegisterPressure);
kernel = ctx->compileKernel();
if (kernel != NULL) {
GBE_ASSERT(ctx->getErrCode() == NO_ERROR);
kernel->setOclVersion(unit.getOclVersion());
break;
}
simdFn->getImageSet()->clearInfo();
// If we get a out of range if/endif error.
// We need to set the context to if endif fix mode and restart the previous compile.
if ( ctx->getErrCode() == OUT_OF_RANGE_IF_ENDIF && !ctx->getIFENDIFFix() ) {
ctx->setIFENDIFFix(true);
codeGen--;
} else
GBE_ASSERT(!(ctx->getErrCode() == OUT_OF_RANGE_IF_ENDIF && ctx->getIFENDIFFix()));
}
//GBE_ASSERTM(kernel != NULL, "Fail to compile kernel, may need to increase reserved registers for spilling.");
return kernel;
#else
return NULL;
#endif
}
#define GEN_BINARY_HEADER_LENGTH 8
enum GEN_BINARY_HEADER_INDEX {
GBHI_BYT = 0,
GBHI_IVB = 1,
GBHI_HSW = 2,
GBHI_CHV = 3,
GBHI_BDW = 4,
GBHI_SKL = 5,
GBHI_BXT = 6,
GBHI_KBL = 7,
GBHI_GLK = 8,
GBHI_MAX,
};
#define GEN_BINARY_VERSION 1
static const unsigned char gen_binary_header[GBHI_MAX][GEN_BINARY_HEADER_LENGTH]= \
{{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'B', 'Y', 'T'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'I', 'V', 'B'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'H', 'S', 'W'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'C', 'H', 'V'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'B', 'D', 'W'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'S', 'K', 'L'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'B', 'X', 'T'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'K', 'B', 'T'},
{GEN_BINARY_VERSION, 'G','E', 'N', 'C', 'G', 'L', 'K'}
};
#define FILL_GEN_HEADER(binary, index) do {int i = 0; do {*(binary+i) = gen_binary_header[index][i]; i++; }while(i < GEN_BINARY_HEADER_LENGTH);}while(0)
#define FILL_BYT_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_BYT)
#define FILL_IVB_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_IVB)
#define FILL_HSW_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_HSW)
#define FILL_CHV_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_CHV)
#define FILL_BDW_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_BDW)
#define FILL_SKL_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_SKL)
#define FILL_BXT_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_BXT)
#define FILL_KBL_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_KBL)
#define FILL_GLK_HEADER(binary) FILL_GEN_HEADER(binary, GBHI_GLK)
static bool genHeaderCompare(const unsigned char *BufPtr, GEN_BINARY_HEADER_INDEX index)
{
bool matched = true;
for (int i = 1; i < GEN_BINARY_HEADER_LENGTH; ++i)
{
matched = matched && (BufPtr[i] == gen_binary_header[index][i]);
}
if(matched) {
if(BufPtr[0] != gen_binary_header[index][0]) {
std::cout << "Beignet binary format have been changed, please generate binary again.\n";
matched = false;
}
}
return matched;
}
#define MATCH_BYT_HEADER(binary) genHeaderCompare(binary, GBHI_BYT)
#define MATCH_IVB_HEADER(binary) genHeaderCompare(binary, GBHI_IVB)
#define MATCH_HSW_HEADER(binary) genHeaderCompare(binary, GBHI_HSW)
#define MATCH_CHV_HEADER(binary) genHeaderCompare(binary, GBHI_CHV)
#define MATCH_BDW_HEADER(binary) genHeaderCompare(binary, GBHI_BDW)
#define MATCH_SKL_HEADER(binary) genHeaderCompare(binary, GBHI_SKL)
#define MATCH_BXT_HEADER(binary) genHeaderCompare(binary, GBHI_BXT)
#define MATCH_KBL_HEADER(binary) genHeaderCompare(binary, GBHI_KBL)
#define MATCH_GLK_HEADER(binary) genHeaderCompare(binary, GBHI_GLK)
#define MATCH_DEVICE(deviceID, binary) ((IS_IVYBRIDGE(deviceID) && MATCH_IVB_HEADER(binary)) || \
(IS_IVYBRIDGE(deviceID) && MATCH_IVB_HEADER(binary)) || \
(IS_BAYTRAIL_T(deviceID) && MATCH_BYT_HEADER(binary)) || \
(IS_HASWELL(deviceID) && MATCH_HSW_HEADER(binary)) || \
(IS_BROADWELL(deviceID) && MATCH_BDW_HEADER(binary)) || \
(IS_CHERRYVIEW(deviceID) && MATCH_CHV_HEADER(binary)) || \
(IS_SKYLAKE(deviceID) && MATCH_SKL_HEADER(binary)) || \
(IS_BROXTON(deviceID) && MATCH_BXT_HEADER(binary)) || \
(IS_KABYLAKE(deviceID) && MATCH_KBL_HEADER(binary)) || \
(IS_GEMINILAKE(deviceID) && MATCH_GLK_HEADER(binary)) \
)
static gbe_program genProgramNewFromBinary(uint32_t deviceID, const char *binary, size_t size) {
using namespace gbe;
std::string binary_content;
if(size < GEN_BINARY_HEADER_LENGTH)
return NULL;
//the header length is 8 bytes: 1 byte is binary type, 4 bytes are bitcode header, 3 bytes are hw info.
if(!MATCH_DEVICE(deviceID, (unsigned char*)binary)){
return NULL;
}
binary_content.assign(binary+GEN_BINARY_HEADER_LENGTH, size-GEN_BINARY_HEADER_LENGTH);
GenProgram *program = GBE_NEW(GenProgram, deviceID);
std::istringstream ifs(binary_content, std::ostringstream::binary);
if (!program->deserializeFromBin(ifs)) {
delete program;
return NULL;
}
//program->printStatus(0, std::cout);
return reinterpret_cast(program);
}
static gbe_program genProgramNewFromLLVMBinary(uint32_t deviceID, const char *binary, size_t size) {
#ifdef GBE_COMPILER_AVAILABLE
using namespace gbe;
std::string binary_content;
//the first byte stands for binary_type.
binary_content.assign(binary+1, size-1);
llvm::StringRef llvm_bin_str(binary_content);
#if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 39
llvm::LLVMContext& c = GBEGetLLVMContext();
#else
llvm::LLVMContext& c = llvm::getGlobalContext();
#endif
llvm::SMDiagnostic Err;
#if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 36
std::unique_ptr memory_buffer = llvm::MemoryBuffer::getMemBuffer(llvm_bin_str, "llvm_bin_str");
acquireLLVMContextLock();
llvm::Module* module = llvm::parseIR(memory_buffer->getMemBufferRef(), Err, c).release();
#else
llvm::MemoryBuffer* memory_buffer = llvm::MemoryBuffer::getMemBuffer(llvm_bin_str, "llvm_bin_str");
acquireLLVMContextLock();
llvm::Module* module = llvm::ParseIR(memory_buffer, Err, c);
#endif
// if load 32 bit spir binary, the triple should be spir-unknown-unknown.
llvm::Triple triple(module->getTargetTriple());
if (triple.getArchName() == "spir" && triple.getVendorName() == "unknown" && triple.getOSName() == "unknown"){
module->setTargetTriple("spir");
} else if (triple.getArchName() == "spir64" && triple.getVendorName() == "unknown" && triple.getOSName() == "unknown"){
module->setTargetTriple("spir64");
}
releaseLLVMContextLock();
if(module == NULL){
GBE_ASSERT(0);
}
GenProgram *program = GBE_NEW(GenProgram, deviceID, module);
//program->printStatus(0, std::cout);
return reinterpret_cast(program);
#else
return NULL;
#endif
}
static size_t genProgramSerializeToBinary(gbe_program program, char **binary, int binary_type) {
using namespace gbe;
size_t sz;
std::ostringstream oss;
GenProgram *prog = (GenProgram*)program;
//0 means GEN binary, 1 means LLVM bitcode compiled object, 2 means LLVM bitcode library
if(binary_type == 0){
if ((sz = prog->serializeToBin(oss)) == 0) {
*binary = NULL;
return 0;
}
//add header to differetiate from llvm bitcode binary.
//the header length is 8 bytes: 1 byte is binary type, 4 bytes are bitcode header, 3 bytes are hw info.
*binary = (char *)malloc(sizeof(char) * (sz+GEN_BINARY_HEADER_LENGTH) );
if(*binary == NULL)
return 0;
memset(*binary, 0, sizeof(char) * (sz+GEN_BINARY_HEADER_LENGTH) );
if(IS_IVYBRIDGE(prog->deviceID)){
FILL_IVB_HEADER(*binary);
if(IS_BAYTRAIL_T(prog->deviceID)){
FILL_BYT_HEADER(*binary);
}
}else if(IS_HASWELL(prog->deviceID)){
FILL_HSW_HEADER(*binary);
}else if(IS_BROADWELL(prog->deviceID)){
FILL_BDW_HEADER(*binary);
}else if(IS_CHERRYVIEW(prog->deviceID)){
FILL_CHV_HEADER(*binary);
}else if(IS_SKYLAKE(prog->deviceID)){
FILL_SKL_HEADER(*binary);
}else if(IS_BROXTON(prog->deviceID)){
FILL_BXT_HEADER(*binary);
}else if(IS_KABYLAKE(prog->deviceID)){
FILL_KBL_HEADER(*binary);
}else if(IS_GEMINILAKE(prog->deviceID)){
FILL_GLK_HEADER(*binary);
}else {
free(*binary);
*binary = NULL;
return 0;
}
memcpy(*binary+GEN_BINARY_HEADER_LENGTH, oss.str().c_str(), sz*sizeof(char));
return sz+GEN_BINARY_HEADER_LENGTH;
}else{
#ifdef GBE_COMPILER_AVAILABLE
std::string str;
llvm::raw_string_ostream OS(str);
llvm::WriteBitcodeToFile((llvm::Module*)prog->module, OS);
std::string& bin_str = OS.str();
int llsz = bin_str.size();
*binary = (char *)malloc(sizeof(char) * (llsz+1) );
if(*binary == NULL)
return 0;
*(*binary) = binary_type;
memcpy(*binary+1, bin_str.c_str(), llsz);
return llsz+1;
#else
return 0;
#endif
}
}
static gbe_program genProgramNewFromLLVM(uint32_t deviceID,
const void* module,
const void* llvm_ctx,
const char* asm_file_name,
size_t stringSize,
char *err,
size_t *errSize,
int optLevel,
const char* options)
{
using namespace gbe;
uint32_t fast_relaxed_math = 0;
if (options != NULL)
if (strstr(options, "-cl-fast-relaxed-math") != NULL)
fast_relaxed_math = 1;
GenProgram *program = GBE_NEW(GenProgram, deviceID, module, llvm_ctx, asm_file_name, fast_relaxed_math);
#ifdef GBE_COMPILER_AVAILABLE
std::string error;
// Try to compile the program
if (program->buildFromLLVMModule(module, error, optLevel) == false) {
if (err != NULL && errSize != NULL && stringSize > 0u) {
const size_t msgSize = std::min(error.size(), stringSize-1u);
std::memcpy(err, error.c_str(), msgSize);
*errSize = error.size();
}
GBE_DELETE(program);
return NULL;
}
#endif
// Everything run fine
return (gbe_program) program;
}
static gbe_program genProgramNewGenProgram(uint32_t deviceID, const void* module,
const void* llvm_ctx,const char* asm_file_name) {
using namespace gbe;
GenProgram *program = GBE_NEW(GenProgram, deviceID, module, llvm_ctx, asm_file_name);
// Everything run fine
return (gbe_program) program;
}
static bool genProgramLinkFromLLVM(gbe_program dst_program,
gbe_program src_program,
size_t stringSize,
char * err,
size_t * errSize)
{
#ifdef GBE_COMPILER_AVAILABLE
using namespace gbe;
char* errMsg = NULL;
if(((GenProgram*)dst_program)->module == NULL){
#if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 38
((GenProgram*)dst_program)->module = llvm::CloneModule((llvm::Module*)((GenProgram*)src_program)->module).release();
#else
((GenProgram*)dst_program)->module = llvm::CloneModule((llvm::Module*)((GenProgram*)src_program)->module);
#endif
errSize = 0;
} else {
#if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 39
// Src now will be removed automatically. So clone it.
llvm::Module* src = llvm::CloneModule((llvm::Module*)((GenProgram*)src_program)->module).release();
#else
llvm::Module* src = (llvm::Module*)((GenProgram*)src_program)->module;
#endif
llvm::Module* dst = (llvm::Module*)((GenProgram*)dst_program)->module;
#if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 39
if (LLVMLinkModules2(wrap(dst), wrap(src))) {
#elif LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 37
if (LLVMLinkModules(wrap(dst), wrap(src), LLVMLinkerPreserveSource_Removed, &errMsg)) {
#else
if (LLVMLinkModules(wrap(dst), wrap(src), LLVMLinkerPreserveSource, &errMsg)) {
#endif
if (err != NULL && errSize != NULL && stringSize > 0u && errMsg) {
strncpy(err, errMsg, stringSize-1);
err[stringSize-1] = '\0';
*errSize = strlen(err);
}
return true;
}
}
// Everything run fine
#endif
return false;
}
static void genProgramBuildFromLLVM(gbe_program program,
size_t stringSize,
char *err,
size_t *errSize,
const char * options)
{
#ifdef GBE_COMPILER_AVAILABLE
using namespace gbe;
std::string error;
int optLevel = 1;
std::string dumpASMFileName;
size_t start = 0, end = 0;
uint32_t fast_relaxed_math = 0;
if(options) {
char *p;
p = strstr(const_cast(options), "-cl-opt-disable");
if (p)
optLevel = 0;
if (options != NULL)
if (strstr(options, "-cl-fast-relaxed-math") != NULL)
fast_relaxed_math = 1;
char *options_str = (char *)malloc(sizeof(char) * (strlen(options) + 1));
if (options_str == NULL)
return;
memcpy(options_str, options, strlen(options) + 1);
std::string optionStr(options_str);
while (end != std::string::npos) {
end = optionStr.find(' ', start);
std::string str = optionStr.substr(start, end - start);
start = end + 1;
if(str.size() == 0)
continue;
if(str.find("-dump-opt-asm=") != std::string::npos) {
dumpASMFileName = str.substr(str.find("=") + 1);
continue; // Don't push this str back; ignore it.
}
}
free(options_str);
}
GenProgram* p = (GenProgram*) program;
p->fast_relaxed_math = fast_relaxed_math;
if (!dumpASMFileName.empty()) {
p->asm_file_name = dumpASMFileName.c_str();
FILE *asmDumpStream = fopen(dumpASMFileName.c_str(), "w");
if (asmDumpStream)
fclose(asmDumpStream);
}
// Try to compile the program
acquireLLVMContextLock();
llvm::Module* module = (llvm::Module*)p->module;
if (p->buildFromLLVMModule(module, error, optLevel) == false) {
if (err != NULL && errSize != NULL && stringSize > 0u) {
const size_t msgSize = std::min(error.size(), stringSize-1u);
std::memcpy(err, error.c_str(), msgSize);
*errSize = error.size();
}
}
releaseLLVMContextLock();
#endif
}
} /* namespace gbe */
void genSetupCallBacks(void)
{
gbe_program_new_from_binary = gbe::genProgramNewFromBinary;
gbe_program_new_from_llvm_binary = gbe::genProgramNewFromLLVMBinary;
gbe_program_serialize_to_binary = gbe::genProgramSerializeToBinary;
gbe_program_new_from_llvm = gbe::genProgramNewFromLLVM;
gbe_program_new_gen_program = gbe::genProgramNewGenProgram;
gbe_program_link_from_llvm = gbe::genProgramLinkFromLLVM;
gbe_program_build_from_llvm = gbe::genProgramBuildFromLLVM;
}