diff --git a/include/hobbes/eval/jitcc.H b/include/hobbes/eval/jitcc.H index d9b13b11..a24939fc 100644 --- a/include/hobbes/eval/jitcc.H +++ b/include/hobbes/eval/jitcc.H @@ -264,6 +264,8 @@ private: struct IrTracer; std::unique_ptr irTracer; + struct JitFuncTracer; + std::unique_ptr jitFuncTracer; }; // shorthand for compilation over a sequence of expressions diff --git a/lib/hobbes/eval/jitcc.C b/lib/hobbes/eval/jitcc.C index f18bb195..cc50305a 100644 --- a/lib/hobbes/eval/jitcc.C +++ b/lib/hobbes/eval/jitcc.C @@ -1,5 +1,4 @@ -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -21,10 +21,13 @@ #include #include +#include +#include #include #include #include #include +#include #include #pragma GCC diagnostic push @@ -569,11 +572,72 @@ struct jitcc::IrTracer { } void log(const llvm::Module& m) { - if (!isEnabled()) { + if (isEnabled()) { + m.print(*out, nullptr, false, true); + } + } + +private: + [[nodiscard]] bool isEnabled() const noexcept { + return !!out; + } + + std::unique_ptr out; +}; + +struct jitcc::JitFuncTracer { + JitFuncTracer() { + std::error_code ec; + const char* name = std::getenv("HOBBES_JIT_FUNC_TRACE_FILE"); + if (name == nullptr) { + return; + } + out = std::make_unique(name, ec, llvm::sys::fs::F_None); + if (ec != std::errc()) { + out = nullptr; return; } + } + +private: + struct ModuleFunctions { + ModuleFunctions() = default; + explicit ModuleFunctions(const llvm::Module& m) { + funcs.reserve(m.getFunctionList().size()); + for (const auto& f : m) { + if (!f.isDeclaration()) { + funcs.push_back(f.getName()); + } + } + } + + const std::vector& get() const noexcept { + return funcs; + } + + private: + std::vector funcs; + }; - m.print(*out, nullptr, false, true); +public: + ModuleFunctions getCollector(const llvm::Module& m) { + if (isEnabled()) { + return ModuleFunctions(m); + } + return {}; + } + + void log(const ModuleFunctions& mf, llvm::ExecutionEngine& ee) { + if (isEnabled()) { + for (const auto name : mf.get()) { + if (const auto a = ee.getFunctionAddress(name)) { + *out << name << ":0x"; + out->write_hex(a); + *out << '\n'; + } + } + out->flush(); + } } private: @@ -589,7 +653,8 @@ jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), vtenv(std::make_unique()), ignoreLocalScope(false), globals(std::make_unique()), globalData(32768 /* min global page size = 32K */), constants(std::make_unique()), - irTracer(std::make_unique()) + irTracer(std::make_unique()), + jitFuncTracer(std::make_unique()) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -620,7 +685,8 @@ jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), ignoreLocalScope(false), globalData(32768 /* min global page size = 32K */), - irTracer(std::make_unique()) + irTracer(std::make_unique()), + jitFuncTracer(std::make_unique()) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -786,6 +852,8 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) } irTracer->log(*this->currentModule); + const auto moduleFunctions = jitFuncTracer->getCollector(*this->currentModule); + // make a new execution engine out of this module (finalizing the module) std::string err; llvm::ExecutionEngine* ee = makeExecutionEngine(this->currentModule, reinterpret_cast(new jitmm(this))); @@ -839,6 +907,8 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) // and _now_ we must be able to get machine code for this function void* pf = ee->getPointerToFunction(f); + jitFuncTracer->log(moduleFunctions, *ee); + if (listener) { ee->UnregisterJITEventListener(listener); } @@ -1239,7 +1309,6 @@ void jitcc::defineGlobal(const std::string& vn, const ExprPtr& ue) { // compile and run this function, it should then perform the global variable assignment // (make sure that any allocation happens in the global context iff we need it) auto f = reinterpret_cast(getMachineCode(initfn)); - if (hasPointerRep(uety)) { size_t oldregion = pushGlobalRegion(); f();