Add 'xo-jit/' from commit '855887df71'
git-subtree-dir: xo-jit git-subtree-mainline:35555df976git-subtree-split:855887df71
This commit is contained in:
commit
757dfed99c
49 changed files with 7305 additions and 0 deletions
83
xo-jit/include/xo/jit/IrPipeline.hpp
Normal file
83
xo-jit/include/xo/jit/IrPipeline.hpp
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/** @file IrPipeline.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/refcnt/Refcounted.hpp"
|
||||
#include "LlvmContext.hpp"
|
||||
|
||||
/* stuff from kaleidoscope.cpp */
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# include "llvm/ADT/APFloat.h"
|
||||
# include "llvm/ADT/STLExtras.h"
|
||||
# include "llvm/IR/BasicBlock.h"
|
||||
# include "llvm/IR/Constants.h"
|
||||
# include "llvm/IR/DerivedTypes.h"
|
||||
# include "llvm/IR/Function.h"
|
||||
# include "llvm/IR/IRBuilder.h"
|
||||
# include "llvm/IR/LLVMContext.h"
|
||||
# include "llvm/IR/Module.h"
|
||||
# include "llvm/IR/PassManager.h"
|
||||
# include "llvm/IR/Type.h"
|
||||
# include "llvm/IR/Verifier.h"
|
||||
# include "llvm/Passes/PassBuilder.h"
|
||||
# include "llvm/Passes/StandardInstrumentations.h"
|
||||
# include "llvm/Support/TargetSelect.h"
|
||||
# include "llvm/Target/TargetMachine.h"
|
||||
# include "llvm/Transforms/InstCombine/InstCombine.h"
|
||||
# include "llvm/Transforms/Scalar.h"
|
||||
# include "llvm/Transforms/Scalar/GVN.h"
|
||||
# include "llvm/Transforms/Utils/Mem2Reg.h"
|
||||
# include "llvm/Transforms/Scalar/Reassociate.h"
|
||||
# include "llvm/Transforms/Scalar/SimplifyCFG.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
/** @class IrPipeline
|
||||
* @brief represent an LLVM IR pipeline
|
||||
*
|
||||
* Represents analysis/transformation short of generating
|
||||
* machine-code. For now pipeline stages are hardwired;
|
||||
* adapted from the LLVM Kaleidoscope example project.
|
||||
*
|
||||
* Conversely, pipeline *starts* with code already that has
|
||||
* already been expressed in LLVM IR
|
||||
**/
|
||||
class IrPipeline : public ref::Refcount {
|
||||
public:
|
||||
explicit IrPipeline(rp<LlvmContext> llvm_cx);
|
||||
|
||||
void run_pipeline(llvm::Function & fn);
|
||||
|
||||
private:
|
||||
// ----- transforms (also adapted from kaleidescope.cpp) ------
|
||||
|
||||
/** keepalive for contained llvm::LLVMContext **/
|
||||
rp<LlvmContext> llvm_cx_;
|
||||
|
||||
/** manages all the passes+analaysis (?) **/
|
||||
std::unique_ptr<llvm::FunctionPassManager> llvm_fpmgr_;
|
||||
/** loop analysis (?) **/
|
||||
std::unique_ptr<llvm::LoopAnalysisManager> llvm_lamgr_;
|
||||
/** function-level analysis (?) **/
|
||||
std::unique_ptr<llvm::FunctionAnalysisManager> llvm_famgr_;
|
||||
/** cgscc (?) analysis **/
|
||||
std::unique_ptr<llvm::CGSCCAnalysisManager> llvm_cgamgr_;
|
||||
/** module analsyis (?) **/
|
||||
std::unique_ptr<llvm::ModuleAnalysisManager> llvm_mamgr_;
|
||||
/** pass instrumentation **/
|
||||
std::unique_ptr<llvm::PassInstrumentationCallbacks> llvm_pic_;
|
||||
/** standard instrumentation **/
|
||||
std::unique_ptr<llvm::StandardInstrumentations> llvm_si_;
|
||||
}; /*IrPipeline*/
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end IrPipeline.hpp **/
|
||||
171
xo-jit/include/xo/jit/Jit.hpp
Normal file
171
xo-jit/include/xo/jit/Jit.hpp
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/** @file Jit.hpp **/
|
||||
|
||||
/** Adapted from LLVM KaleidoscopeJIT.h **/
|
||||
|
||||
#pragma once
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# include "llvm/ADT/StringRef.h"
|
||||
# include "llvm/ExecutionEngine/JITSymbol.h"
|
||||
# include "llvm/ExecutionEngine/Orc/CompileUtils.h"
|
||||
# include "llvm/ExecutionEngine/Orc/Core.h"
|
||||
# include "llvm/ExecutionEngine/Orc/ExecutionUtils.h"
|
||||
# include "llvm/ExecutionEngine/Orc/ExecutorProcessControl.h"
|
||||
# include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
|
||||
# include "llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h"
|
||||
# include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
|
||||
# include "llvm/ExecutionEngine/Orc/Shared/ExecutorSymbolDef.h" // need llvm18
|
||||
# include "llvm/ExecutionEngine/SectionMemoryManager.h"
|
||||
# include "llvm/IR/DataLayout.h"
|
||||
# include "llvm/IR/LLVMContext.h"
|
||||
#pragma GCC diagnostic pop
|
||||
#include <memory>
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
|
||||
class Jit {
|
||||
private:
|
||||
using StringRef = llvm::StringRef;
|
||||
using SectionMemoryManager = llvm::SectionMemoryManager;
|
||||
using DynamicLibrarySearchGenerator = llvm::orc::DynamicLibrarySearchGenerator;
|
||||
using ConcurrentIRCompiler = llvm::orc::ConcurrentIRCompiler;
|
||||
using ExecutionSession = llvm::orc::ExecutionSession;
|
||||
using DataLayout = llvm::DataLayout;
|
||||
using MangleAndInterner = llvm::orc::MangleAndInterner;
|
||||
using RTDyldObjectLinkingLayer = llvm::orc::RTDyldObjectLinkingLayer;
|
||||
using IRCompileLayer = llvm::orc::IRCompileLayer;
|
||||
using JITDylib = llvm::orc::JITDylib;
|
||||
using JITTargetMachineBuilder = llvm::orc::JITTargetMachineBuilder;
|
||||
using ThreadSafeModule = llvm::orc::ThreadSafeModule;
|
||||
using ResourceTrackerSP = llvm::orc::ResourceTrackerSP;
|
||||
using ExecutorSymbolDef = llvm::orc::ExecutorSymbolDef;
|
||||
using SelfExecutorProcessControl = llvm::orc::SelfExecutorProcessControl;
|
||||
|
||||
private:
|
||||
/** execution session - represents a currently-running jit program **/
|
||||
std::unique_ptr<ExecutionSession> xsession_;
|
||||
|
||||
/** (?) needed for name mangling (?) **/
|
||||
DataLayout data_layout_;
|
||||
/** symbol mangling and unique-ifying */
|
||||
MangleAndInterner mangler_;
|
||||
|
||||
/** in-process linking layer
|
||||
* (? specialized for jit in running process ?)
|
||||
**/
|
||||
RTDyldObjectLinkingLayer object_layer_;
|
||||
/** compilation layer (sits above linking layer) **/
|
||||
IRCompileLayer compile_layer_;
|
||||
|
||||
/** destination library **/
|
||||
JITDylib & dest_dynamic_lib_; //MainJD;
|
||||
|
||||
public:
|
||||
Jit(std::unique_ptr<ExecutionSession> xsession,
|
||||
JITTargetMachineBuilder jtmb,
|
||||
DataLayout data_layout)
|
||||
: xsession_{std::move(xsession)},
|
||||
data_layout_(std::move(data_layout)),
|
||||
mangler_(*this->xsession_, this->data_layout_),
|
||||
object_layer_(*this->xsession_,
|
||||
[]() { return std::make_unique<SectionMemoryManager>(); }),
|
||||
compile_layer_(*this->xsession_, object_layer_,
|
||||
std::make_unique<ConcurrentIRCompiler>(std::move(jtmb))),
|
||||
dest_dynamic_lib_(this->xsession_->createBareJITDylib("<main>"))
|
||||
{
|
||||
dest_dynamic_lib_.addGenerator
|
||||
(cantFail(DynamicLibrarySearchGenerator::GetForCurrentProcess
|
||||
(data_layout_.getGlobalPrefix())));
|
||||
|
||||
if (jtmb.getTargetTriple().isOSBinFormatCOFF()) {
|
||||
object_layer_.setOverrideObjectFlagsWithResponsibilityFlags(true);
|
||||
object_layer_.setAutoClaimResponsibilityForObjectSymbols(true);
|
||||
}
|
||||
}
|
||||
|
||||
~Jit() {
|
||||
if (auto Err = this->xsession_->endSession())
|
||||
this->xsession_->reportError(std::move(Err));
|
||||
}
|
||||
|
||||
static llvm::Expected<std::unique_ptr<Jit>> Create() {
|
||||
auto EPC = SelfExecutorProcessControl::Create();
|
||||
if (!EPC)
|
||||
return EPC.takeError();
|
||||
|
||||
auto xsession = std::make_unique<ExecutionSession>(std::move(*EPC));
|
||||
|
||||
JITTargetMachineBuilder jtmb
|
||||
(xsession->getExecutorProcessControl().getTargetTriple());
|
||||
|
||||
auto data_layout = jtmb.getDefaultDataLayoutForTarget();
|
||||
if (!data_layout)
|
||||
return data_layout.takeError();
|
||||
|
||||
return std::make_unique<Jit>(std::move(xsession),
|
||||
std::move(jtmb),
|
||||
std::move(*data_layout));
|
||||
}
|
||||
|
||||
/* exposing this for printing */
|
||||
const ExecutionSession * xsession() const { return xsession_.get(); }
|
||||
const DataLayout & data_layout() const { return data_layout_; }
|
||||
|
||||
JITDylib & dest_dynamic_lib_ref() { return dest_dynamic_lib_; }
|
||||
const std::string & target_triple() const {
|
||||
return xsession_->getTargetTriple().getTriple();
|
||||
}
|
||||
|
||||
|
||||
/** compile module to machine code that's runnable from this process;
|
||||
* incorporate into @ref dest_dynamic_lib_
|
||||
**/
|
||||
llvm::Error
|
||||
add_llvm_module(ThreadSafeModule ts_module,
|
||||
ResourceTrackerSP rtracker = nullptr) {
|
||||
if (!rtracker)
|
||||
rtracker = dest_dynamic_lib_.getDefaultResourceTracker();
|
||||
|
||||
return compile_layer_.add(rtracker,
|
||||
std::move(ts_module));
|
||||
}
|
||||
|
||||
/** intern @p symbol, binding it to address @p dest **/
|
||||
template <typename T>
|
||||
llvm::Error intern_symbol(const std::string & symbol, T * dest) {
|
||||
auto mangled_sym = mangler_(symbol);
|
||||
|
||||
llvm::orc::SymbolMap symbol_map;
|
||||
symbol_map[mangled_sym]
|
||||
= llvm::orc::ExecutorSymbolDef(llvm::orc::ExecutorAddr::fromPtr(dest),
|
||||
llvm::JITSymbolFlags());
|
||||
|
||||
auto materializer = llvm::orc::absoluteSymbols(symbol_map);
|
||||
|
||||
return dest_dynamic_lib_.define(materializer);
|
||||
} /*intern_symbol*/
|
||||
|
||||
/** report mangled symbol name **/
|
||||
std::string_view mangle(StringRef name) {
|
||||
auto tmp = *(this->mangler_(name.str()));
|
||||
|
||||
return std::string_view(tmp.data(), tmp.size());
|
||||
}
|
||||
|
||||
llvm::Expected<ExecutorSymbolDef> lookup(StringRef name) {
|
||||
return this->xsession_->lookup({&dest_dynamic_lib_},
|
||||
this->mangle(name));
|
||||
}
|
||||
|
||||
/* dump */
|
||||
void dump_execution_session() {
|
||||
this->xsession_->dump(llvm::errs());
|
||||
}
|
||||
}; /*Jit*/
|
||||
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end Jit.hpp **/
|
||||
45
xo-jit/include/xo/jit/LlvmContext.hpp
Normal file
45
xo-jit/include/xo/jit/LlvmContext.hpp
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/** @file LlvmContext.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "xo/refcnt/Refcounted.hpp"
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# include "llvm/IR/LLVMContext.h"
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
//#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 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 **/
|
||||
281
xo-jit/include/xo/jit/MachPipeline.hpp
Normal file
281
xo-jit/include/xo/jit/MachPipeline.hpp
Normal file
|
|
@ -0,0 +1,281 @@
|
|||
/** @file MachPipeline.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
//#include <cstdint>
|
||||
|
||||
#include "xo/refcnt/Refcounted.hpp"
|
||||
#include "IrPipeline.hpp"
|
||||
#include "LlvmContext.hpp"
|
||||
#include "Jit.hpp"
|
||||
#include "activation_record.hpp"
|
||||
|
||||
#include "xo/expression/Expression.hpp"
|
||||
#include "xo/expression/ConstantInterface.hpp"
|
||||
#include "xo/expression/PrimitiveInterface.hpp"
|
||||
#include "xo/expression/Apply.hpp"
|
||||
#include "xo/expression/Lambda.hpp"
|
||||
#include "xo/expression/Variable.hpp"
|
||||
#include "xo/expression/IfExpr.hpp"
|
||||
#include "xo/expression/GlobalEnv.hpp"
|
||||
|
||||
/* stuff from kaleidoscope.cpp */
|
||||
#include "llvm/ADT/APFloat.h"
|
||||
#include "llvm/ADT/STLExtras.h"
|
||||
#include "llvm/IR/BasicBlock.h"
|
||||
#include "llvm/IR/Constants.h"
|
||||
#include "llvm/IR/DerivedTypes.h"
|
||||
#include "llvm/IR/Function.h"
|
||||
#include "llvm/IR/IRBuilder.h"
|
||||
#include "llvm/IR/LLVMContext.h"
|
||||
#include "llvm/IR/Module.h"
|
||||
#include "llvm/IR/PassManager.h"
|
||||
#include "llvm/IR/Type.h"
|
||||
#include "llvm/IR/Verifier.h"
|
||||
#include "llvm/Passes/PassBuilder.h"
|
||||
#include "llvm/Passes/StandardInstrumentations.h"
|
||||
#include "llvm/Support/TargetSelect.h"
|
||||
#include "llvm/Target/TargetMachine.h"
|
||||
#include "llvm/Transforms/InstCombine/InstCombine.h"
|
||||
#include "llvm/Transforms/Scalar.h"
|
||||
#include "llvm/Transforms/Scalar/GVN.h"
|
||||
#include "llvm/Transforms/Scalar/Reassociate.h"
|
||||
#include "llvm/Transforms/Scalar/SimplifyCFG.h"
|
||||
#include <llvm/ExecutionEngine/Orc/Core.h>
|
||||
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
/** @class MachPipeline
|
||||
* @brief just-in-time compiler for EGAD
|
||||
*
|
||||
* TODO: make module name a parameter?
|
||||
**/
|
||||
class MachPipeline : public ref::Refcount {
|
||||
public:
|
||||
using Expression = xo::ast::Expression;
|
||||
using Lambda = xo::ast::Lambda;
|
||||
using GlobalEnv = xo::ast::GlobalEnv;
|
||||
using TypeDescr = xo::reflect::TypeDescr;
|
||||
using ExecutionSession = llvm::orc::ExecutionSession;
|
||||
using DataLayout = llvm::DataLayout;
|
||||
//using ConstantInterface = xo::ast::ConstantInterface;
|
||||
|
||||
public:
|
||||
/* tracking KaleidoscopeJIT::Create() here.. */
|
||||
static llvm::Expected<std::unique_ptr<MachPipeline>> make_aux();
|
||||
static rp<MachPipeline> make();
|
||||
|
||||
// ----- access -----
|
||||
|
||||
llvm::Module * current_module() { return llvm_module_.get(); }
|
||||
ref::brw<LlvmContext> llvm_cx() { return llvm_cx_; }
|
||||
llvm::IRBuilder<> * llvm_current_ir_builder() { return llvm_toplevel_ir_builder_.get(); }
|
||||
|
||||
/** target triple = string describing target host for codegen **/
|
||||
const std::string & target_triple() const;
|
||||
/** execution session (run jit-generated machine code in this process) **/
|
||||
const ExecutionSession * xsession() const;
|
||||
/** data layout = rules for alignment/padding; specific to target host **/
|
||||
const DataLayout & data_layout() const;
|
||||
/** append function names defined in attached module to *p_v
|
||||
*
|
||||
* (RC 15jun2024 - this part is working)
|
||||
**/
|
||||
std::vector<std::string> get_function_name_v();
|
||||
|
||||
/** write state of execution session (all the associated dynamic libraries) **/
|
||||
void dump_execution_session();
|
||||
|
||||
// ----- code generation -----
|
||||
|
||||
/** establish llvm IR corresponding to a c++ type.
|
||||
* Handles
|
||||
* T := bool|char|short|int|long|float|double
|
||||
* | T1(*)(T2..Tn)
|
||||
* | struct{T1,..,Tn}
|
||||
*
|
||||
* Not supported yet:
|
||||
* - vector<T>
|
||||
* - string
|
||||
* - map<T1,T2>
|
||||
* - unions
|
||||
* - pointers (except function pointers)
|
||||
*
|
||||
* Idempotent: multiple calls with the same @p td produce the same @c llvm::Type pointer.
|
||||
* @c llvm::Type instances are *immortal* (llvm interns them into opaque global lookup tables)
|
||||
**/
|
||||
llvm::Type * codegen_type(TypeDescr td);
|
||||
llvm::Value * codegen_constant(ref::brw<xo::ast::ConstantInterface> expr);
|
||||
llvm::Function * codegen_primitive(ref::brw<xo::ast::PrimitiveInterface> expr);
|
||||
|
||||
/** like @ref codegen_primitive , but create wrapper function that accepts (and discards)
|
||||
* environment pointer as first argument.
|
||||
*
|
||||
* Implementation consists of tail call to natural primitive, that skips the unused
|
||||
* environment pointer
|
||||
**/
|
||||
llvm::Function * codegen_primitive_wrapper(ref::brw<xo::ast::PrimitiveInterface> expr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
|
||||
/** Generate closure for invoking a primitive function.
|
||||
* Primitives don't benefit from a closure, but we need a consistent ABI
|
||||
* to support function-pointer-like behavior for a target function
|
||||
* that may resolve to primitive-or-lambda at runtime
|
||||
**/
|
||||
llvm::Value * codegen_primitive_closure(ref::brw<xo::ast::PrimitiveInterface> expr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
|
||||
llvm::Value * codegen_apply(ref::brw<xo::ast::Apply> expr,
|
||||
llvm::Value * envptr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
/* NOTE: codegen_lambda() needs to be reentrant too.
|
||||
* for example can have a lambda in apply position.
|
||||
*/
|
||||
llvm::Function * codegen_lambda_decl(ref::brw<xo::ast::Lambda> expr);
|
||||
llvm::Function * codegen_lambda_defn(ref::brw<xo::ast::Lambda> expr, llvm::IRBuilder<> & ir_builder);
|
||||
/** Generate closure for invoking a lambda (user-defined function).
|
||||
* See @ref MachPipeline::codegen_apply for invocation
|
||||
* Same ABI as @ref MachPipeline::codegen_primitive_closure
|
||||
*
|
||||
* @param envptr. Environment from surrounding lexical scope.
|
||||
* This will be captured as envptr member by
|
||||
* the IR code for creating a closure.
|
||||
* @ref MachPipeline::codegen_toplevel and friends are responsible for
|
||||
* assembling and propagating this.
|
||||
**/
|
||||
llvm::Value * codegen_lambda_closure(ref::brw<xo::ast::Lambda> lambda,
|
||||
llvm::Value * envptr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
llvm::Value * codegen_variable(ref::brw<xo::ast::Variable> var,
|
||||
llvm::Value * envptr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
llvm::Value * codegen_ifexpr(ref::brw<xo::ast::IfExpr> ifexpr,
|
||||
llvm::Value * envptr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
|
||||
llvm::Value * codegen(ref::brw<Expression> expr,
|
||||
llvm::Value * envptr,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
|
||||
llvm::Value * codegen_toplevel(ref::brw<Expression> expr);
|
||||
|
||||
// ----- jit online execution -----
|
||||
|
||||
/** add IR code in current module to JIT,
|
||||
* so that its available for execution
|
||||
**/
|
||||
void machgen_current_module();
|
||||
|
||||
/** dump text description of module contents to console **/
|
||||
void dump_current_module();
|
||||
|
||||
/** report mangled symbol for @p x **/
|
||||
std::string_view mangle(const std::string & x) const;
|
||||
|
||||
/** lookup symbol in jit-associated output library **/
|
||||
llvm::Expected<llvm::orc::ExecutorAddr> lookup_symbol(const std::string & x);
|
||||
|
||||
virtual void display(std::ostream & os) const;
|
||||
virtual std::string display_string() const;
|
||||
|
||||
private:
|
||||
/** construct instance, adopting jit for compilation+execution **/
|
||||
explicit MachPipeline(std::unique_ptr<Jit> jit);
|
||||
|
||||
/** iniitialize native builder (i.e. for platform we're running on) **/
|
||||
static void init_once();
|
||||
|
||||
/** helper function. find all lambda expressions in AST @p expr **/
|
||||
std::vector<ref::brw<Lambda>> find_lambdas(ref::brw<Expression> expr) const;
|
||||
|
||||
public:
|
||||
/** codegen helper for a user-defined function.
|
||||
* create stack slot on behalf of formal parameters.
|
||||
* linked to (dynamic) callers for stack unwinding
|
||||
**/
|
||||
llvm::AllocaInst * create_entry_frame_alloca(llvm::Function * llvm_fn,
|
||||
llvm::StructType * frame_llvm_type);
|
||||
|
||||
#ifdef OBSOLETE // see activation_record::create_entry_block_alloca()
|
||||
/** codegen helper for a user-defined function (codegen_lambda()):
|
||||
* create stack slot on behalf of some formal parameter to a function,
|
||||
* so we can avoid SSA restriction on function body
|
||||
*
|
||||
* @p var_type. variable type
|
||||
**/
|
||||
llvm::AllocaInst * create_entry_block_alloca(llvm::Function * llvm_fn,
|
||||
const std::string & var_name,
|
||||
TypeDescr var_type);
|
||||
#endif
|
||||
|
||||
private:
|
||||
/** (re)create pipeline to turn expressions into llvm IR code **/
|
||||
void recreate_llvm_ir_pipeline();
|
||||
|
||||
private:
|
||||
// ----- this part adapted from LLVM 19.0 KaleidoscopeJIT.hpp [wip] -----
|
||||
|
||||
/** just-in-time compiler -- construct machine code that can
|
||||
* be invoked from this running process
|
||||
**/
|
||||
std::unique_ptr<Jit> jit_;
|
||||
|
||||
// ----- this part adapted from kaleidoscope.cpp -----
|
||||
|
||||
/** everything below represents a pipeline
|
||||
* that takes expressions, and turns them into llvm IR.
|
||||
*
|
||||
* llvm IR can be added to running JIT by calling
|
||||
* jit_->addModule()
|
||||
* Note that this makes the module itself unavailable to us
|
||||
**/
|
||||
rp<IrPipeline> ir_pipeline_;
|
||||
|
||||
/** owns + manages core "global" llvm data,
|
||||
* including type- and constant- unique-ing tables.
|
||||
*
|
||||
* Not threadsafe, but ok to have multiple threads,
|
||||
* each with its own LLVMContext
|
||||
**/
|
||||
rp<LlvmContext> llvm_cx_;
|
||||
|
||||
/** builder for intermediate-representation objects **/
|
||||
std::unique_ptr<llvm::IRBuilder<>> llvm_toplevel_ir_builder_;
|
||||
|
||||
/** a module (1:1 with library ?) being prepared by llvm.
|
||||
* IR-level -- does not contain machine coode
|
||||
*
|
||||
* - function names are unique within a module.
|
||||
**/
|
||||
std::unique_ptr<llvm::Module> llvm_module_;
|
||||
|
||||
/** map global names to functions/variables **/
|
||||
rp<GlobalEnv> global_env_;
|
||||
|
||||
/** map variable names (formal parameters) to
|
||||
* corresponding llvm IR.
|
||||
*
|
||||
* only supports one level atm (i.e. only top-level functions)
|
||||
*
|
||||
* All values live on the stack, so that we can evade single-assignment
|
||||
* restrictions.
|
||||
*
|
||||
* rhs identifies logical stack location of a variable
|
||||
**/
|
||||
std::stack<activation_record> env_stack_; /* <-> kaleidoscope NamedValues */
|
||||
}; /*MachPipeline*/
|
||||
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const MachPipeline & x) {
|
||||
x.display(os);
|
||||
return os;
|
||||
}
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end MachPipeline.hpp **/
|
||||
221
xo-jit/include/xo/jit/activation_record.hpp
Normal file
221
xo-jit/include/xo/jit/activation_record.hpp
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
/** @file activation_record.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LlvmContext.hpp"
|
||||
#include "xo/expression/Lambda.hpp"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# include <llvm/IR/IRBuilder.h>
|
||||
# include <llvm/IR/Instructions.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#include <map>
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
/** analagous to xo::ast::binding_path,
|
||||
* but with locations renumbered to include only vars that belong to an explict runtime
|
||||
* environment object; in other words we exclude vars with stack-only storage
|
||||
**/
|
||||
struct runtime_binding_path {
|
||||
public:
|
||||
runtime_binding_path() = default;
|
||||
runtime_binding_path(int i_rt_link,
|
||||
int j_rt_slot)
|
||||
: i_rt_link_{i_rt_link}, j_rt_slot_{j_rt_slot} {}
|
||||
|
||||
static runtime_binding_path stackonly() {
|
||||
return runtime_binding_path(0, -1);
|
||||
}
|
||||
static runtime_binding_path local(int j_rt_slot) {
|
||||
return runtime_binding_path(0, j_rt_slot);
|
||||
}
|
||||
|
||||
bool is_stackonly() const { return (i_rt_link_ == 0) && (j_rt_slot_ == -1); }
|
||||
bool is_captured() const { return !is_stackonly(); }
|
||||
|
||||
public:
|
||||
/** nnumber of parent runtime env links to traverse. -1 if global. -2 if sentinel **/
|
||||
int i_rt_link_ = -2;
|
||||
/** >= 0: slot# within explicit runtime environment where this variable bound.
|
||||
* (local vars only -- ignored for global vars)
|
||||
* -1: stack-only parameter
|
||||
**/
|
||||
int j_rt_slot_ = 0;
|
||||
};
|
||||
|
||||
struct runtime_binding_detail {
|
||||
/** Formal index position for this formal parameter.
|
||||
* Index into @ref activation_record::binding_v_,
|
||||
* also for @ref Lambda::fn_arg
|
||||
**/
|
||||
int i_argno_ = -1;
|
||||
|
||||
/** instructions for establishing stack address of this variable
|
||||
* In practice will be either an AllocaInst (for non-captured variables),
|
||||
* or result of IRBuilder<>::CreateInBoundsGEP (for captured variables).
|
||||
**/
|
||||
llvm::Value * llvm_addr_ = nullptr;
|
||||
|
||||
/** llvm type associated with stack-allocated variable.
|
||||
* Determines (when combined with llvm::DataLayout) how much space
|
||||
* will be required for this particular variable
|
||||
**/
|
||||
llvm::Type * llvm_type_ = nullptr;
|
||||
};
|
||||
|
||||
inline std::ostream &
|
||||
operator<<(std::ostream & os, const runtime_binding_detail & x) {
|
||||
os << "<runtime_binding_detail"
|
||||
<< xtag("i_argno", x.i_argno_)
|
||||
<< xtag("llvm_addr", (void*)x.llvm_addr_)
|
||||
<< xtag("llvm_type", (void*)x.llvm_type_)
|
||||
<< ">";
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
/**
|
||||
* 1. pattern for a stack frame associated with a user-defined function (some Lambda lm)
|
||||
*
|
||||
* 2. each function needs its own IR builder, to keep track of things like insert point
|
||||
*
|
||||
* 3. simple case first.
|
||||
* if lm->needs_closure_flag() is false, then:
|
||||
*
|
||||
* a. still need a closure-shaped object, because when we invoke function, we may
|
||||
* not know until runtime whether it relies on closure.
|
||||
* For such function we will generate a closure with empty environment pointer.
|
||||
* b. all formal parameters of lm
|
||||
* are used only in the layer associated with that lambda's body; in particular
|
||||
* they aren't free in any nested lambda
|
||||
* c. conversely, the top layer of lm's body has no free variables.
|
||||
* The only variables that *do* appear are lm's formal parameters.
|
||||
*
|
||||
* In this case, all of lm's formals will be allocated on the stack using regular
|
||||
* allocInst, and we don't need a closure for lm.
|
||||
*
|
||||
* 4. complex case second
|
||||
* If lm->needs_closure_flag() is true, then either:
|
||||
*
|
||||
* a. at least one formal parameter of lm appears free in some nested lambda.
|
||||
* b. lambda's top layer itself contains one or more free variables.
|
||||
*
|
||||
* In either case we will create an explicit environment for lm,
|
||||
* containing all the variables needed by some nested lambda
|
||||
**/
|
||||
class activation_record {
|
||||
public:
|
||||
using Lambda = xo::ast::Lambda;
|
||||
using TypeDescr = xo::reflect::TypeDescr;
|
||||
|
||||
public:
|
||||
activation_record(const rp<Lambda> & lm);
|
||||
|
||||
const rp<Lambda> lambda() const { return lambda_; }
|
||||
|
||||
/** retrieve @c llvm::Value* representing the primary stack location
|
||||
* for formal parameter @p var_name
|
||||
**/
|
||||
const runtime_binding_detail * lookup_var(const std::string & var_name) const;
|
||||
|
||||
/** Remember allocation of a function variable on the stack
|
||||
*
|
||||
* @param var_name. formal parameter name
|
||||
* @param binding. address + supporting details for
|
||||
* primary (stack-allocated) storage for this variable
|
||||
**/
|
||||
const runtime_binding_detail * alloc_var(const std::string & var_name,
|
||||
const runtime_binding_detail & binding);
|
||||
|
||||
#ifdef NOT_USING
|
||||
llvm::AllocaInst * create_runtime_localenv_alloca(ref::brw<LlvmContext> llvm_cx,
|
||||
//const llvm::DataLayout & data_layout,
|
||||
llvm::Function * llvm_fn,
|
||||
llvm::IRBuilder<> & fn_ir_builder);
|
||||
#endif
|
||||
|
||||
runtime_binding_detail create_entry_block_alloca(ref::brw<LlvmContext> llvm_cx,
|
||||
//const llvm::DataLayout & data_layout,
|
||||
llvm::Function * llvm_fn,
|
||||
llvm::IRBuilder<> & fn_ir_builder,
|
||||
int i_arg,
|
||||
const std::string & var_name,
|
||||
TypeDescr var_td);
|
||||
|
||||
/** generate instructions that establish stacck location for a local-environment slot
|
||||
*
|
||||
* @param llvm_cx. handle for context -- manages storage for llvm::Types + related
|
||||
* @param localenv_llvm_type. describes contents of local environment
|
||||
* for a particular function. Same as @c localenv_alloca->getAllocatedType()
|
||||
* @param localenv_alloca. stack location for local environment
|
||||
* @param i_slot. 0-based slot number within local environment,
|
||||
* for which address is required
|
||||
* @param fn_ir_builder. insertion point for generated instructions
|
||||
* that compute target slot address (will be at/near top of function,
|
||||
* since we will copy captured function arguments to localenv,
|
||||
* then use the localenv copy exclusively.
|
||||
* @return value representing localenv slot address
|
||||
**/
|
||||
llvm::Value * runtime_localenv_slot_addr(ref::brw<LlvmContext> llvm_cx,
|
||||
llvm::StructType * localenv_llvm_type,
|
||||
llvm::AllocaInst * localenv_alloca,
|
||||
int i_slot,
|
||||
llvm::IRBuilder<> & fn_ir_builder);
|
||||
|
||||
/** establish storage for formal parameters on behalf of a new-but-empty
|
||||
* llvm function @p llvm_fn. Creates llvm IR instructions on function
|
||||
* entry that
|
||||
* 1. allocates stack space for function parameters.
|
||||
* 2. stores incoming parameters in that stack space.
|
||||
*
|
||||
* Strategy:
|
||||
* - for stackonly parameters, use individual @c llvm::AllocaInst instances
|
||||
* - create custom @c llvm::StructType for captured parameters, also initially stack-allocated
|
||||
**/
|
||||
bool bind_locals(ref::brw<LlvmContext> llvm_cx,
|
||||
//const llvm::DataLayout & data_layout,
|
||||
llvm::Function * llvm_fn,
|
||||
llvm::IRBuilder<> & ir_builder);
|
||||
|
||||
private:
|
||||
/** this activation record created on behalf of a call to @ref lambda_.
|
||||
* @ref Variable::path_ specifies a logical path to a variable,
|
||||
* but does not distinguish stack-native variables from variables in explicit
|
||||
* runtime environment records.
|
||||
*
|
||||
**/
|
||||
rp<Lambda> lambda_;
|
||||
|
||||
/** @c binding_v_[i] specifies how/where we mean to navigate to
|
||||
* location for formal parameter number *i* of @ref lambda_.
|
||||
**/
|
||||
std::vector<runtime_binding_path> binding_v_;
|
||||
|
||||
/** if this function requires an explicit environment,
|
||||
* gives stack location for that environment.
|
||||
**/
|
||||
llvm::AllocaInst * localenv_alloca_ = nullptr;
|
||||
|
||||
/** maps named slots in a stack frame to logical addresses.
|
||||
*
|
||||
* - For captured arguments: will refer to slot within stack-allocated local environment
|
||||
* (an llvm::StructType, created by type2llvm::create_localenv_llvm_type())
|
||||
*
|
||||
* - For non-captured arguments: will refer to stack-allocated argument copy
|
||||
*
|
||||
* In either case using copy-to-stack to evade directly confronting
|
||||
* so we don't have to comply with llvm IR's SSA requirement.
|
||||
**/
|
||||
std::map<std::string, runtime_binding_detail> frame_; /* <-> kaleidoscope NamedValues */
|
||||
}; /*activation_record*/
|
||||
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end activation_record.hpp **/
|
||||
66
xo-jit/include/xo/jit/activation_record.new.hpp
Normal file
66
xo-jit/include/xo/jit/activation_record.new.hpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
/** @file activation_record.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LlvmContext.hpp"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# include <llvm/IR/IRBuilder.h>
|
||||
# include <llvm/IR/Instructions.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#include <map>
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
/** scope for a stack frame associated with a user-defined function
|
||||
*
|
||||
* each function needs its own IR builder, to keep track of things like insert point
|
||||
**/
|
||||
class activation_record {
|
||||
public:
|
||||
activation_record(llvm::Function * llvm_fn,
|
||||
llvm::AllocaInst * frame) : frame_{frame} {
|
||||
int i_arg = 0;
|
||||
for (auto & arg : llvm_fn->args()) {
|
||||
std::string arg_name = std::string(arg.getName());
|
||||
|
||||
name2ix_map_[arg_name] = 2 + i_arg;
|
||||
}
|
||||
}
|
||||
|
||||
std::int32_t lookup_var(const std::string & var_name) const;
|
||||
|
||||
#ifdef OBSOLETE
|
||||
llvm::AllocaInst * lookup_var(const std::string & var_name) const;
|
||||
|
||||
llvm::AllocaInst * alloc_var(const std::string & var_name,
|
||||
llvm::AllocaInst * alloca);
|
||||
#endif
|
||||
|
||||
private:
|
||||
/** stack frame for a user-defined function (lambda) **/
|
||||
llvm::AllocaInst * frame_ = nullptr;
|
||||
|
||||
/** for each formal parameter,
|
||||
* reports its position in stack frame.
|
||||
* This is the position to use with getelementptr,
|
||||
* i.e. +2 to skip first two slots, that are reserved
|
||||
* for nextframe pointer (slot 0) + unwind pointer (slot 1)
|
||||
**/
|
||||
std::map<std::string, std::int32_t> name2ix_map_;
|
||||
|
||||
#ifdef OBSOLETE
|
||||
/** maps named slots in a stack frame to logical addresses **/
|
||||
std::map<std::string, llvm::AllocaInst*> frame_; /* <-> kaleidoscope NamedValues */
|
||||
#endif
|
||||
}; /*activation_record*/
|
||||
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end activation_record.hpp **/
|
||||
41
xo-jit/include/xo/jit/activation_record.orig.hpp
Normal file
41
xo-jit/include/xo/jit/activation_record.orig.hpp
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
/** @file activation_record.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LlvmContext.hpp"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
# include <llvm/IR/IRBuilder.h>
|
||||
# include <llvm/IR/Instructions.h>
|
||||
#pragma GCC diagnostic pop
|
||||
#include <map>
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
/** scope for a stack frame associated with a user-defined function
|
||||
*
|
||||
* each function needs its own IR builder, to keep track of things like insert point
|
||||
**/
|
||||
class activation_record {
|
||||
public:
|
||||
activation_record() = default;
|
||||
|
||||
llvm::AllocaInst * lookup_var(const std::string & var_name) const;
|
||||
|
||||
llvm::AllocaInst * alloc_var(const std::string & var_name,
|
||||
llvm::AllocaInst * alloca);
|
||||
|
||||
private:
|
||||
/** maps named slots in a stack frame to logical addresses **/
|
||||
std::map<std::string, llvm::AllocaInst*> frame_; /* <-> kaleidoscope NamedValues */
|
||||
}; /*activation_record*/
|
||||
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
||||
/** end activation_record.hpp **/
|
||||
13
xo-jit/include/xo/jit/intrinsics.hpp
Normal file
13
xo-jit/include/xo/jit/intrinsics.hpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/** @file intrinsics.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
extern "C" int32_t mul_i32(int32_t x, int32_t y);
|
||||
extern "C" double mul_f64(double x, double y);
|
||||
|
||||
/** end intrinsics.hpp **/
|
||||
228
xo-jit/include/xo/jit/type2llvm.hpp
Normal file
228
xo-jit/include/xo/jit/type2llvm.hpp
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
/** @file type2llvm.hpp
|
||||
*
|
||||
* Author: Roland Conybeare
|
||||
**/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "LlvmContext.hpp"
|
||||
#include "xo/expression/Lambda.hpp"
|
||||
#include "xo/reflect/TypeDescr.hpp"
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#pragma GCC diagnostic pop
|
||||
//#include <cstdint>
|
||||
|
||||
namespace xo {
|
||||
namespace jit {
|
||||
/**
|
||||
**/
|
||||
struct type2llvm {
|
||||
public:
|
||||
using FunctionInterface = xo::ast::FunctionInterface;
|
||||
using Lambda = xo::ast::Lambda;
|
||||
using TypeDescr = xo::reflect::TypeDescr;
|
||||
|
||||
public:
|
||||
/** establish suitable llvm representation for a c++ type (described by @p td)
|
||||
* llvm types are unique'd, at least within @p llvm_cx
|
||||
**/
|
||||
static llvm::Type * td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr td);
|
||||
|
||||
/** establish llvm representation for a function type
|
||||
* described by @p fn_td
|
||||
*
|
||||
* @param wrapper_flag If true, create function type for a wrapper
|
||||
* to be associated with a closure.
|
||||
* The wrapper accepts (and ignores) an envapi pointer as first argument.
|
||||
* Necessary to (for example) support function pointers that may refer
|
||||
* to either {primitive functions, functions-requiring-closures},
|
||||
* with choice deferred until runtime
|
||||
**/
|
||||
static llvm::FunctionType * function_td_to_lvtype(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
bool wrapper_flag = false);
|
||||
|
||||
/** establish llvm representation for a function-pointer type
|
||||
* described by @p fn_td
|
||||
*
|
||||
* @param wrapper_flag If true, create function type for a wrapper
|
||||
* to be associated with a closure.
|
||||
* The wrapper accepts (and ignores) an envapi pointer as first argument.
|
||||
* Necessary to (for example) support function pointers that may refer
|
||||
* to either {primitive functions, functions-requiring-closures},
|
||||
* with choice deferred until runtime
|
||||
**/
|
||||
static llvm::PointerType * function_td_to_llvm_fnptr_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
bool wrapper_flag);
|
||||
|
||||
/** establish llvm concrete representation for a closure.
|
||||
*
|
||||
* +-------+
|
||||
* [0] | o-------> fnptr T (*)(envptr, ...)
|
||||
* +-------+
|
||||
* [1] | o-------\
|
||||
* +-------+ |
|
||||
* |
|
||||
* |
|
||||
* v
|
||||
* +-------+
|
||||
* parent_env [0] | o-------> _env_api*
|
||||
* +-------+
|
||||
* unwind_fn [1] | o-------> env * (*)(env*, ctl)
|
||||
* +-------+
|
||||
*
|
||||
* @return struct type. typename will be @c c.foo for a function
|
||||
* (primitive or lambda) with name @c foo
|
||||
**/
|
||||
static llvm::StructType *
|
||||
create_closureapi_lvtype(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
xo::ref::brw<FunctionInterface> fn);
|
||||
|
||||
/** establish llvm abstract representation for a closure:
|
||||
* struct with
|
||||
* - [0] function pointer
|
||||
* - [1] runtime localenv pointer
|
||||
*
|
||||
* +-------+
|
||||
* | o---------> native function
|
||||
* +-------+
|
||||
* | o---------> runtime localenv
|
||||
* +-------+ (possibly nullptr)
|
||||
*
|
||||
* 1. for primitives, localenv will be null pointer
|
||||
* 2. for lambdas L with L->requires_closure_flag() = false,
|
||||
* localenv will also be null pointer
|
||||
* 3. for lambdas with L->requires_closure_flag() = true,
|
||||
*
|
||||
* localenv will (for lambdas requiring closures)
|
||||
* in practice be struct:
|
||||
*
|
||||
* ^
|
||||
* | parent
|
||||
* +-------+ |
|
||||
* parent_env [0] | o-------/
|
||||
* +-------+
|
||||
* unwind_fn [1] | o-------> env * (*)(env*, ctl)
|
||||
* +-------+
|
||||
* arg[i] [2+i] . ... .
|
||||
* . ... .
|
||||
* +-------+
|
||||
*
|
||||
* ctl=0 unwind. finalization for any arg[i] that requires it.
|
||||
* returns nullptr
|
||||
* ctl=1 copy. copy runtime environment to heap destination
|
||||
* and return address of the copy
|
||||
*
|
||||
* Implementation here will just use generic pointer for runtime
|
||||
* localenv.
|
||||
**/
|
||||
static llvm::StructType *
|
||||
function_td_to_closureapi_lvtype(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
const std::string & hint_name);
|
||||
|
||||
/** establish llvm concrete representation for a particular lambda's
|
||||
* runtime local environment:
|
||||
*
|
||||
* ^
|
||||
* | parent
|
||||
* +-------+ |
|
||||
* parent_env [0] | o-------/
|
||||
* +-------+
|
||||
* unwind_fn [1] | o-------> env * (*)(env*, ctl)
|
||||
* +-------+
|
||||
* arg[i] [2+i] . ... .
|
||||
* . ... .
|
||||
* +-------+
|
||||
*
|
||||
* ctl=0 unwind. finalization for any arg[i] that requires it.
|
||||
* returns nullptr
|
||||
* ctl=1 copy. copy runtime environment to heap destination
|
||||
* and return address of the copy
|
||||
*
|
||||
* arg[] comprises the subset of lambda arg names arg[j] for which
|
||||
* lambda->is_captured(arg[j]) is true
|
||||
*
|
||||
* @return struct type. typename will be @c e.foo for lambda with name @c foo
|
||||
**/
|
||||
static llvm::StructType *
|
||||
create_localenv_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
xo::ref::brw<Lambda> lambda);
|
||||
|
||||
/** establish llvm rep'n for a pointer to an abstract local environment:
|
||||
*
|
||||
* +-------+
|
||||
* | o-------------\
|
||||
* +-------+ |
|
||||
* |
|
||||
* |
|
||||
* |
|
||||
* v
|
||||
* +-------+
|
||||
* parent_env [0] | o-------> _env_api*
|
||||
* +-------+
|
||||
* unwind_fn [1] | o-------> env * (*)(env*, ctl)
|
||||
* +-------+
|
||||
**/
|
||||
static llvm::PointerType *
|
||||
env_api_llvm_ptr_type(xo::ref::brw<LlvmContext> llvm_cx);
|
||||
|
||||
/** function type:
|
||||
* @code
|
||||
* env_api_* (env_api* env, int ctl);
|
||||
* @endcode
|
||||
*
|
||||
* ctl=0 unwind. finalization for any arg[i] that requires it.
|
||||
* returns nullptr
|
||||
* ctl=1 copy. copy runtime environment to heap destination
|
||||
* and return address of the copy
|
||||
*
|
||||
* returns function-pointer type
|
||||
**/
|
||||
static llvm::PointerType *
|
||||
require_localenv_unwind_llvm_fnptr_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
llvm::PointerType * hint_envptr_llvm_type = nullptr);
|
||||
|
||||
private:
|
||||
|
||||
/** establish llvm representation for a struct type described by @p struct_td
|
||||
**/
|
||||
static llvm::StructType * struct_td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr struct_td);
|
||||
|
||||
/** establish llvm representation for a pointer type described by @p pointer_td **/
|
||||
static llvm::PointerType * pointer_td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr pointer_td);
|
||||
|
||||
/** establish llvm abstract representation for a local environment:
|
||||
*
|
||||
* ^
|
||||
* | parent
|
||||
* +-------+ |
|
||||
* parent_env [0] | o-------/
|
||||
* +-------+
|
||||
* unwind_fn [1] | o-------> env * (*)(env*, ctl)
|
||||
* +-------+
|
||||
*
|
||||
* ctl=0 unwind. finalization for any arg[i] that requires it.
|
||||
* returns nullptr
|
||||
* ctl=1 copy. copy runtime environment to heap destination
|
||||
* and return address of the copy
|
||||
*
|
||||
* Concrete implementation will probably occupy additional memory,
|
||||
* to store captured lambda variables.
|
||||
*
|
||||
* @see type2llvm::function_td_to_llvm_closure_type
|
||||
**/
|
||||
static llvm::StructType *
|
||||
env_api_llvm_type(xo::ref::brw<LlvmContext> llvm_cx);
|
||||
|
||||
}; /*type2llvm*/
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
/** end type2llvm.hpp **/
|
||||
Loading…
Add table
Add a link
Reference in a new issue