/* * 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 . */ #include #include #include #include #include #include #include "sys/cvar.hpp" #include "src/GBEConfig.h" #include "llvm_includes.hpp" #include "llvm/llvm_gen_backend.hpp" #include "ir/unit.hpp" using namespace llvm; SVAR(OCL_BITCODE_LIB_PATH, OCL_BITCODE_BIN); SVAR(OCL_BITCODE_LIB_20_PATH, OCL_BITCODE_BIN_20); namespace gbe { static Module* createOclBitCodeModule(LLVMContext& ctx, bool strictMath, uint32_t oclVersion) { std::string bitCodeFiles = oclVersion >= 200 ? OCL_BITCODE_LIB_20_PATH : OCL_BITCODE_LIB_PATH; if(bitCodeFiles == "") bitCodeFiles = oclVersion >= 200 ? OCL_BITCODE_BIN_20 : OCL_BITCODE_BIN; std::istringstream bitCodeFilePath(bitCodeFiles); std::string FilePath; bool findBC = false; Module* oclLib = NULL; SMDiagnostic Err; while (std::getline(bitCodeFilePath, FilePath, ':')) { if(access(FilePath.c_str(), R_OK) == 0) { findBC = true; break; } } if (!findBC) { printf("Fatal Error: ocl lib %s does not exist\n", bitCodeFiles.c_str()); return NULL; } #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR <= 35 oclLib = getLazyIRFileModule(FilePath, Err, ctx); #else oclLib = getLazyIRFileModule(FilePath, Err, ctx).release(); #endif if (!oclLib) { printf("Fatal Error: ocl lib can not be opened\n"); return NULL; } llvm::GlobalVariable* mathFastFlag = oclLib->getGlobalVariable("__ocl_math_fastpath_flag"); assert(mathFastFlag); Type* intTy = IntegerType::get(ctx, 32); mathFastFlag->setInitializer(ConstantInt::get(intTy, strictMath ? 0 : 1)); return oclLib; } static bool materializedFuncCall(Module& src, Module& lib, llvm::Function& KF, std::set& MFS, std::vector&Gvs) { bool fromSrc = false; for (llvm::Function::iterator B = KF.begin(), BE = KF.end(); B != BE; B++) { for (BasicBlock::iterator instI = B->begin(), instE = B->end(); instI != instE; ++instI) { llvm::CallInst* call = dyn_cast(instI); if (!call) { continue; } llvm::Function * callFunc = call->getCalledFunction(); //if(!callFunc) { // continue; //} if (callFunc && callFunc->getIntrinsicID() != 0) continue; std::string fnName = call->getCalledValue()->stripPointerCasts()->getName(); if (!MFS.insert(fnName).second) { continue; } fromSrc = false; llvm::Function *newMF = lib.getFunction(fnName); if (!newMF) { newMF = src.getFunction(fnName); if (!newMF) { printf("Can not find the lib: %s\n", fnName.c_str()); return false; } fromSrc = true; } std::string ErrInfo;// = "Not Materializable"; if (!fromSrc && newMF->isMaterializable()) { #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 40 if (llvm::Error EC = newMF->materialize()) { std::string Msg; handleAllErrors(std::move(EC), [&](ErrorInfoBase &EIB) { Msg = EIB.message(); }); printf("Can not materialize the function: %s, because %s\n", fnName.c_str(), Msg.c_str()); return false; } Gvs.push_back((GlobalValue *)newMF); #elif LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 36 if (std::error_code EC = newMF->materialize()) { printf("Can not materialize the function: %s, because %s\n", fnName.c_str(), EC.message().c_str()); return false; } Gvs.push_back((GlobalValue *)newMF); #else if (newMF->Materialize(&ErrInfo)) { printf("Can not materialize the function: %s, because %s\n", fnName.c_str(), ErrInfo.c_str()); return false; } #endif } if (!materializedFuncCall(src, lib, *newMF, MFS, Gvs)) return false; } } return true; } Module* runBitCodeLinker(Module *mod, bool strictMath, ir::Unit &unit) { LLVMContext& ctx = mod->getContext(); std::set materializedFuncs; std::vector Gvs; uint32_t oclVersion = getModuleOclVersion(mod); ir::PointerSize size = oclVersion >= 200 ? ir::POINTER_64_BITS : ir::POINTER_32_BITS; unit.setPointerSize(size); Module* clonedLib = createOclBitCodeModule(ctx, strictMath, oclVersion); if (clonedLib == NULL) return NULL; std::vector kernels; std::vector kerneltmp; std::vector builtinFuncs; /* Add the memset and memcpy functions here. */ builtinFuncs.push_back("__gen_memcpy_gg"); builtinFuncs.push_back("__gen_memcpy_gp"); builtinFuncs.push_back("__gen_memcpy_gl"); builtinFuncs.push_back("__gen_memcpy_pg"); builtinFuncs.push_back("__gen_memcpy_pp"); builtinFuncs.push_back("__gen_memcpy_pl"); builtinFuncs.push_back("__gen_memcpy_lg"); builtinFuncs.push_back("__gen_memcpy_lp"); builtinFuncs.push_back("__gen_memcpy_ll"); builtinFuncs.push_back("__gen_memset_p"); builtinFuncs.push_back("__gen_memset_g"); builtinFuncs.push_back("__gen_memset_l"); builtinFuncs.push_back("__gen_memcpy_gg_align"); builtinFuncs.push_back("__gen_memcpy_gp_align"); builtinFuncs.push_back("__gen_memcpy_gl_align"); builtinFuncs.push_back("__gen_memcpy_pg_align"); builtinFuncs.push_back("__gen_memcpy_pp_align"); builtinFuncs.push_back("__gen_memcpy_pl_align"); builtinFuncs.push_back("__gen_memcpy_lg_align"); builtinFuncs.push_back("__gen_memcpy_lp_align"); builtinFuncs.push_back("__gen_memcpy_ll_align"); builtinFuncs.push_back("__gen_memset_p_align"); builtinFuncs.push_back("__gen_memset_g_align"); builtinFuncs.push_back("__gen_memset_l_align"); builtinFuncs.push_back("__gen_memcpy_pc"); builtinFuncs.push_back("__gen_memcpy_gc"); builtinFuncs.push_back("__gen_memcpy_lc"); builtinFuncs.push_back("__gen_memcpy_pc_align"); builtinFuncs.push_back("__gen_memcpy_gc_align"); builtinFuncs.push_back("__gen_memcpy_lc_align"); if (oclVersion >= 200) { builtinFuncs.push_back("__gen_memcpy_gn"); builtinFuncs.push_back("__gen_memcpy_pn"); builtinFuncs.push_back("__gen_memcpy_ln"); builtinFuncs.push_back("__gen_memcpy_ng"); builtinFuncs.push_back("__gen_memcpy_np"); builtinFuncs.push_back("__gen_memcpy_nl"); builtinFuncs.push_back("__gen_memcpy_nc"); builtinFuncs.push_back("__gen_memcpy_nn"); builtinFuncs.push_back("__gen_memset_n"); builtinFuncs.push_back("__gen_memcpy_gn_align"); builtinFuncs.push_back("__gen_memcpy_pn_align"); builtinFuncs.push_back("__gen_memcpy_ln_align"); builtinFuncs.push_back("__gen_memcpy_ng_align"); builtinFuncs.push_back("__gen_memcpy_np_align"); builtinFuncs.push_back("__gen_memcpy_nl_align"); builtinFuncs.push_back("__gen_memcpy_nc_align"); builtinFuncs.push_back("__gen_memcpy_nn_align"); builtinFuncs.push_back("__gen_memset_n_align"); } for (Module::iterator SF = mod->begin(), E = mod->end(); SF != E; ++SF) { if (SF->isDeclaration()) continue; if (!isKernelFunction(*SF)) continue; // mod will be deleted after link, copy the names. const char *funcName = SF->getName().data(); char * tmp = new char[strlen(funcName)+1]; strcpy(tmp,funcName); kernels.push_back(tmp); kerneltmp.push_back(tmp); if (!materializedFuncCall(*mod, *clonedLib, *SF, materializedFuncs, Gvs)) { delete clonedLib; return NULL; } Gvs.push_back((GlobalValue *)&*SF); } if (kernels.empty()) { printf("One module without kernel function!\n"); delete clonedLib; return NULL; } for (auto &f : builtinFuncs) { const std::string fnName(f); if (!materializedFuncs.insert(fnName).second) { continue; } llvm::Function *newMF = clonedLib->getFunction(fnName); if (!newMF) { printf("Can not find the function: %s\n", fnName.c_str()); delete clonedLib; return NULL; } std::string ErrInfo;// = "Not Materializable"; if (newMF->isMaterializable()) { #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 40 if (llvm::Error EC = newMF->materialize()) { std::string Msg; handleAllErrors(std::move(EC), [&](ErrorInfoBase &EIB) { Msg = EIB.message(); }); printf("Can not materialize the function: %s, because %s\n", fnName.c_str(), Msg.c_str()); delete clonedLib; return NULL; } #elif LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 36 if (std::error_code EC = newMF->materialize()) { printf("Can not materialize the function: %s, because %s\n", fnName.c_str(), EC.message().c_str()); delete clonedLib; return NULL; } #else if (newMF->Materialize(&ErrInfo)) { printf("Can not materialize the function: %s, because %s\n", fnName.c_str(), ErrInfo.c_str()); delete clonedLib; return NULL; } #endif } if (!materializedFuncCall(*mod, *clonedLib, *newMF, materializedFuncs, Gvs)) { delete clonedLib; return NULL; } Gvs.push_back((GlobalValue *)newMF); kernels.push_back(f); } /* The llvm 3.8 now has a strict materialized check for all value by checking * module is materialized. If we want to use library as old style that just * materialize what we need, we need to remove what we did not need before * materialize all of the module. To do this, we need all of the builtin * funcitons and what are needed from the kernel functions, these functions * are materalized and are recorded in Gvs, the GlobalValue like PI are also * needed and are added. Now we could not use use_empty to check if the GVs * are needed before the module is marked as all materialized, so we just * materialize all of them as there are only 7 GVs. Then we use GVExtraction * pass to extract the functions and values in Gvs from the library module. * After extract what we need and remove what we do not need, we use * materializeAll to mark the module as materialized. */ #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 38 /* Get all GlobalValue from module. */ Module::GlobalListType &GVlist = clonedLib->getGlobalList(); for(Module::global_iterator GVitr = GVlist.begin();GVitr != GVlist.end();++GVitr) { GlobalValue * GV = &*GVitr; #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 40 ExitOnError ExitOnErr("Can not materialize the clonedLib: "); ExitOnErr(clonedLib->materialize(GV)); #else clonedLib->materialize(GV); #endif Gvs.push_back(GV); } llvm::legacy::PassManager Extract; /* Extract all values we need using GVExtractionPass. */ Extract.add(createGVExtractionPass(Gvs, false)); Extract.run(*clonedLib); /* Mark the library module as materialized for later use. */ #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 40 ExitOnError ExitOnErr("Can not materialize the clonedLib: "); ExitOnErr(clonedLib->materializeAll()); #else clonedLib->materializeAll(); #endif #endif /* the SPIR binary datalayout maybe different with beignet's bitcode */ if(clonedLib->getDataLayout() != mod->getDataLayout()) mod->setDataLayout(clonedLib->getDataLayout()); /* We use beignet's bitcode as dst because it will have a lot of lazy functions which will not be loaded. */ #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 39 llvm::Module * linked_module = llvm::CloneModule((llvm::Module*)mod).release(); if(LLVMLinkModules2(wrap(clonedLib), wrap(linked_module))) { #else char* errorMsg; if(LLVMLinkModules(wrap(clonedLib), wrap(mod), LLVMLinkerDestroySource, &errorMsg)) { printf("Fatal Error: link the bitcode error:\n%s\n", errorMsg); #endif delete clonedLib; return NULL; } #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 37 llvm::legacy::PassManager passes; #else llvm::PassManager passes; #endif #if LLVM_VERSION_MAJOR * 10 + LLVM_VERSION_MINOR >= 39 auto PreserveKernel = [=](const GlobalValue &GV) { for(size_t i = 0;i < kernels.size(); ++i) if(strcmp(GV.getName().data(), kernels[i])) return true; return false; }; passes.add(createInternalizePass(PreserveKernel)); #else passes.add(createInternalizePass(kernels)); #endif passes.add(createGlobalDCEPass()); passes.run(*clonedLib); for(size_t i = 0;i < kerneltmp.size(); i++) delete[] kerneltmp[i]; return clonedLib; } } // end namespace