xo-jit: use Environment for toplevel lambdas
This commit is contained in:
parent
71df4f824d
commit
1f0c0cb71d
4 changed files with 16 additions and 286 deletions
|
|
@ -20,6 +20,7 @@
|
|||
#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"
|
||||
|
|
@ -57,6 +58,7 @@ namespace xo {
|
|||
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;
|
||||
|
|
@ -211,7 +213,7 @@ namespace xo {
|
|||
std::unique_ptr<llvm::Module> llvm_module_;
|
||||
|
||||
/** map global names to functions/variables **/
|
||||
std::map<std::string, xo::ref::rp<Expression>> global_env_;
|
||||
ref::rp<GlobalEnv> global_env_;
|
||||
|
||||
/** map variable names (formal parameters) to
|
||||
* corresponding llvm IR.
|
||||
|
|
|
|||
|
|
@ -22,41 +22,16 @@ namespace xo {
|
|||
**/
|
||||
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());
|
||||
activation_record() = default;
|
||||
|
||||
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*/
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ namespace xo {
|
|||
using xo::ast::Variable;
|
||||
using xo::ast::Apply;
|
||||
using xo::ast::IfExpr;
|
||||
using xo::ast::GlobalEnv;
|
||||
using xo::ast::llvmintrinsic;
|
||||
using xo::reflect::Reflect;
|
||||
using xo::reflect::StructMember;
|
||||
|
|
@ -69,7 +70,8 @@ namespace xo {
|
|||
} /*make*/
|
||||
|
||||
MachPipeline::MachPipeline(std::unique_ptr<Jit> jit)
|
||||
: jit_{std::move(jit)}
|
||||
: jit_{std::move(jit)},
|
||||
global_env_{GlobalEnv::make()}
|
||||
{
|
||||
this->recreate_llvm_ir_pipeline();
|
||||
}
|
||||
|
|
@ -627,42 +629,6 @@ namespace xo {
|
|||
} /*create_entry_block_alloca*/
|
||||
|
||||
|
||||
/* in kaleidoscope7.cpp: CreateEntryBlockAlloca */
|
||||
llvm::AllocaInst *
|
||||
MachPipeline::create_entry_frame_alloca(llvm::Function * llvm_fn,
|
||||
llvm::StructType * frame_llvm_type)
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
using xo::scope;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag),
|
||||
xtag("llvm_fn", (void*)llvm_fn));
|
||||
|
||||
llvm::IRBuilder<> tmp_ir_builder(&llvm_fn->getEntryBlock(),
|
||||
llvm_fn->getEntryBlock().begin());
|
||||
|
||||
if (!frame_llvm_type)
|
||||
return nullptr;
|
||||
|
||||
if (log) {
|
||||
std::string llvm_frame_type_str;
|
||||
llvm::raw_string_ostream ss(llvm_frame_type_str);
|
||||
frame_llvm_type->print(ss);
|
||||
|
||||
log(xtag("frame_llvm_type", llvm_frame_type_str));
|
||||
}
|
||||
|
||||
llvm::AllocaInst * retval = tmp_ir_builder.CreateAlloca(frame_llvm_type,
|
||||
nullptr,
|
||||
llvm_fn->getName());
|
||||
|
||||
log && log(xtag("alloca", (void*)retval),
|
||||
xtag("align", retval->getAlign().value()),
|
||||
xtag("size", retval->getAllocationSize(jit_->data_layout()).value()));
|
||||
|
||||
return retval;
|
||||
} /*create_entry_frame_alloca*/
|
||||
|
||||
std::vector<ref::brw<Lambda>>
|
||||
MachPipeline::find_lambdas(ref::brw<Expression> expr) const
|
||||
{
|
||||
|
|
@ -688,7 +654,7 @@ namespace xo {
|
|||
scope log(XO_DEBUG(c_debug_flag),
|
||||
xtag("lambda-name", lambda->name()));
|
||||
|
||||
global_env_[lambda->name()] = lambda.get();
|
||||
this->global_env_->require_global(lambda->name(), lambda);
|
||||
|
||||
/* do we already know a function with this name? */
|
||||
auto * fn = llvm_module_->getFunction(lambda->name());
|
||||
|
|
@ -736,175 +702,6 @@ namespace xo {
|
|||
return fn;
|
||||
} /*codegen_lambda_decl*/
|
||||
|
||||
namespace {
|
||||
/** A function type:
|
||||
*
|
||||
* _baseframe* (*) (_baseframe*
|
||||
**/
|
||||
llvm::FunctionType *
|
||||
require_baseframe_unwind_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
llvm::PointerType * frameptr_llvm_type)
|
||||
{
|
||||
std::vector<llvm::Type *> llvm_argtype_v;
|
||||
llvm_argtype_v.reserve(2);
|
||||
|
||||
/* 1st arg is frame pointer */
|
||||
llvm_argtype_v.push_back(frameptr_llvm_type);
|
||||
|
||||
/* 2nd arg is an i32.
|
||||
* 0 -> unwind.
|
||||
* 1 -> lift to heap (someday)
|
||||
*/
|
||||
llvm_argtype_v.push_back
|
||||
(llvm::Type::getInt32Ty(llvm_cx->llvm_cx_ref()));
|
||||
|
||||
/* return value is frame pointer */
|
||||
llvm::Type * retval_llvm_type = frameptr_llvm_type;
|
||||
|
||||
auto * unwind_llvm_type = llvm::FunctionType::get(retval_llvm_type,
|
||||
llvm_argtype_v,
|
||||
false /*!varargs*/);
|
||||
|
||||
return unwind_llvm_type;
|
||||
} /*require_baseframe_unwind_llvm_type*/
|
||||
|
||||
/** Each lambda gets its own stack frame definition.
|
||||
* However all the various frame representations share the same 'baseframe'
|
||||
* prefix.
|
||||
*
|
||||
* _baseframe:
|
||||
* ^
|
||||
* |
|
||||
* +-------+ |
|
||||
* next_frame [0] | o-------/
|
||||
* +-------|
|
||||
* unwind_fn [1] | o-------> frame* (*)(frame*, ctl)
|
||||
* +-------+
|
||||
*
|
||||
* This helper function generates an llvm::Type* for a baseframe.
|
||||
* It only needs to be invoked once (per LlvmContext, I guess ..)
|
||||
**/
|
||||
llvm::StructType *
|
||||
require_baseframe_llvm_type(xo::ref::brw<LlvmContext> llvm_cx)
|
||||
{
|
||||
/* _baseframe: base type for a stack frame */
|
||||
llvm::StructType * frame_llvm_type
|
||||
= llvm::StructType::get(llvm_cx->llvm_cx_ref(),
|
||||
"_baseframe");
|
||||
|
||||
/* _baseframe*: pointer to a stack frame */
|
||||
llvm::PointerType * frameptr_llvm_type
|
||||
= llvm::PointerType::getUnqual(frame_llvm_type);
|
||||
|
||||
/* unwind function = frame[1] */
|
||||
llvm::FunctionType * unwind_llvm_type
|
||||
= require_baseframe_unwind_llvm_type(llvm_cx,
|
||||
frameptr_llvm_type);
|
||||
|
||||
/* _baseframe members */
|
||||
std::vector<llvm::Type *> llvm_membertype_v;
|
||||
{
|
||||
llvm_membertype_v.reserve(2);
|
||||
|
||||
/* frame[0] = pointer to next frame */
|
||||
llvm_membertype_v.push_back(frameptr_llvm_type);
|
||||
/* frame[1] = unwind function */
|
||||
llvm_membertype_v.push_back(unwind_llvm_type);
|
||||
}
|
||||
|
||||
frame_llvm_type->setBody(frameptr_llvm_type /*frame[0]*/,
|
||||
unwind_llvm_type /*frame[1]*/);
|
||||
|
||||
return frame_llvm_type;
|
||||
} /*require_baseframe_llvm_type*/
|
||||
|
||||
/** need a supporting type for stack frame
|
||||
* - so we can handle variables with non-trivial dtors
|
||||
* (e.g. smart pointers)
|
||||
* - so we can implement nested lexical scoping
|
||||
* - so we can walk stack for exception handling
|
||||
* - eventually: so we can eventually implement trampoline
|
||||
*
|
||||
* frame representation:
|
||||
*
|
||||
* ^
|
||||
* |
|
||||
* +-------+ |
|
||||
* next_frame [0] | o-------/
|
||||
* +-------|
|
||||
* unwind_fn [1] | o-------> baseframe* (*)(baseframe*, ctl)
|
||||
* +-------|
|
||||
* arg[i] [2+i] | ... |
|
||||
* +-------+
|
||||
*
|
||||
* invoke frame.unwind_fn to dispose of a frame
|
||||
* - ctl=0 dtor. deal with smart pointers etc.
|
||||
* - ctl=1 copy. lift frame into heap for lambda capture
|
||||
*
|
||||
* every frame is a subtype of _baseframe (ofc llvm doesn't know this).
|
||||
* See baseframe_llvm_type(), baseframe_unwind_llvm_type() above
|
||||
*
|
||||
* editor bait: activation_record_to_llvm_type
|
||||
**/
|
||||
llvm::StructType *
|
||||
frame_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
ref::brw<Lambda> lambda,
|
||||
llvm::Function * lambda_llvm_fn)
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
using xo::scope;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
/* frame type doesn't need a name */
|
||||
llvm::StructType * frame_llvm_type
|
||||
= llvm::StructType::get(llvm_cx->llvm_cx_ref());
|
||||
|
||||
/* _baseframe */
|
||||
llvm::StructType * baseframe_llvm_type
|
||||
= require_baseframe_llvm_type(llvm_cx);
|
||||
|
||||
/* _baseframe*: pointer to a generic stack frame */
|
||||
llvm::PointerType * baseframeptr_llvm_type
|
||||
= llvm::PointerType::getUnqual(baseframe_llvm_type);
|
||||
|
||||
/* _baseframe* (*)(_baseframe*, i32) */
|
||||
llvm::FunctionType * unwind_llvm_type
|
||||
= require_baseframe_unwind_llvm_type(llvm_cx,
|
||||
baseframeptr_llvm_type);
|
||||
|
||||
/* llvm_argtype_v:
|
||||
* - llvm_argtype_v[0] = llvm::Type* for pointer to next_frame
|
||||
* - llvm_argtype_v[1] = llvm::Type* for unwind_fn
|
||||
* - llvm_argtype_v[2+i] = llvm::Type* for lambda->fn_arg(i)
|
||||
*/
|
||||
std::vector<llvm::Type *> llvm_argtype_v;
|
||||
{
|
||||
llvm_argtype_v.reserve(2 + lambda_llvm_fn->arg_size());
|
||||
|
||||
/* frame pointer */
|
||||
llvm_argtype_v.push_back(baseframeptr_llvm_type);
|
||||
/* unwind function */
|
||||
llvm_argtype_v.push_back(unwind_llvm_type);
|
||||
|
||||
int i_arg = 0;
|
||||
for (auto & arg : lambda_llvm_fn->args()) {
|
||||
log && log(xtag("i_arg", i_arg),
|
||||
xtag("param", std::string(arg.getName())));
|
||||
|
||||
llvm_argtype_v.push_back(td_to_llvm_type(llvm_cx,
|
||||
lambda->fn_arg(i_arg)));
|
||||
|
||||
++i_arg;
|
||||
}
|
||||
}
|
||||
|
||||
frame_llvm_type->setBody(llvm_argtype_v);
|
||||
|
||||
return frame_llvm_type;
|
||||
} /*frame_to_llvm_type*/
|
||||
} /*namespace*/
|
||||
|
||||
llvm::Function *
|
||||
MachPipeline::codegen_lambda_defn(ref::brw<Lambda> lambda,
|
||||
llvm::IRBuilder<> & ir_builder)
|
||||
|
|
@ -915,7 +712,7 @@ namespace xo {
|
|||
scope log(XO_DEBUG(c_debug_flag),
|
||||
xtag("lambda-name", lambda->name()));
|
||||
|
||||
global_env_[lambda->name()] = lambda.get();
|
||||
global_env_->require_global(lambda->name(), lambda.get());
|
||||
|
||||
/* do we already know a function with this name? */
|
||||
auto * llvm_fn = llvm_module_->getFunction(lambda->name());
|
||||
|
|
@ -929,30 +726,17 @@ namespace xo {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
/* generate function body */
|
||||
|
||||
auto block = llvm::BasicBlock::Create(llvm_cx_->llvm_cx_ref(), "entry", llvm_fn);
|
||||
|
||||
ir_builder.SetInsertPoint(block);
|
||||
|
||||
/* create stack frame */
|
||||
llvm::StructType * frame_llvm_type
|
||||
= frame_to_llvm_type(llvm_cx_,
|
||||
lambda,
|
||||
llvm_fn);
|
||||
|
||||
llvm::AllocaInst * frame_alloca = create_entry_frame_alloca(llvm_fn,
|
||||
frame_llvm_type);
|
||||
|
||||
if (!frame_alloca)
|
||||
return nullptr;
|
||||
|
||||
/** Actual parameters will need their own activation record.
|
||||
* Track its shape here.
|
||||
*
|
||||
* Local variables will be formal parameters to a nested lambda;
|
||||
**/
|
||||
this->env_stack_.push(activation_record(llvm_fn, frame_alloca));
|
||||
this->env_stack_.push(activation_record());
|
||||
|
||||
{
|
||||
log && log("lambda: stack size Z", xtag("Z", env_stack_.size()));
|
||||
|
|
@ -965,7 +749,6 @@ namespace xo {
|
|||
|
||||
std::string arg_name = std::string(arg.getName());
|
||||
|
||||
#ifdef OBSOLETE
|
||||
/* stack location for arg[i] */
|
||||
llvm::AllocaInst * alloca
|
||||
= create_entry_block_alloca(llvm_fn,
|
||||
|
|
@ -976,45 +759,17 @@ namespace xo {
|
|||
this->env_stack_.pop();
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* need to store args to stack frame */
|
||||
|
||||
std::int32_t i_slot = env_stack_.top().lookup_var(arg_name);
|
||||
|
||||
if (i_slot == -1) {
|
||||
/* variable not found! */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
llvm::Value * i32_zero = llvm::ConstantInt::get(llvm_cx_->llvm_cx_ref(),
|
||||
llvm::APInt(32, 0));
|
||||
llvm::Value * i32_slot = llvm::ConstantInt::get(llvm_cx_->llvm_cx_ref(),
|
||||
llvm::APInt(32, i_slot));
|
||||
|
||||
std::array<llvm::Value*,2> index_v = {
|
||||
{i32_zero /*deref frame pointer*/,
|
||||
i32_slot /*field# relative to frame pointer*/}};
|
||||
|
||||
/* location in stack frame of arg #i */
|
||||
auto arg_ptr
|
||||
= ir_builder.CreateInBoundsGEP(frame_llvm_type,
|
||||
frame_alloca,
|
||||
index_v);
|
||||
|
||||
/* store on function entry
|
||||
* see codegen_variable() for corresponding load
|
||||
*/
|
||||
ir_builder.CreateStore(&arg,
|
||||
alloca /*destination*/);
|
||||
ir_builder.CreateStore(&arg, alloca);
|
||||
|
||||
#ifdef OBSOLETE
|
||||
/* remember stack location for reference + assignment
|
||||
* in lambda body.
|
||||
*
|
||||
*/
|
||||
env_stack_.top().alloc_var(arg_name, alloca);
|
||||
#endif
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,22 +9,21 @@ namespace xo {
|
|||
using std::cerr;
|
||||
using std::endl;
|
||||
|
||||
int32_t
|
||||
llvm::AllocaInst *
|
||||
activation_record::lookup_var(const std::string & x) const {
|
||||
|
||||
auto ix = name2ix_map_.find(x);
|
||||
auto ix = frame_.find(x);
|
||||
|
||||
if (ix == name2ix_map_.end()) {
|
||||
if (ix == frame_.end()) {
|
||||
cerr << "activation_record::lookup_var: no binding for variable x"
|
||||
<< xtag("x", x)
|
||||
<< endl;
|
||||
return -1;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ix->second;
|
||||
} /*lookup_var*/
|
||||
|
||||
#ifdef OBSOLETE
|
||||
llvm::AllocaInst *
|
||||
activation_record::alloc_var(const std::string & x,
|
||||
llvm::AllocaInst * alloca)
|
||||
|
|
@ -39,7 +38,6 @@ namespace xo {
|
|||
frame_[x] = alloca;
|
||||
return alloca;
|
||||
} /*alloc_var*/
|
||||
#endif
|
||||
} /*namespace jit*/
|
||||
} /*namespace xo*/
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue