xo-jit: + LlvmContext to keepalive native LLVMContext

This commit is contained in:
Roland Conybeare 2024-06-16 11:00:37 -04:00
commit a927d44e0e
7 changed files with 94 additions and 17 deletions

View file

@ -6,6 +6,7 @@
#pragma once
#include "xo/refcnt/Refcounted.hpp"
#include "LlvmContext.hpp"
/* stuff from kaleidoscope.cpp */
#include "llvm/ADT/APFloat.h"
@ -46,13 +47,16 @@ namespace xo {
**/
class IrPipeline : public ref::Refcount {
public:
explicit IrPipeline(llvm::LLVMContext & llvm_cx);
explicit IrPipeline(ref::rp<LlvmContext> llvm_cx);
void run_pipeline(llvm::Function & fn);
private:
// ----- transforms (also adapted from kaleidescope.cpp) ------
/** keepalive for contained llvm::LLVMContext **/
ref::rp<LlvmContext> llvm_cx_;
/** manages all the passes+analaysis (?) **/
std::unique_ptr<llvm::FunctionPassManager> llvm_fpmgr_;
/** loop analysis (?) **/

View file

@ -9,6 +9,7 @@
#include "xo/refcnt/Refcounted.hpp"
#include "IrPipeline.hpp"
#include "LlvmContext.hpp"
#include "xo/expression/Expression.hpp"
#include "xo/expression/ConstantInterface.hpp"
#include "xo/expression/PrimitiveInterface.hpp"
@ -31,7 +32,6 @@
#include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h"
#include "llvm/ExecutionEngine/SectionMemoryManager.h"
#include "llvm/IR/DataLayout.h"
#include "llvm/IR/LLVMContext.h"
#include <memory>
#endif
@ -159,7 +159,8 @@ namespace xo {
* Not threadsafe, but ok to have multiple threads,
* each with its own LLVMContext
**/
std::unique_ptr<llvm::LLVMContext> llvm_cx_;
ref::rp<LlvmContext> llvm_cx_;
//std::unique_ptr<llvm::LLVMContext> llvm_cx_;
/** builder for intermediate-representation objects **/
std::unique_ptr<llvm::IRBuilder<>> llvm_ir_builder_;
/** a module (1:1 with library) being prepared by llvm.

View file

@ -0,0 +1,40 @@
/** @file LlvmContext.hpp
*
* Author: Roland Conybeare
**/
#pragma once
#include "xo/refcnt/Refcounted.hpp"
#include "llvm/IR/LLVMContext.h"
//#include <cstdint>
namespace xo {
namespace jit {
/** @class LlvmContext
* @brief Keepalive for a llvm::LLVMContext instance.
*
* For example IrPipeline holds an rp<LlvmContext>
* to help ensure validity of embedded llvm::LLVMContext reference
**/
class LlvmContext : public ref::Refcount {
public:
static xo::ref::rp<LlvmContext> make();
llvm::LLVMContext & llvm_cx_ref() { return *llvm_cx_; }
std::unique_ptr<llvm::LLVMContext> & llvm_cx() { return llvm_cx_; }
private:
LlvmContext();
private:
/** Llvm context. Ties together fragments of code generation
* for AST subtrees that go into the same module.
**/
std::unique_ptr<llvm::LLVMContext> llvm_cx_;
}; /*LlvmContext*/
} /*namespace jit*/
} /*namespace xo*/
/** end LlvmContext.hpp **/

View file

@ -2,6 +2,7 @@
set(SELF_LIB xo_jit)
set(SELF_SRCS
LlvmContext.cpp
IrPipeline.cpp
Jit.cpp
)

View file

@ -4,17 +4,20 @@
namespace xo {
namespace jit {
IrPipeline::IrPipeline(llvm::LLVMContext & llvm_cx)
IrPipeline::IrPipeline(ref::rp<LlvmContext> llvm_cx)
{
using std::make_unique;
this->llvm_cx_ = std::move(llvm_cx);
this->llvm_fpmgr_ = make_unique<llvm::FunctionPassManager>();
this->llvm_lamgr_ = std::make_unique<llvm::LoopAnalysisManager>();
this->llvm_famgr_ = std::make_unique<llvm::FunctionAnalysisManager>();
this->llvm_cgamgr_ = std::make_unique<llvm::CGSCCAnalysisManager>();
this->llvm_mamgr_ = std::make_unique<llvm::ModuleAnalysisManager>();
this->llvm_pic_ = std::make_unique<llvm::PassInstrumentationCallbacks>();
this->llvm_si_ = std::make_unique<llvm::StandardInstrumentations>(llvm_cx,
/* reference kept alive by @ref llvm_cx_ */
this->llvm_si_ = std::make_unique<llvm::StandardInstrumentations>(llvm_cx_->llvm_cx_ref(),
/*DebugLogging*/ true);
this->llvm_si_->registerCallbacks(*llvm_pic_, llvm_mamgr_.get());

View file

@ -132,9 +132,10 @@ namespace xo {
void
Jit::recreate_llvm_ir_pipeline()
{
llvm_cx_ = std::make_unique<llvm::LLVMContext>();
llvm_ir_builder_ = std::make_unique<llvm::IRBuilder<>>(*llvm_cx_);
llvm_module_ = std::make_unique<llvm::Module>("xojit", *llvm_cx_);
//llvm_cx_ = std::make_unique<llvm::LLVMContext>();
llvm_cx_ = LlvmContext::make();
llvm_ir_builder_ = std::make_unique<llvm::IRBuilder<>>(llvm_cx_->llvm_cx_ref());
llvm_module_ = std::make_unique<llvm::Module>("xojit", llvm_cx_->llvm_cx_ref());
llvm_module_->setDataLayout(kal_jit_->getDataLayout());
@ -148,7 +149,7 @@ namespace xo {
throw std::runtime_error("Jit::ctor: expected non-empty llvm module");
}
ir_pipeline_ = new IrPipeline(*llvm_cx_);
ir_pipeline_ = new IrPipeline(llvm_cx_);
} /*recreate_llvm_ir_pipeline*/
const std::string &
@ -176,10 +177,10 @@ namespace xo {
TypeDescr td = expr->value_td();
if (td->is_native<double>()) {
return llvm::ConstantFP::get(*llvm_cx_,
return llvm::ConstantFP::get(llvm_cx_->llvm_cx_ref(),
llvm::APFloat(*(expr->value_tp().recover_native<double>())));
} else if (td->is_native<float>()) {
return llvm::ConstantFP::get(*llvm_cx_,
return llvm::ConstantFP::get(llvm_cx_->llvm_cx_ref(),
llvm::APFloat(*(expr->value_tp().recover_native<float>())));
}
@ -230,7 +231,7 @@ namespace xo {
log && log(xtag("i_arg", i), xtag("arg_td", arg_td->short_name()));
if (arg_td->is_native<double>()) {
llvm_argtype_v.push_back(llvm::Type::getDoubleTy(*llvm_cx_));
llvm_argtype_v.push_back(llvm::Type::getDoubleTy(llvm_cx_->llvm_cx_ref()));
// TODO: extend with other native types here...
} else {
@ -252,7 +253,7 @@ namespace xo {
llvm::Type * llvm_retval = nullptr;
if (retval_td->is_native<double>()) {
llvm_retval = llvm::Type::getDoubleTy(*llvm_cx_);
llvm_retval = llvm::Type::getDoubleTy(llvm_cx_->llvm_cx_ref());
} else {
cerr << "Jit::codegen_primitive: error: primitive f returning T where double expected"
<< xtag("f", expr->name())
@ -363,9 +364,9 @@ namespace xo {
// PLACEHOLDER
// just handle double arguments + return type for now
std::vector<llvm::Type *> double_v(1, llvm::Type::getDoubleTy(*llvm_cx_));
std::vector<llvm::Type *> double_v(1, llvm::Type::getDoubleTy(llvm_cx_->llvm_cx_ref()));
auto * llvm_fn_type = llvm::FunctionType::get(llvm::Type::getDoubleTy(*llvm_cx_),
auto * llvm_fn_type = llvm::FunctionType::get(llvm::Type::getDoubleTy(llvm_cx_->llvm_cx_ref()),
double_v,
false /*!varargs*/);
@ -381,7 +382,7 @@ namespace xo {
/* generate function body */
auto block = llvm::BasicBlock::Create(*llvm_cx_, "entry", fn);
auto block = llvm::BasicBlock::Create(llvm_cx_->llvm_cx_ref(), "entry", fn);
llvm_ir_builder_->SetInsertPoint(block);
@ -459,8 +460,15 @@ namespace xo {
auto tracker = kal_jit_->getMainJITDylib().createResourceTracker();
/* invalidates llvm_cx_->llvm_cx_ref(); will discard and re-create
*
* Note that @ref ir_pipeline_ holds reference, which is invalidated here
*/
auto ts_module = llvm::orc::ThreadSafeModule(std::move(llvm_module_),
std::move(llvm_cx_));
std::move(llvm_cx_->llvm_cx()));
/* note does not discard llvm_cx_->llvm_cx(), it's already been moved */
this->llvm_cx_ = nullptr;
llvm_exit_on_err(kal_jit_->addModule(std::move(ts_module), tracker));

20
src/jit/LlvmContext.cpp Normal file
View file

@ -0,0 +1,20 @@
/* @file LlvmContext.cpp */
#include "LlvmContext.hpp"
namespace xo {
namespace jit {
xo::ref::rp<LlvmContext>
LlvmContext::make() {
return new LlvmContext();
}
LlvmContext::LlvmContext()
: llvm_cx_{std::make_unique<llvm::LLVMContext>()}
{}
} /*namespace jit*/
} /*namespace xo*/
/* end LlvmContext.cpp */