So in our previous post we were playing a bit with push constants, and came to the conclusion that using luajit over C++ to define dynamic behavior only introduced an about 5% performance penalty. Still, now I really feel like diving back into the LLVM JIT compiler implementation to check what has changed there since the last time I tried that 😁. And who knows, maybe I can easily reclaim that 5% hit in the end ? 😏
namespace nv { struct NervJITImpl; class NVLLVM_EXPORT NervJIT { public: enum HeaderType { HEADER_SYSTEM, HEADER_ANGLED, HEADER_QUOTED, }; private: NervJITImpl* impl; public: NervJIT(); ~NervJIT(); void loadModuleFromFiles(const FileList& files) { for(auto& f: files) { loadModuleFromFile(f); } } void clearMacroDefinitions(); void addMacroDefinition(std::string val); void addMacroDefinition(const std::string& key, const std::string& val) { addMacroDefinition(key+"="+val); } void linkModule(const std::string& outFile, const std::vector<std::string>& inputList, bool onlyNeeded, bool internalize, bool optimize, bool preserveUseListOrder); void clearHeaderSearchPaths(); void addHeaderSearchPath(std::string path, HeaderType htype); void generateBitcodeFromFile(const std::string& inputFile, std::string outFile); void generateBitcodeFromBuffer(const std::string& buffer, std::string outFile); void generateObjectFromFile(const std::string& inputFile, std::string outFile); void generateObjectFromBuffer(const std::string& buffer, std::string outFile); void generatePCHFromFile(const std::string& inputFile, std::string outFile); void generatePCHFromBuffer(const std::string& buffer, std::string outFile); void usePCHFile(std::string pchfile); void loadModuleBitcode(const std::string& bcfile); void loadObject(const std::string& objFile); void loadModuleFromFile(const std::string& file); void loadModuleFromBuffer(const std::string& buffer); uint64_t lookup(const std::string& name); void addCurrentProcess(); void addDynamicLib(const char* filename); void setupCommandLine(const std::vector<std::string>& args); void setCurrentDylib(const std::string& libname); void createDylib(const std::string& libname); bool hasDylib(const std::string& libname); }; };
---@class app.LLVMApp: app.AppBase local Class = createClass { name = "LLVMApp", bases = "app.AppBase" } local jit = import "base.JITCompiler" function Class:__init(args) Class.super.__init(self, args) end function Class:run() logDEBUG("Done running app.") end return Class
---@class base.JITCompiler: base.Object local Class = createClass { name = "LLVMApp", bases = "base.Object" } local bm = import "base.BindingsManager" function Class:__init() Class.super.__init(self) logDEBUG('Loading luaLLVM module...') bm:loadBindings("LLVM", "luaLLVM") logDEBUG('Done loading luaLLVM module.') -- Create the NervJIT engine: self.nvjit = nvll.NervJIT() end -- Return an instance of that class: return Class()
NervJITImpl::NervJITImpl() { logDEBUG("Creating NervJITImpl."); // First we detect the host here: auto jtmb = CHECK_LLVM(llvm::orc::JITTargetMachineBuilder::detectHost()); logDEBUG("Detected CPU: {}", jtmb.getCPU()); logDEBUG("Detected Triple: {}", jtmb.getTargetTriple().str()); }
2022-11-17 12:36:58.508021 [DEBUG] Creating NervJIT object. 2022-11-17 12:36:58.508033 [DEBUG] Creating NervJITImpl. 2022-11-17 12:36:58.508215 [DEBUG] Detected CPU: ivybridge 2022-11-17 12:36:58.508223 [DEBUG] Detected Triple: x86_64-pc-windows-msvc
// Resetting emulateTLS to false if needed: auto& tgtOpts = jtmb.getOptions(); if (tgtOpts.EmulatedTLS || tgtOpts.ExplicitEmulatedTLS) { tgtOpts.EmulatedTLS = false; tgtOpts.ExplicitEmulatedTLS = false; logDEBUG("=> Explicitly disabling emulated TLS."); }/
2022-11-17 13:17:54.486091 [DEBUG] Detected CPU: ivybridge 2022-11-17 13:17:54.486106 [DEBUG] Detected Triple: x86_64-pc-windows-msvc 2022-11-17 13:17:54.486115 [DEBUG] => Explicitly disabling emulated TLS. 2022-11-17 13:17:54.486119 [DEBUG] Creating target machine... 2022-11-17 13:17:54.486248 [FATAL] Error in lua app: C++ exception 2022-11-17 13:17:54.486260 [DEBUG] Destroying NervApp... 2022-11-17 13:17:54.486498 [DEBUG] Destroying all lua states. 2022-11-17 13:17:54.486511 [INFO] Closing lua state...
function Class:init() nv.initLLVM() self.jit = nv.NervJIT() -- more stuff here. end
function Class:__init() Class.super.__init(self) logTRACE('Loading luaLLVM module...') bm:loadBindings("LLVM", "luaLLVM") logTRACE('Done loading luaLLVM module.') -- Initialize: self:init() -- Register uninit callback: utils.on_uninit(function() self:uninit() end) end
nvLogTRACE("Running uninit.lua") local utils = import "base.utils" utils.execute_uninit_callbacks()
_CxxThrowException(CxxExcept, (_ThrowInfo*)Info);
logDEBUG("Creating LLJIT object."); llvm::orc::LLJITBuilder llb; llb.setJITTargetMachineBuilder(std::move(jtmb)).setNumCompileThreads(2); #if 0 // We use our custom memory manager here: llb.setObjectLinkingLayerCreator( [](llvm::orc::ExecutionSession& ES, const llvm::Triple& triple) -> std::unique_ptr<llvm::orc::ObjectLayer> { auto GetMemMgr = []() { return std::make_unique<SingleSectionMemoryManager>(); }; auto ObjLinkingLayer = std::make_unique<llvm::orc::RTDyldObjectLinkingLayer>( ES, std::move(GetMemMgr)); // Note sure this is needed/appropriate ? if (triple.isOSBinFormatCOFF()) { ObjLinkingLayer->setOverrideObjectFlagsWithResponsibilityFlags( true); ObjLinkingLayer->setAutoClaimResponsibilityForObjectSymbols( true); } return {std::move(ObjLinkingLayer)}; }); #endif lljit = CHECK_LLVM(llb.create());
NervJITImpl::~NervJITImpl() { logDEBUG("Destroying NervJITImpl."); // We should start with running the at exit callbacks: run_exit_callbacks(); while (!dylibList.empty()) { std::string libname = dylibList.back(); dylibList.pop_back(); logDEBUG("Uninitializing dylib {}...", libname); checkLLVMError(lljit->deinitialize(*lljit->getJITDylibByName(libname))); } } void NervJITImpl::setup_dylib(llvm::orc::JITDylib& JD) const { llvm::orc::SymbolMap RuntimeInterposes; // But we still need to manually take care of the atexit function itself: RuntimeInterposes[(*mangler)("atexit")] = llvm::JITEvaluatedSymbol( toTargetAddress(&at_exit_override), llvm::JITSymbolFlags::Exported); checkLLVMError(JD.define(absoluteSymbols(std::move(RuntimeInterposes)))); }
CXXCompiler::CXXCompiler(const llvm::Triple& triple) { logDEBUG("Creating CXX Compiler."); diagnosticOptions = new clang::DiagnosticOptions; textDiagnosticPrinter = std::make_unique<clang::TextDiagnosticPrinter>( llvm::outs(), diagnosticOptions.get()); // The diagnotic engine should not own the client below (or it could if we // release our unique_ptr.) diagnosticsEngine = new clang::DiagnosticsEngine( diagIDs, diagnosticOptions, textDiagnosticPrinter.get(), false); compilerInstance = std::make_unique<clang::CompilerInstance>(); auto& compilerInvocation = compilerInstance->getInvocation(); auto& targetOptions = compilerInvocation.getTargetOpts(); auto& frontEndOptions = compilerInvocation.getFrontendOpts(); frontEndOptions.ShowStats = false; auto& headerSearchOptions = compilerInvocation.getHeaderSearchOpts(); headerSearchOptions.Verbose = false; headerSearchOptions.UserEntries.clear(); // targetOptions.Triple = llvm::sys::getDefaultTargetTriple(); targetOptions.Triple = triple.str(); compilerInstance->createDiagnostics(textDiagnosticPrinter.get(), false); }
-- We try to generate the simple PCH file: local pchfile = self:getPCHForBuffer([[ #include <core_lua.h> #include <lua/LuaManager.h> #include <NervApp.h> #include <view/WindowManager.h> using namespace nv; ]]) local startTime = nv.SystemTime.getCurrentTime() logDEBUG("JIT: Loading Lua extensions...") self:usePCHFile(pchfile) self:loadBitcodeForFile(self.script_dir.."lua_base_extensions.cpp") -- self.jit:generateBitcodeFromFile(self.script_dir.."lua_base_extensions.cpp", self.bc_dir.."lua_base_extensions.bc") -- self.jit:loadModuleBitcode(self.bc_dir.."lua_base_extensions.bc") -- self.jit:loadModuleFromFile(self.script_dir.."lua_base_extensions.cpp") local endTime = nv.SystemTime.getCurrentTime() logDEBUG(string.format("Script compiled in %.3fms", (endTime - startTime)*1000.0)) self.jit:call("loadLuaBaseExtensions")
#include <core_common.h> using namespace nv; extern "C" void helloWorld() { logDEBUG("Hello world from JIT function."); };
void CXXCompiler::set_input_buffer(llvm::MemoryBuffer* buf) const { auto& compilerInvocation = compilerInstance->getInvocation(); auto& frontEndOptions = compilerInvocation.getFrontendOpts(); frontEndOptions.Inputs.clear(); llvm::MemoryBufferRef rbuf(*buf); frontEndOptions.Inputs.push_back( clang::FrontendInputFile(rbuf, clang::InputKind(clang::Language::CXX))); } void CXXCompiler::generate_bitcode(const char* out_file) const { auto& compilerInvocation = compilerInstance->getInvocation(); auto& frontEndOptions = compilerInvocation.getFrontendOpts(); std::string prevFile = std::move(frontEndOptions.OutputFile); frontEndOptions.OutputFile = out_file; // keep a copy of the current program action: auto prevAction = frontEndOptions.ProgramAction; frontEndOptions.ProgramAction = clang::frontend::EmitBC; if (!compilerInstance->ExecuteAction(*emit_bc_action)) { logERROR("Failed executing emit_bc_action with compiler instance!"); } // Restore the previous values: frontEndOptions.OutputFile = std::move(prevFile); frontEndOptions.ProgramAction = prevAction; }
function Class:run() logDEBUG("Running simple compilation...") jit:buildBitcodeForFile("tests/hello_world.cpp") logDEBUG("Done running app.") end
2022-11-19 22:36:25.073421 [DEBUG] Running simple compilation... 2022-11-19 22:36:25.073946 [DEBUG] Generating bytecode file D:/Projects/NervLand/dist/compiled/3777ff771e2dffff7aff657249ffff550550ff6932ffffff60ff79ff38ffffffffffb4.bc... :1:10: fatal error: 'core_common.h' file not found #include <core_common.h> ^~~~~~~~~~~~~~~ 1 error generated. 2022-11-19 22:36:25.083238 [ERROR] Failed executing emit_bc_action with compiler instance! 2022-11-19 22:36:25.083310 [DEBUG] Bitcode compiled in 9.325ms 2022-11-19 22:36:25.083328 [DEBUG] Done running app.
void setup_command_line(const nv::StringList& args); void clear_header_search_paths(); void add_header_search_path(const char* path, HeaderType htype); void clear_macro_definitions(); void add_macro_definition(const char* macro);
-- Method used to register new lists: function Class:registerCommandLineArgList(clName, args) self.cl_args_lists[clName] = args end function Class:registerHeaderPathList(name, list) self.header_paths_lists[name] = list end function Class:registerMacroDefList(name, list) self.macro_defs_lists[name] = list end -- Setup usage of given lists: function Class:useCommandLineArgLists(listNames, keep) if not keep then self.current_cl_args = {} end for _, lname in ipairs(listNames) do local plist = self.cl_args_lists[lname] for _, entry in ipairs(plist) do table.insert(self.current_cl_args, entry) end end self.jit:setup_command_line(self.current_cl_args) logDEBUG("Done setting up command line args.") -- This will anyway clear the other settings: self.current_header_paths = {} self.current_macro_defs = {} self:updateCommandLineArgsHash() self:updateHeaderPathsHash() self:updateMacroDefsHash() end function Class:useHeaderPathLists(listNames, keep) if not keep then -- clear the previous list of header search paths: self.jit:clear_header_search_paths(); self.current_header_paths = {} end for _, lname in ipairs(listNames) do local plist = self.header_paths_lists[lname] for _, entry in ipairs(plist) do table.insert(self.current_header_paths, entry[1]) self.jit:add_header_search_path(entry[1], entry[2]); end end -- We now generate the hash: self:updateHeaderPathsHash() end function Class:useMacroDefLists(listNames, keep) if not keep then -- clear the previous list of header search paths: self.jit:clear_macro_definitions(); self.current_macro_defs = {} end for _, lname in ipairs(listNames) do local plist = self.macro_defs_lists[lname] for _, dval in ipairs(plist) do table.insert(self.current_macro_defs, dval) self.jit:add_macro_definition(dval) end end -- We now generate the hash: self:updateMacroDefsHash() end function Class:addHeaderPaths(paths, ptype) ptype = ptype or nvll.HeaderType.ANGLED for _, path in ipairs(paths) do table.insert(self.current_header_paths, path) self.jit:add_header_search_path(path, ptype); end -- We now generate the hash: self:updateHeaderPathsHash() end function Class:addMacroDefs(dvals) for _, dval in ipairs(dvals) do table.insert(self.current_macro_defs, dval) self.jit:add_macro_definition(dval) end -- We now generate the hash: self:updateMacroDefsHash() end -- Methods used to generate a hash value from a string list: function Class:computeStringListHash(list) return nv.SHA256.from_buffer(table.concat(list, " ")) end function Class:updateContextHash() self.current_context_hash = self.cl_args_hash .. self.header_paths_hash .. self.macro_defs_hash .. self.pch_file_hash -- logDEBUG("Current context hash is: ", self.current_context_hash) end function Class:updatePCHHash() self.pch_file_hash = nv.SHA256.from_buffer(self.current_pch_file) self:updateContextHash() end function Class:updateMacroDefsHash() self.macro_defs_hash = self:computeStringListHash(self.current_macro_defs) self:updateContextHash() end function Class:updateHeaderPathsHash() self.header_paths_hash = self:computeStringListHash(self.current_header_paths) self:updateContextHash() end function Class:updateCommandLineArgsHash() self.cl_args_hash = self:computeStringListHash(self.current_cl_args) self:updateContextHash() end -- Get the hash for a given buffer in the current compilation context: function Class:getContextualBufferHash(buf) local hash = nv.SHA256.from_buffer(buf) return nv.SHA256.from_buffer(self.current_context_hash .. hash) end
2022-11-22 15:51:50.429210 [DEBUG] Lua: Done creating NervJIT. 2022-11-22 15:51:50.429489 [DEBUG] Parsed args: {} 2022-11-22 15:51:50.429502 [DEBUG] Running simple compilation... 2022-11-22 15:51:50.434819 [DEBUG] Generating bytecode file D:/Projects/NervLand/dist/compiled/84b9b5238dba0d3c71dbe14e726fb5a4a6e477b34731e6b957ab6933d49e2e07.bc... 2022-11-22 15:51:53.347740 [DEBUG] Bitcode compiled in 2912.878ms 2022-11-22 15:51:53.347779 [DEBUG] Done running app. 2022-11-22 15:51:53.347790 [DEBUG] Destroying NervApp...
// Parse the main module. orc::ThreadSafeContext TSCtx(std::make_unique<LLVMContext>()); auto MainModule = ExitOnErr(loadModule(InputFile, TSCtx)); // Get TargetTriple and DataLayout from the main module if they're explicitly // set. Optional<Triple> TT; Optional<DataLayout> DL; MainModule.withModuleDo([&](Module &M) { if (!M.getTargetTriple().empty()) TT = Triple(M.getTargetTriple()); if (!M.getDataLayout().isDefault()) DL = M.getDataLayout(); });
// If the object cache is enabled then set a custom compile function // creator to use the cache. std::unique_ptr<LLIObjectCache> CacheManager; if (EnableCacheManager) { CacheManager = std::make_unique<LLIObjectCache>(ObjectCacheDir); Builder.setCompileFunctionCreator( [&](orc::JITTargetMachineBuilder JTMB) -> Expected<std::unique_ptr<orc::IRCompileLayer::IRCompiler>> { if (LazyJITCompileThreads > 0) return std::make_unique<orc::ConcurrentIRCompiler>(std::move(JTMB), CacheManager.get()); auto TM = JTMB.createTargetMachine(); if (!TM) return TM.takeError(); return std::make_unique<orc::TMOwningSimpleCompiler>(std::move(*TM), CacheManager.get()); }); }
std::unique_ptr<orc::ExecutorProcessControl> EPC = nullptr; if (JITLinker == JITLinkerKind::JITLink) { EPC = ExitOnErr(orc::SelfExecutorProcessControl::Create( std::make_shared<orc::SymbolStringPool>())); Builder.setObjectLinkingLayerCreator([&EPC](orc::ExecutionSession &ES, const Triple &) { auto L = std::make_unique<orc::ObjectLinkingLayer>(ES, EPC->getMemMgr()); L->addPlugin(std::make_unique<orc::EHFrameRegistrationPlugin>( ES, ExitOnErr(orc::EPCEHFrameRegistrar::Create(ES)))); L->addPlugin(std::make_unique<orc::DebugObjectManagerPlugin>( ES, ExitOnErr(orc::createJITLoaderGDBRegistrar(ES)))); return L; }); }
if (PerModuleLazy) J->setPartitionFunction(orc::CompileOnDemandLayer::compileWholeModule);
void NervJITImpl::setup_dylib(llvm::orc::JITDylib& JD) const { // make process symbols available to JIT'd code: JD.addGenerator(CHECK_LLVM( llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess( lljit->getDataLayout().getGlobalPrefix(), [MainName = (*mangler)("main")](const llvm::orc::SymbolStringPtr& Name) { return Name != MainName; }))); // Not sure we need the following anymore ? llvm::orc::SymbolMap RuntimeInterposes; // But we still need to manually take care of the atexit function itself: RuntimeInterposes[(*mangler)("atexit")] = llvm::JITEvaluatedSymbol( toTargetAddress(&at_exit_override), llvm::JITSymbolFlags::Exported); checkLLVMError(JD.define(absoluteSymbols(std::move(RuntimeInterposes)))); }
// Regular modules are greedy: They materialize as a whole and trigger // materialization for all required symbols recursively. Lazy modules go // through partitioning and they replace outgoing calls with reexport stubs // that resolve on call-through. auto AddModule = [&](orc::JITDylib &JD, orc::ThreadSafeModule M) { return UseJITKind == JITKind::OrcLazy ? J->addLazyIRModule(JD, std::move(M)) : J->addIRModule(JD, std::move(M)); };
// Run any -thread-entry points. std::vector<std::thread> AltEntryThreads; for (auto &ThreadEntryPoint : ThreadEntryPoints) { auto EntryPointSym = ExitOnErr(J->lookup(ThreadEntryPoint)); typedef void (*EntryPointPtr)(); auto EntryPoint = reinterpret_cast<EntryPointPtr>( static_cast<uintptr_t>(EntryPointSym.getAddress())); AltEntryThreads.push_back(std::thread([EntryPoint]() { EntryPoint(); })); }/
2022-11-23 08:19:33.801336 [DEBUG] Loading module from tests/hello_world_v1.cpp 2022-11-23 08:19:33.801716 [DEBUG] Generating bytecode file D:/Projects/NervLand/dist/compiled/5e4e0b1a6312893612634b3515416b4efc2545b760e8f6cdbf1dbb60b6d685b2.bc... 2022-11-23 08:19:36.480648 [DEBUG] Bitcode compiled in 2678.904ms 2022-11-23 08:19:36.496620 [DEBUG] Module dumped functions: [ __orc_init_func.D:/Projects/NervLand/dist/compiled/5e4e0b1a6312893612634b3515416b4efc2545b760e8f6cdbf1dbb60b6d685b2.bc.submodule.0x6e951aa21ea83511.ll ] LLVM ERROR: Associative COMDAT symbol '??_7?$basic_memory_buffer@D$0BPE@V?$allocator@D@std@@@v9@fmt@@6B@' is not a key for its COMDAT.
/
#include <iostream> extern "C" void helloWorld() { // Simple hello world output: std::cout << "Hello world from JIT function." << std::endl; };
2022-11-23 08:28:09.322860 [DEBUG] Destroying NervJITImpl. 2022-11-23 08:28:09.322896 [DEBUG] Uninitializing dylib main... 2022-11-23 08:28:09.323587 [DEBUG] Module dumped functions: [ __lljit_run_atexits ] JIT session error: Unsupported file format
/
void link(std::unique_ptr<LinkGraph> G, std::unique_ptr<JITLinkContext> Ctx) { switch (G->getTargetTriple().getObjectFormat()) { case Triple::MachO: return link_MachO(std::move(G), std::move(Ctx)); case Triple::ELF: return link_ELF(std::move(G), std::move(Ctx)); case Triple::COFF: return link_COFF(std::move(G), std::move(Ctx)); default: Ctx->notifyFailed(make_error<JITLinkError>("Unsupported object format")); }; }
- name: LLVM version: 15.0.4 extracted_dir: llvm-project-llvmorg-15.0.4 windows_url: https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-15.0.4.zip linux_url: https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-15.0.4.tar.gz # version: 14.0.6 # extracted_dir: llvm-project-llvmorg-14.0.6 # windows_url: https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-14.0.6.zip # linux_url: https://github.com/llvm/llvm-project/archive/refs/tags/llvmorg-14.0.6.tar.gz
.\nvp.bat build libs llvm -c msvc
-- Install configuration: "Release" -- Installing: D:/Projects/NervProj/libraries/windows_msvc/LLVM-15.0.4/lib/c++.lib -- Installing: D:/Projects/NervProj/libraries/windows_msvc/LLVM-15.0.4/bin/c++.dll -- Installing: D:/Projects/NervProj/libraries/windows_msvc/LLVM-15.0.4/lib/libc++.lib -- Installing: D:/Projects/NervProj/libraries/windows_msvc/LLVM-15.0.4/lib/libc++experimental.lib 2022/11/23 10:21:05 [nvp.core.build_manager] INFO: Removing build folder D:\Projects\NervProj\libraries\build\LLVM-15.0.4 2022/11/23 10:21:14 [nvp.core.build_manager] INFO: Done building LLVM-15.0.4 (build time: 2067.87 seconds)
.\nvp.bat build libs llvm -c clang
-- Install configuration: "Release" -- Installing: D:/Projects/NervProj/libraries/windows_clang/LLVM-15.0.4/lib/c++.lib -- Installing: D:/Projects/NervProj/libraries/windows_clang/LLVM-15.0.4/bin/c++.dll -- Installing: D:/Projects/NervProj/libraries/windows_clang/LLVM-15.0.4/lib/libc++.lib -- Installing: D:/Projects/NervProj/libraries/windows_clang/LLVM-15.0.4/lib/libc++experimental.lib 2022/11/23 12:38:21 [nvp.core.build_manager] INFO: Removing build folder D:\Projects\NervProj\libraries\build\LLVM-15.0.4 2022/11/23 12:38:30 [nvp.core.build_manager] INFO: Done building LLVM-15.0.4 (build time: 2421.34 seconds)
2022-11-23 21:51:29.911254 [DEBUG] Lua: Uninitializing JIT Compiler... 2022-11-23 21:51:29.911512 [DEBUG] Destroying NervJIT object. 2022-11-23 21:51:29.911521 [DEBUG] Destroying CXX Compiler. 2022-11-23 21:51:29.911557 [DEBUG] Destroying NervJITImpl. 2022-11-23 21:51:29.911561 [DEBUG] Uninitializing dylib main... 2022-11-23 21:51:29.912430 [DEBUG] Module dumped functions: [ __lljit_run_atexits atexit ] JIT session error: Unsupported x86_64 relocation:1
/
nvp.bat build libs luajit -c clang --rebuild
self:registerHeaderPathList("default", { { "D:/Projects/NervProj/libraries/windows_clang/LLVM-15.0.4/lib/clang/15.0.4/include", nvll.HeaderType.SYSTEM }, { "D:/Softs/VisualStudio2022CE/VC/Tools/MSVC/14.34.31933/include", nvll.HeaderType.SYSTEM }, { "D:/Softs/VisualStudio2022CE/VC/Tools/MSVC/14.34.31933/atlmfc/include", nvll.HeaderType.SYSTEM }, { "D:/Windows Kits/10/Include/10.0.19041.0/ucrt", nvll.HeaderType.SYSTEM }, { "D:/Windows Kits/10/Include/10.0.19041.0/shared", nvll.HeaderType.SYSTEM }, { "D:/Windows Kits/10/Include/10.0.19041.0/um", nvll.HeaderType.SYSTEM }, { "D:/Windows Kits/10/Include/10.0.19041.0/winrt", nvll.HeaderType.SYSTEM }, { "D:/Projects/NervProj/libraries/windows_clang/LLVM-15.0.4/lib/clang/15.0.4/include", nvll.HeaderType.SYSTEM }, { "D:/Softs/VisualStudio/VS2022/VC/Tools/MSVC/14.34.31933/include", nvll.HeaderType.SYSTEM }, { "D:/Softs/VisualStudio/VS2022/VC/Tools/MSVC/14.34.31933/atlmfc/include", nvll.HeaderType.SYSTEM }, { "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/ucrt", nvll.HeaderType.SYSTEM }, { "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/shared", nvll.HeaderType.SYSTEM }, { "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/um", nvll.HeaderType.SYSTEM }, { "C:/Program Files (x86)/Windows Kits/10/Include/10.0.19041.0/winrt", nvll.HeaderType.SYSTEM }, })
2022-11-25 11:39:38.637546 [DEBUG] Lua: Uninitializing JIT Compiler... 2022-11-25 11:39:38.637961 [DEBUG] Destroying NervJIT object. 2022-11-25 11:39:38.637975 [DEBUG] Destroying CXX Compiler. 2022-11-25 11:39:38.641635 [DEBUG] Destroying NervJITImpl. 2022-11-25 11:39:38.641667 [DEBUG] Uninitializing dylib main... 2022-11-25 11:39:38.642465 [DEBUG] Module dumped functions: [ __lljit_run_atexits atexit ] JIT session error: Unsupported x86_64 relocation:1
#if 0 logDEBUG("Creating execution process control"); // Prepare the JITLinker: executionProcessControl = CHECK_LLVM(llvm::orc::SelfExecutorProcessControl::Create( std::make_shared<llvm::orc::SymbolStringPool>())); logDEBUG("Setting up object linking layer creator."); llb.setObjectLinkingLayerCreator( [this](llvm::orc::ExecutionSession& ES, const llvm::Triple&) { auto L = std::make_unique<llvm::orc::ObjectLinkingLayer>( ES, executionProcessControl->getMemMgr()); // L->addPlugin(std::make_unique<llvm::orc::EHFrameRegistrationPlugin>( // ES, CHECK_LLVM(llvm::orc::EPCEHFrameRegistrar::Create(ES)))); // L->addPlugin(std::make_unique<llvm::orc::DebugObjectManagerPlugin>( // ES, CHECK_LLVM(llvm::orc::createJITLoaderGDBRegistrar(ES)))); return L; }); #endif
void NervJITImpl::call(const char* fname) const { auto funcAddr = CHECK_LLVM(lljit->lookup(fname)); using func_t = void(); auto func = funcAddr.toPtr<func_t*>(); CHECK(func != nullptr, "Invalid pointer for {}", fname); try { func(); } catch (const std::exception& e) { logERROR("Exception catched from JIT code: {}", e.what()); } catch (...) { logERROR("Unknown exception catched from JIT code."); } }
2022-11-25 14:26:47.113095 [DEBUG] JIT: Setting up macro definitions... 2022-11-25 14:26:47.113163 [DEBUG] Lua: Done creating NervJIT. 2022-11-25 14:26:47.113775 [DEBUG] Parsed args: {} 2022-11-25 14:26:47.113799 [DEBUG] Loading module from tests/hello_world_v0.cpp 2022-11-25 14:26:47.121210 [DEBUG] Module dumped functions: [ helloWorld ] 2022-11-25 14:26:47.128711 [DEBUG] Module dumped functions: [ ] 2022-11-25 14:26:47.131765 [DEBUG] Module dumped functions: [ ??$?6U?$char_traits@D@std@@@std@@YAAEAV?$basic_ostream@DU?$char_traits@D@std@@@0@AEAV10@PEBD@Z ] 2022-11-25 14:26:47.145352 [DEBUG] Module dumped functions: [ ?length@?$_Narrow_char_traits@DH@std@@SA_KQEBD@Z ] 2022-11-25 14:26:47.149317 [DEBUG] Module dumped functions: [ ??0sentry@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAA@AEAV12@@Z ] 2022-11-25 14:26:47.155943 [DEBUG] Module dumped functions: [ ??0_Sentry_base@?$basic_ostream@DU?$char_traits@D@std@@@std@@QEAA@AEAV12@@Z ]
2022-11-25 14:42:16.086668 [DEBUG] Lua: Done creating NervJIT. 2022-11-25 14:42:16.086955 [DEBUG] Parsed args: {} 2022-11-25 14:42:16.086974 [DEBUG] Loading module from tests/hello_world_v0.cpp Hello world from JIT function. 2022-11-25 14:42:16.140975 [DEBUG] Done running app. 2022-11-25 14:42:16.141002 [DEBUG] Destroying NervApp...
#include <core_common.h> using namespace nv; extern "C" void helloWorld() { logDEBUG("Hello world from JIT function with LogManager."); };
2022-11-25 15:41:52.169769 [DEBUG] Parsed args: {} 2022-11-25 15:41:52.169792 [DEBUG] Loading module from tests/hello_world_v1.cpp 2022-11-25 15:41:52.170334 [DEBUG] Generating bytecode file D:/Projects/NervLand/dist/compiled/ffaca731a73d87e83bc6973ca2f44373239012167e21d832130c254d792daea9.bc... 2022-11-25 15:41:54.785121 [DEBUG] Bitcode compiled in 2614.747ms LLVM ERROR: Associative COMDAT symbol '??_7?$basic_memory_buffer@D$0BPE@V?$allocator@D@std@@@v9@fmt@@6B@' is not a key for its COMDAT.
void NervJITImpl::add_library_from_file(const char* path) const { auto gen = CHECK_LLVM(llvm::orc::StaticLibraryDefinitionGenerator::Load( lljit->getObjLinkingLayer(), path, targetTriple)); logDEBUG("Adding generator for library {}", path); currentDylib->addGenerator(std::move(gen)); checkLLVMError(lljit->initialize(*currentDylib)); }
2022-11-25 16:15:34.538482 [DEBUG] Lua: Done creating NervJIT. 2022-11-25 16:15:34.538759 [DEBUG] Parsed args: {} 2022-11-25 16:15:34.538771 [DEBUG] Loading module from tests/hello_world_v1.cpp 2022-11-25 16:15:34.561055 [DEBUG] Hello world from JIT function with LogManager. 2022-11-25 16:15:34.561107 [DEBUG] Done running app. 2022-11-25 16:15:34.561127 [DEBUG] Destroying NervApp... 2022-11-25 16:15:34.561576 [DEBUG] Lua: Uninitializing JIT Compiler...
2022-11-26 08:56:22.021585 [DEBUG] Adding fmt library... 2022-11-26 08:56:22.027984 [DEBUG] Adding generator for library D:/Projects/NervProj/libraries/windows_clang/fmt-9.1.1/lib/fmt.lib 2022-11-26 08:56:22.028052 [DEBUG] Loading module from tests/hello_world_v1.cpp 2022-11-26 08:56:22.074953 [INFO] Hello world from JIT function with LogManager. 2022-11-26 08:56:22.074993 [DEBUG] Done running app.
#include <core_common.h> #include <vulkan_common.h> #include <base/RefPtr.h> #include <base/VulkanCommandBuffer.h> #include <base/VulkanFence.h> #include <base/VulkanFramebuffer.h> #include <base/VulkanPipeline.h> #include <base/VulkanPipelineCache.h> #include <base/VulkanPipelineLayout.h> #include <base/VulkanRenderPass.h> #include <base/VulkanSemaphore.h> #include <engine/VulkanRenderer.h> #include <engine/VulkanVertexBuffer.h> #include <vulkan_wrappers.h> using namespace nv; namespace nvk { class MyCmdBuffersProvider : public CmdBuffersProvider { NV_DECLARE_NO_COPY(MyCmdBuffersProvider) NV_DECLARE_NO_MOVE(MyCmdBuffersProvider) public: MyCmdBuffersProvider(){}; MyCmdBuffersProvider(VulkanRenderer* renderer, VulkanRenderPass* rpass, VulkanVertexBuffer* vbuf, VulkanPipelineLayout* playout, VulkanGraphicsPipelineCreateInfo* cfg, VulkanPipelineCache* pcache, const VulkanCommandBufferList& cbufs, const nv::ByteArray& pushArr){}; ~MyCmdBuffersProvider() override{}; void get_buffers(FrameDesc& fdesc, VkCommandBufferList& buffers) override; // auto get_push_constants() -> nv::ByteArray* { return &_pushArr; } protected: nv::RefPtr<VulkanRenderer> _renderer; nv::RefPtr<VulkanPipeline> _pipeline; nv::RefPtr<VulkanPipelineCache> _pipelineCache; nv::RefPtr<VulkanRenderPass> _rpass; nv::RefPtr<VulkanVertexBuffer> _vbuf; nv::RefPtr<VulkanPipelineLayout> _playout; nv::RefPtr<VulkanGraphicsPipelineCreateInfo> _cfg; VulkanCommandBufferList _cbufs; nv::ByteArray _pushArr; U32 _width{0}; U32 _height{0}; }; // MyCmdBuffersProvider::MyCmdBuffersProvider( // VulkanRenderer* renderer, VulkanRenderPass* rpass, VulkanVertexBuffer* // vbuf, VulkanPipelineLayout* playout, VulkanGraphicsPipelineCreateInfo* // cfg, VulkanPipelineCache* pcache, const VulkanCommandBufferList& cbufs, // const nv::ByteArray& pushArr) // : _renderer(renderer), _pipelineCache(pcache), _rpass(rpass), // _vbuf(vbuf), // _playout(playout), _cfg(cfg), _cbufs(cbufs), _pushArr(pushArr) {} void MyCmdBuffersProvider::get_buffers(FrameDesc& fdesc, VkCommandBufferList& buffers) { // Write the command buffer: U32 idx = fdesc.swapchainImageIndex; // Re-record the command buffer as above: auto* cbuf = _cbufs[idx].get(); auto* fbuf = _renderer->get_swapchain_framebuffer(idx); // We update our push constants here to contain a time value: F32 time = (F32)fdesc.frameTime; // logDEBUG("Writing time value: {}", time); // We write the time as the z element of the first vec4: _pushArr.write_f32(time, 8); // Push constants stages : U32 pstages = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT; U32 width = _renderer->get_swapchain_width(); U32 height = _renderer->get_swapchain_height(); // Check if we need to rebuild the pipeline: if (_width != width || _height != height) { _cfg->getCurrentViewportState()->setViewport((float)width, (float)height); _pipeline = _renderer->get_device()->create_graphics_pipeline( _cfg->getVk(), _pipelineCache->getVk()); _width = width; _height = height; } fbuf->set_clear_color(0, 0.2, 0.2, 0.2, 1.0); cbuf->begin(0); // Begin rendering into the swapchain framebuffer: cbuf->begin_inline_pass(_rpass.get(), fbuf); // Bind the graphics pipeline: cbuf->push_bind_graphics_pipeline(_pipeline->getVk()); // Bind the vertex buffer: cbuf->bind_vertex_buffer(_vbuf.get(), 0); // add the push constants cbuf->write_push_contants(_playout->getVk(), pstages, 0, _pushArr.get_size(), _pushArr.get_data()); // Draw our triangle: cbuf->draw(3); // End the render pass cbuf->end_render_pass(); // Finish the command buffer: cbuf->finish(); // Add the buffer to the list: buffers.push_back(cbuf->getVk()); } } // namespace nvk extern "C" void helloWorld() { // nv::LogManager::debug("Hello world from JIT function with LogManager."); logDEBUG("Hello world from JIT function with LogManager."); nv::RefPtr<nvk::MyCmdBuffersProvider> obj = new nvk::MyCmdBuffersProvider(); logDEBUG("Created object."); // logINFO("Hello world from JIT function with LogManager."); };
2022-11-25 18:50:20.097813 [DEBUG] Loading module from tests/cmd_buf_prov_v1.cpp 2022-11-25 18:50:20.098411 [DEBUG] Generating bytecode file D:/Projects/NervLand/dist/compiled/3c3bfaf5dae61d7dedccfcf21789db4b1b2c497fd5ab0b2a2dccb88ff8f5061d.bc... 2022-11-25 18:50:23.813658 [DEBUG] Bitcode compiled in 3715.204ms LLVM ERROR: Associative COMDAT symbol '??_7MyCmdBuffersProvider@nvk@@6B@' is not a key for its COMDAT.
#include <core_common.h> #include <vulkan_common.h> #include <base/RefPtr.h> #include <base/VulkanCommandBuffer.h> #include <base/VulkanFence.h> #include <base/VulkanFramebuffer.h> #include <base/VulkanPipeline.h> #include <base/VulkanPipelineCache.h> #include <base/VulkanPipelineLayout.h> #include <base/VulkanRenderPass.h> #include <base/VulkanSemaphore.h> #include <engine/VulkanRenderer.h> #include <engine/VulkanVertexBuffer.h> #include <vulkan_wrappers.h> using namespace nv; namespace nvk { class MyTestClass { NV_DECLARE_NO_COPY(MyTestClass) NV_DECLARE_NO_MOVE(MyTestClass) public: explicit MyTestClass(U32 val) : _value(val){}; ~MyTestClass() = default; auto get_value() -> U32 { return _value; } protected: U32 _value{0}; }; } // namespace nvk extern "C" void helloWorld() { // nv::LogManager::debug("Hello world from JIT function with LogManager."); logDEBUG("Hello world from JIT function with LogManager."); nvk::MyTestClass obj(42); std::cout << "Meaning of life is: " << obj.get_value() << "!" << std::endl; // logDEBUG("Meaning of life is: {}", obj.get_value()); // logINFO("Hello world from JIT function with LogManager."); };
#include <core_common.h> #include <vulkan_common.h> #include <base/RefPtr.h> #include <base/VulkanCommandBuffer.h> #include <base/VulkanFence.h> #include <base/VulkanFramebuffer.h> #include <base/VulkanPipeline.h> #include <base/VulkanPipelineCache.h> #include <base/VulkanPipelineLayout.h> #include <base/VulkanRenderPass.h> #include <base/VulkanSemaphore.h> #include <engine/VulkanRenderer.h> #include <engine/VulkanVertexBuffer.h> #include <vulkan_wrappers.h> using namespace nv; namespace nvk { class MyTestClass : public RefObject { NV_DECLARE_NO_COPY(MyTestClass) NV_DECLARE_NO_MOVE(MyTestClass) public: explicit MyTestClass(U32 val) : _value(val){}; ~MyTestClass() override { logDEBUG("Destroying test object."); }; auto get_value() const -> U32 { return _value; } protected: U32 _value{0}; }; } // namespace nvk extern "C" void helloWorld() { // nv::LogManager::debug("Hello world from JIT function with LogManager."); logDEBUG("Hello world from JIT function with LogManager."); nv::RefPtr<nvk::MyTestClass> obj = new nvk::MyTestClass(42); std::cout << "Meaning of life is: " << obj->get_value() << "!" << std::endl; // logDEBUG("Meaning of life is: {}", obj.get_value()); // logINFO("Hello world from JIT function with LogManager."); };
2022-11-25 18:54:46.771240 [DEBUG] Parsed args: {} 2022-11-25 18:54:46.771299 [DEBUG] Adding fmt library... 2022-11-25 18:54:46.771309 [DEBUG] Loading module from tests/hello_world_v2.cpp 2022-11-25 18:54:46.771670 [DEBUG] Generating bytecode file D:/Projects/NervLand/dist/compiled/ed1caf0a470bc67c84bf94e9305dd9afc2866f93ed055b3cf8b07d487e386a99.bc... 2022-11-25 18:54:50.740277 [DEBUG] Bitcode compiled in 3968.571ms LLVM ERROR: Associative COMDAT symbol '??_7MyTestClass@nvk@@6B@' is not a key for its COMDAT.
2022-11-25 19:17:29.210786 [DEBUG] Parsed args: {} 2022-11-25 19:17:29.210832 [DEBUG] Loading module from tests/hello_world_v2.cpp JIT session error: Symbols not found: [ ??_7type_info@@6B@ ] 2022-11-25 19:17:29.250670 [FATAL] Error in lua app: C++ exception 2022-11-25 19:17:29.250698 [DEBUG] Destroying NervApp...
// we just export the symbols we need from here: #include <sstream> #include <vector> // Helper module used to re-export the missing symbols that may be needed in // LLVM JIT code. #pragma comment(linker, "/export:??3@YAXPEAX_K@Z") #pragma comment(linker, "/export:??2@YAPEAX_K@Z") #pragma comment(linker, "/export:??3@YAXPEAX@Z") #pragma comment(linker, "/export:??_7type_info@@6B@") #pragma comment(linker, "/export:_Init_thread_header") #pragma comment(linker, "/export:_Init_thread_footer") #pragma comment(linker, "/export:_Init_thread_abort") #pragma comment(linker, "/export:_tls_index") #pragma comment(linker, "/export:_Init_thread_epoch") #pragma comment(linker, "/export:?_Facet_Register@std@@YAXPEAV_Facet_base@1@@Z") #pragma comment(linker, "/export:??2@YAPEAX_KAEBUnothrow_t@std@@@Z") #pragma comment(linker, "/export:?nothrow@std@@3Unothrow_t@1@B") // #pragma comment(linker, "/export:atexit") #pragma comment(linker, "/export:__security_check_cookie") // #pragma comment(linker, "/export:__security_cookie") #pragma comment(linker, "/export:?__type_info_root_node@@3U__type_info_node@@A") #pragma comment(linker, "/export:??_V@YAXPEAX@Z")/
JIT session error: Symbols not found: [ ?write_push_contants@VulkanCommandBuffer@nvk@@QEAAXPEAUVkPipelineLayout_T@@IIIPEBX@Z, ??0CmdBuffersProvider@nvk@@QEAA@XZ, ?set_clear_color@VulkanFramebuffer@nvk@@QEAA?A?<auto>@@IMMMM@Z, ?setViewport@VulkanPipelineViewportStateCreateInfo@nvk@@QEAAAEAU12@MMMMMM@Z, ??1CmdBuffersProvider@nvk@@UEAA@XZ, ?push_bind_graphics_pipeline@VulkanCommandBuffer@nvk@@QEAAXPEAUVkPipeline_T@@@Z, ?get_swapchain_width@VulkanRenderer@nvk@@QEBAIXZ, ?get_swapchain_height@VulkanRenderer@nvk@@QEBAIXZ, ?get_swapchain_framebuffer@VulkanRenderer@nvk@@QEAAPEAVVulkanFramebuffer@2@I@Z, ?get_device@VulkanRenderer@nvk@@QEBAPEAVVulkanDevice@2@XZ, ?begin@VulkanCommandBuffer@nvk@@QEAAXI@Z, ?begin_inline_pass@VulkanCommandBuffer@nvk@@QEAAXPEAVVulkanRenderPass@2@PEAVVulkanFramebuffer@2@HHII@Z, ?bind_vertex_buffer@VulkanCommandBuffer@nvk@@QEAAXPEAVVulkanVertexBuffer@2@I@Z, ?create_graphics_pipeline@VulkanDevice@nvk@@QEAA?AV?$RefPtr@VVulkanPipeline@nvk@@@nv@@AEBUVkGraphicsPipelineCreateInfo@@PEAUVkPipelineCache_T@@@Z, ?getVk@VulkanPipelineLayout@nvk@@QEBAPEAUVkPipelineLayout_T@@XZ, ?draw@VulkanCommandBuffer@nvk@@QEAAXIIII@Z, ?end_render_pass@VulkanCommandBuffer@nvk@@QEAAXXZ, ?finish@VulkanCommandBuffer@nvk@@QEAAXXZ, ?getVk@VulkanPipelineCache@nvk@@QEBAPEAUVkPipelineCache_T@@XZ, ?getCurrentViewportState@VulkanGraphicsPipelineCreateInfo@nvk@@QEAAAEAV?$RefPtr@UVulkanPipelineViewportStateCreateInfo@nvk@@@nv@@XZ, ?getVk@VulkanCommandBuffer@nvk@@QEBAPEAUVkCommandBuffer_T@@XZ, ?getVk@VulkanGraphicsPipelineCreateInfo@nvk@@QEBAAEAUVkGraphicsPipelineCreateInfo@@XZ, ?getVk@VulkanPipeline@nvk@@QEBAPEAUVkPipeline_T@@XZ ] 2022-11-25 19:41:20.403431 [INFO] Closing lua state...
extern "C" void helloWorld() { // nv::LogManager::debug("Hello world from JIT function with LogManager."); logDEBUG("Hello world from JIT function with LogManager."); auto& lman = LuaManager::instance(); auto& L = lman.get_main_state(); L.new_table(); // Create new table. L.push_int(1); // push key L.push_string("hello"); // push value L.raw_set(-3); // set k=val in table at -3 and pop key & val L.push_int(2); // push key L.push_string("manu"); // push value L.raw_set(-3); // set k=val in table at -3 and pop key & val L.set_global("jit_test"); nv::RefPtr<nvk::MyCmdBuffersProvider> obj = new nvk::MyCmdBuffersProvider(); logDEBUG("Created object."); // logINFO("Hello world from JIT function with LogManager."); };
logDEBUG("Loading module from tests/cmd_buf_prov_v1.cpp") jit:addModuleFromFile("tests/cmd_buf_prov_v1.cpp") CHECK(jit_test == nil, "Unexpected non nil") jit:execute("helloWorld") CHECK(jit_test[1] == "hello", "Unexpected value 1") CHECK(jit_test[2] == "manu", "Unexpected value 2") logDEBUG("Message: ", jit_test[1], " ", jit_test[2]) logDEBUG("Done running app.")
2022-11-25 23:31:01.655741 [DEBUG] Loading vulkan extensions... 2022-11-25 23:31:01.655817 [DEBUG] Loading module from tests/cmd_buf_prov_v1.cpp 2022-11-25 23:31:01.758399 [DEBUG] Hello world from JIT function with LogManager. 2022-11-25 23:31:01.758444 [DEBUG] Created object. 2022-11-25 23:31:01.758462 [DEBUG] Message: hello manu 2022-11-25 23:31:01.758479 [DEBUG] Done running app.