xo-jit: fnptr -> closures for primitives+lambdas throughout
This commit is contained in:
parent
26a055eb1c
commit
09f5c141df
6 changed files with 295 additions and 124 deletions
|
|
@ -68,6 +68,17 @@ namespace xo {
|
|||
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)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -38,9 +38,23 @@ namespace xo {
|
|||
* to either {primitive functions, functions-requiring-closures},
|
||||
* with choice deferred until runtime
|
||||
**/
|
||||
static llvm::FunctionType * function_td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
bool wrapper_flag = false);
|
||||
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.
|
||||
*
|
||||
|
|
@ -171,19 +185,6 @@ namespace xo {
|
|||
llvm::PointerType * hint_envptr_llvm_type = nullptr);
|
||||
|
||||
private:
|
||||
/** 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 representation for a struct type described by @p struct_td
|
||||
**/
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ namespace xo {
|
|||
TypeDescr fn_td = expr->valuetype();
|
||||
|
||||
llvm::FunctionType * llvm_fn_type
|
||||
= type2llvm::function_td_to_llvm_type(llvm_cx_.borrow(), fn_td);
|
||||
= type2llvm::function_td_to_lvtype(llvm_cx_.borrow(), fn_td);
|
||||
|
||||
if (!llvm_fn_type)
|
||||
return nullptr;
|
||||
|
|
@ -251,7 +251,7 @@ namespace xo {
|
|||
|
||||
llvm::Function *
|
||||
MachPipeline::codegen_primitive_wrapper(ref::brw<PrimitiveInterface> expr,
|
||||
llvm::IRBuilder<> & ir_builder)
|
||||
llvm::IRBuilder<> & /*ir_builder*/)
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
|
||||
|
|
@ -266,7 +266,7 @@ namespace xo {
|
|||
std::string wrap_name = std::string(c_prefix) + expr->name();
|
||||
|
||||
/* original primitive */
|
||||
auto * native_lvfn = codegen_primitive(expr);
|
||||
auto * native_lvfn = this->codegen_primitive(expr);
|
||||
|
||||
/* wrapped primitive */
|
||||
auto * wrap_lvfn = llvm_module_->getFunction(wrap_name);
|
||||
|
|
@ -279,13 +279,15 @@ namespace xo {
|
|||
TypeDescr fn_td = expr->valuetype();
|
||||
|
||||
llvm::FunctionType * native_lvtype
|
||||
= type2llvm::function_td_to_llvm_type(llvm_cx_.borrow(), fn_td);
|
||||
= type2llvm::function_td_to_lvtype(llvm_cx_.borrow(),
|
||||
fn_td,
|
||||
false /*!wrapper_flag*/);
|
||||
|
||||
if (!native_lvtype)
|
||||
return nullptr;
|
||||
|
||||
llvm::FunctionType * wrapper_lvtype
|
||||
= type2llvm::function_td_to_llvm_type(llvm_cx_.borrow(),
|
||||
= type2llvm::function_td_to_lvtype(llvm_cx_.borrow(),
|
||||
fn_td,
|
||||
true /*wrapper_flag (for closure)*/);
|
||||
|
||||
|
|
@ -301,7 +303,11 @@ namespace xo {
|
|||
auto block = llvm::BasicBlock::Create(llvm_cx_->llvm_cx_ref(),
|
||||
"entry", wrap_lvfn);
|
||||
|
||||
ir_builder.SetInsertPoint(block);
|
||||
/* don't call SetInsertPoint() on incoming ir_builder argument.
|
||||
* Want to avoid disturbing top-to-bottom flow
|
||||
*/
|
||||
llvm::IRBuilder<> tmp_ir_builder(llvm_cx_->llvm_cx_ref());
|
||||
tmp_ir_builder.SetInsertPoint(block);
|
||||
|
||||
std::vector<llvm::Value *> args;
|
||||
|
||||
|
|
@ -323,15 +329,15 @@ namespace xo {
|
|||
/* {caller,callee} must agree on calling convention,
|
||||
* so for primitives we need to assume c.
|
||||
*/
|
||||
llvm::CallInst * call = ir_builder.CreateCall(native_lvtype,
|
||||
native_lvfn,
|
||||
args,
|
||||
"w.calltmp");
|
||||
llvm::CallInst * call = tmp_ir_builder.CreateCall(native_lvtype,
|
||||
native_lvfn,
|
||||
args,
|
||||
"w.calltmp");
|
||||
if (call) {
|
||||
call->setTailCall(true);
|
||||
|
||||
/* does this work if call returns void? Is this needed with tail call? */
|
||||
ir_builder.CreateRet(call);
|
||||
tmp_ir_builder.CreateRet(call);
|
||||
|
||||
llvm::verifyFunction(*wrap_lvfn);
|
||||
|
||||
|
|
@ -365,6 +371,9 @@ namespace xo {
|
|||
MachPipeline::codegen_primitive_closure(ref::brw<xo::ast::PrimitiveInterface> expr,
|
||||
llvm::IRBuilder<> & ir_builder)
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
llvm::StructType * closure_lvtype
|
||||
= type2llvm::create_closureapi_lvtype(llvm_cx_.borrow(), expr);
|
||||
|
||||
|
|
@ -402,6 +411,8 @@ namespace xo {
|
|||
* - MachPipeline::codegen_primitive_closure
|
||||
* - MachPipeline::codegen_lambda_closure
|
||||
* - type2llvm::create_closure_lvtype
|
||||
*
|
||||
* although this refers to a closure, llvm doesn't know that
|
||||
*/
|
||||
llvm::Value * llvm_closure = nullptr;
|
||||
llvmintrinsic intrinsic = llvmintrinsic::invalid;
|
||||
|
|
@ -413,7 +424,7 @@ namespace xo {
|
|||
auto pm = PrimitiveInterface::from(apply->fn());
|
||||
|
||||
if (pm) {
|
||||
llvm_closure = this->codegen_primitive(pm);
|
||||
llvm_closure = this->codegen_primitive_closure(pm, ir_builder);
|
||||
/* hint, when available. use faster alternative to IRBuilder::CreateCall below */
|
||||
intrinsic = pm->intrinsic();
|
||||
}
|
||||
|
|
@ -442,69 +453,93 @@ namespace xo {
|
|||
/* function type in apply node's function position */
|
||||
TypeDescr ast_fn_td = apply->fn()->valuetype();
|
||||
|
||||
#ifdef NOT_USING_DEBUG
|
||||
cerr << "MachPipeline::codegen_apply: fn:" << endl;
|
||||
fn->print(llvm::errs());
|
||||
cerr << endl;
|
||||
#endif
|
||||
if (log) {
|
||||
log("MachPipeline::codegen_apply: fn in apply pos...");
|
||||
llvm_closure->print(llvm::errs());
|
||||
log("...done");
|
||||
log("llvm type...");
|
||||
llvm_closure->getType()->dump();
|
||||
log("...done");
|
||||
}
|
||||
|
||||
/* checks here will be redundant */
|
||||
|
||||
#ifdef REDUNDANT_TYPECHECK
|
||||
if (apply->argv().size() != ast_fn_td->n_fn_arg()) {
|
||||
cerr << "MachPipeline::codegen_apply: error: callee f expecting n1 args where n2 supplied"
|
||||
//<< xtag("f", ast_fn->name())
|
||||
<< xtag("n1", ast_fn_td->n_fn_arg())
|
||||
<< xtag("n2", apply->argv().size())
|
||||
<< endl;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/** also check argument types **/
|
||||
for (size_t i = 0, n = ast_fn_td->n_fn_arg(); i < n; ++i) {
|
||||
if (apply->argv()[i]->valuetype() != ast_fn_td->fn_arg(i)) {
|
||||
cerr << "MachPipeline::codegen_apply: error: callee F for arg# I seeeing U instead of expected T"
|
||||
<< xtag("F", apply->fn())
|
||||
<< xtag("I", i)
|
||||
<< xtag("U", apply->argv()[i]->valuetype()->short_name())
|
||||
<< xtag("T", ast_fn_td->fn_arg(i)->short_name())
|
||||
<< endl;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef OBSOLETE
|
||||
llvm::StructType * closure_lvtype
|
||||
= type2llvm::function_td_to_closureapi_lvtype(llvm_cx_,
|
||||
ast_fn_td,
|
||||
"" /*name - not required*/);
|
||||
#endif
|
||||
|
||||
llvm::Value * lv_fnptr = nullptr;
|
||||
{
|
||||
#ifdef MAYBE_VERBOSE
|
||||
llvm::Value * i0_slot
|
||||
= llvm::ConstantInt::get(llvm_cx_->llvm_cx_ref(),
|
||||
llvm::APInt(32 /*bits*/, 0 /*value*/));
|
||||
|
||||
llvm::Value * fnptr_slot
|
||||
= llvm::ConstantInt::get(llvm_cx_->llvm_cx_ref(),
|
||||
llvm::APInt(32 /*bits*/, 0 /*value*/));
|
||||
|
||||
std::array<llvm::Value*, 1> index_v = {{fnptr_slot /*fnptr slot = closure[0]*/}};
|
||||
std::array<llvm::Value*, 2> index_v
|
||||
= {{i0_slot,
|
||||
fnptr_slot /*fnptr slot = closure[0]*/}};
|
||||
|
||||
lv_fnptr = ir_builder.CreateInBoundsGEP(closure_lvtype,
|
||||
llvm_closure,
|
||||
index_v);
|
||||
llvm::Value * lv_fnptr_addr
|
||||
= ir_builder.CreateInBoundsGEP(llvm_closure->getType(), //closure_lvtype,
|
||||
llvm_closure,
|
||||
index_v);
|
||||
|
||||
llvm::Type * fnptr_lvtype
|
||||
= type2llvm::function_td_to_llvm_fnptr_type(llvm_cx_,
|
||||
apply->fn()->valuetype(),
|
||||
true /*wrapper_flag*/);
|
||||
|
||||
/* the thing we're going to call */
|
||||
lv_fnptr = ir_builder.CreateLoad(fnptr_lvtype, lv_fnptr_addr);
|
||||
#endif
|
||||
|
||||
std::array<unsigned int, 1> index_v = {{ 0 }};
|
||||
|
||||
//ir_builder.CreateExtractValue(Value *Agg, ArrayRef<unsigned int> Idxs)
|
||||
|
||||
lv_fnptr = ir_builder.CreateExtractValue(llvm_closure,
|
||||
index_v,
|
||||
"fnptr");
|
||||
}
|
||||
|
||||
llvm::Value * lv_fnenvptr = nullptr;
|
||||
{
|
||||
#ifdef MAYBE_VERBOSE
|
||||
llvm::Value * i0_slot
|
||||
= llvm::ConstantInt::get(llvm_cx_->llvm_cx_ref(),
|
||||
llvm::APInt(32 /*bits*/, 0 /*value*/));
|
||||
|
||||
llvm::Value * envptr_slot
|
||||
= llvm::ConstantInt::get(llvm_cx_->llvm_cx_ref(),
|
||||
llvm::APInt(32 /*bits*/, 1 /*value*/));
|
||||
|
||||
std::array<llvm::Value*, 1> index_v = {{envptr_slot /*envptr slot = closure[1]*/}};
|
||||
std::array<llvm::Value*, 2> index_v
|
||||
= {{i0_slot,
|
||||
envptr_slot /*envptr slot = closure[1]*/}};
|
||||
|
||||
lv_fnenvptr = ir_builder.CreateInBoundsGEP(closure_lvtype,
|
||||
llvm_closure,
|
||||
index_v);
|
||||
llvm::Value * lv_fnenvptr_addr
|
||||
= ir_builder.CreateInBoundsGEP(llvm_closure->getType(), //closure_lvtype,
|
||||
llvm_closure,
|
||||
index_v);
|
||||
|
||||
llvm::Type * fnenvptr_lvtype
|
||||
= type2llvm::env_api_llvm_ptr_type(llvm_cx_);
|
||||
|
||||
lv_fnenvptr = ir_builder.CreateLoad(fnenvptr_lvtype, lv_fnenvptr_addr);
|
||||
#endif
|
||||
|
||||
std::array<unsigned int, 1> index_v = {{ 1 }};
|
||||
|
||||
lv_fnenvptr = ir_builder.CreateExtractValue(llvm_closure,
|
||||
index_v,
|
||||
"envptr");
|
||||
}
|
||||
|
||||
std::vector<llvm::Value *> args;
|
||||
|
|
@ -524,8 +559,13 @@ namespace xo {
|
|||
if (log) {
|
||||
/* TODO: print helper for llvm::Value* */
|
||||
std::string llvm_value_str;
|
||||
llvm::raw_string_ostream ss(llvm_value_str);
|
||||
arg->print(ss);
|
||||
|
||||
if (arg) {
|
||||
llvm::raw_string_ostream ss(llvm_value_str);
|
||||
arg->print(ss);
|
||||
} else {
|
||||
llvm_value_str = "<null llvm::Value>";
|
||||
}
|
||||
|
||||
log(xtag("i_arg", i),
|
||||
xtag("arg", llvm_value_str));
|
||||
|
|
@ -533,6 +573,14 @@ namespace xo {
|
|||
|
||||
args.push_back(arg);
|
||||
++i;
|
||||
|
||||
if (!arg) {
|
||||
cerr << "MachPipeline::codegen_apply: failed for i'th argument"
|
||||
<< xtag("i", i)
|
||||
<< endl;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* if we have an intrinsic hint,
|
||||
|
|
@ -571,9 +619,9 @@ namespace xo {
|
|||
}
|
||||
|
||||
llvm::FunctionType * llvm_fn_type
|
||||
= type2llvm::function_td_to_llvm_type(this->llvm_cx_,
|
||||
ast_fn_td,
|
||||
true /*wrapper_flag*/);
|
||||
= type2llvm::function_td_to_lvtype(this->llvm_cx_,
|
||||
ast_fn_td,
|
||||
true /*wrapper_flag*/);
|
||||
|
||||
return ir_builder.CreateCall(llvm_fn_type,
|
||||
lv_fnptr,
|
||||
|
|
@ -623,13 +671,13 @@ namespace xo {
|
|||
* Note that this argument is not present in lambda,
|
||||
* so we need care. lambda->fn_arg(i) -> lvfn->arg [i+1]
|
||||
*/
|
||||
llvm::FunctionType * llvm_fn_type
|
||||
= type2llvm::function_td_to_llvm_type(llvm_cx_.borrow(),
|
||||
lambda->valuetype(),
|
||||
true /*wrapper_flag*/);
|
||||
llvm::FunctionType * fn_lvtype
|
||||
= type2llvm::function_td_to_lvtype(llvm_cx_.borrow(),
|
||||
lambda->valuetype(),
|
||||
true /*wrapper_flag*/);
|
||||
|
||||
/* create (initially empty) function */
|
||||
fn = llvm::Function::Create(llvm_fn_type,
|
||||
fn = llvm::Function::Create(fn_lvtype,
|
||||
llvm::Function::ExternalLinkage,
|
||||
lambda->name(),
|
||||
llvm_module_.get());
|
||||
|
|
@ -660,7 +708,7 @@ namespace xo {
|
|||
|
||||
llvm::Function *
|
||||
MachPipeline::codegen_lambda_defn(ref::brw<Lambda> lambda,
|
||||
llvm::IRBuilder<> & ir_builder)
|
||||
llvm::IRBuilder<> & /*ir_builder*/)
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
|
||||
|
|
@ -690,14 +738,19 @@ namespace xo {
|
|||
|
||||
auto block = llvm::BasicBlock::Create(llvm_cx_->llvm_cx_ref(), "entry", llvm_fn);
|
||||
|
||||
ir_builder.SetInsertPoint(block);
|
||||
/* since we need to explictly set builder's insert point,
|
||||
* make a new builder instead of disturbing the top-to-bottom flow of the
|
||||
* called ir_builder
|
||||
*/
|
||||
llvm::IRBuilder<> tmp_ir_builder(llvm_cx_->llvm_cx_ref());
|
||||
tmp_ir_builder.SetInsertPoint(block);
|
||||
|
||||
/** Actual parameters will need their own activation record.
|
||||
* Track its shape + setup/teardown here.
|
||||
**/
|
||||
this->env_stack_.push(activation_record(lambda.get()));
|
||||
|
||||
bool ok_flag = this->env_stack_.top().bind_locals(llvm_cx_, llvm_fn, ir_builder);
|
||||
bool ok_flag = this->env_stack_.top().bind_locals(llvm_cx_, llvm_fn, tmp_ir_builder);
|
||||
|
||||
if (!ok_flag) {
|
||||
this->env_stack_.pop();
|
||||
|
|
@ -706,11 +759,11 @@ namespace xo {
|
|||
|
||||
llvm::Value * retval = this->codegen(lambda->body(),
|
||||
envptr,
|
||||
ir_builder);
|
||||
tmp_ir_builder);
|
||||
|
||||
if (retval) {
|
||||
/* completes the function.. */
|
||||
ir_builder.CreateRet(retval);
|
||||
tmp_ir_builder.CreateRet(retval);
|
||||
|
||||
/* validate! always validate! */
|
||||
llvm::verifyFunction(*llvm_fn);
|
||||
|
|
@ -742,7 +795,9 @@ namespace xo {
|
|||
|
||||
this->env_stack_.pop();
|
||||
|
||||
log && log("after pop, env stack size Z", xtag("Z", env_stack_.size()));
|
||||
log && log("after pop, env stack size Z",
|
||||
xtag("Z", env_stack_.size()),
|
||||
xtag("llvm_fn", (void*)llvm_fn));
|
||||
|
||||
return llvm_fn;
|
||||
} /*codegen_lambda_defn*/
|
||||
|
|
@ -755,14 +810,21 @@ namespace xo {
|
|||
llvm::StructType * closure_lvtype
|
||||
= type2llvm::create_closureapi_lvtype(llvm_cx_.borrow(), lambda);
|
||||
|
||||
llvm::Function * lvfn = codegen_lambda_decl(lambda);
|
||||
llvm::Function * lvfn = codegen_lambda_defn(lambda, ir_builder);
|
||||
|
||||
if (!lvfn) {
|
||||
cerr << "MachPipeline::codegen_lambda_closure: codegen lambda failed"
|
||||
<< endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
llvm::Value * lv_closure = nullptr;
|
||||
|
||||
lv_closure = ir_builder.CreateInsertValue(llvm::UndefValue::get(closure_lvtype),
|
||||
lvfn, {0}, "lmfnptr" /*name*/);
|
||||
lv_closure = ir_builder.CreateInsertValue(lv_closure,
|
||||
envptr, {1}, "envptr" /*name*/);
|
||||
{
|
||||
lv_closure = ir_builder.CreateInsertValue(llvm::UndefValue::get(closure_lvtype),
|
||||
lvfn, {0}); //, "lmfnptr" /*name*/);
|
||||
lv_closure = ir_builder.CreateInsertValue(lv_closure,
|
||||
envptr, {1}, "closure" /*name*/);
|
||||
}
|
||||
|
||||
return lv_closure;
|
||||
} /*codegen_lambda_closure*/
|
||||
|
|
@ -836,44 +898,45 @@ namespace xo {
|
|||
when_false_bb);
|
||||
|
||||
/* populate when_true_bb */
|
||||
ir_builder.SetInsertPoint(when_true_bb);
|
||||
llvm::IRBuilder<> tmp_ir_builder(llvm_cx_->llvm_cx_ref());
|
||||
tmp_ir_builder.SetInsertPoint(when_true_bb);
|
||||
|
||||
llvm::Value * when_true_ir = this->codegen(expr->when_true(),
|
||||
envptr,
|
||||
ir_builder);
|
||||
tmp_ir_builder);
|
||||
|
||||
if (!when_true_ir)
|
||||
return nullptr;
|
||||
|
||||
/* at end of when-true sequence, jump to merge suffix */
|
||||
ir_builder.CreateBr(merge_bb);
|
||||
tmp_ir_builder.CreateBr(merge_bb);
|
||||
/* note: codegen for expr->when_true() may have altered builder's "current block" */
|
||||
when_true_bb = ir_builder.GetInsertBlock();
|
||||
when_true_bb = tmp_ir_builder.GetInsertBlock();
|
||||
|
||||
/* populate when_false_bb */
|
||||
parent_fn->insert(parent_fn->end(), when_false_bb);
|
||||
ir_builder.SetInsertPoint(when_false_bb);
|
||||
tmp_ir_builder.SetInsertPoint(when_false_bb);
|
||||
|
||||
llvm::Value * when_false_ir = this->codegen(expr->when_false(),
|
||||
envptr,
|
||||
ir_builder);
|
||||
tmp_ir_builder);
|
||||
if (!when_false_ir)
|
||||
return nullptr;
|
||||
|
||||
/* at end of when-false sequence, jump to merge suffix */
|
||||
ir_builder.CreateBr(merge_bb);
|
||||
tmp_ir_builder.CreateBr(merge_bb);
|
||||
/* note: codegen for expr->when_false() may have altered builder's "current block" */
|
||||
when_false_bb = ir_builder.GetInsertBlock();
|
||||
when_false_bb = tmp_ir_builder.GetInsertBlock();
|
||||
|
||||
/* merged suffix sequence */
|
||||
parent_fn->insert(parent_fn->end(), merge_bb);
|
||||
ir_builder.SetInsertPoint(merge_bb);
|
||||
tmp_ir_builder.SetInsertPoint(merge_bb);
|
||||
|
||||
/** TODO: switch to getInt1Ty here **/
|
||||
llvm::PHINode * phi_node
|
||||
= ir_builder.CreatePHI(llvm::Type::getDoubleTy(llvm_cx_->llvm_cx_ref()),
|
||||
2 /*#of branches being merged (?)*/,
|
||||
"iftmp");
|
||||
= tmp_ir_builder.CreatePHI(llvm::Type::getDoubleTy(llvm_cx_->llvm_cx_ref()),
|
||||
2 /*#of branches being merged (?)*/,
|
||||
"iftmp");
|
||||
phi_node->addIncoming(when_true_ir, when_true_bb);
|
||||
phi_node->addIncoming(when_false_ir, when_false_bb);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,12 @@ namespace xo {
|
|||
} /*ctor*/
|
||||
|
||||
const runtime_binding_detail *
|
||||
activation_record::lookup_var(const std::string & x) const {
|
||||
activation_record::lookup_var(const std::string & x) const
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
using xo::scope;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
auto ix = frame_.find(x);
|
||||
|
||||
|
|
@ -41,6 +46,10 @@ namespace xo {
|
|||
cerr << "activation_record::lookup_var: no binding for variable x"
|
||||
<< xtag("x", x)
|
||||
<< endl;
|
||||
cerr << "frame:";
|
||||
for (const auto & ix : frame_)
|
||||
cerr << xtag("var", ix.first) << xtag("->", ix.second) << endl;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
|
@ -51,6 +60,14 @@ namespace xo {
|
|||
activation_record::alloc_var(const std::string & x,
|
||||
const runtime_binding_detail & binding)
|
||||
{
|
||||
constexpr bool c_debug_flag = true;
|
||||
using xo::scope;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
log && log(xtag("var", x),
|
||||
xtag("binding", binding));
|
||||
|
||||
if (frame_.find(x) != frame_.end()) {
|
||||
cerr << "activation_record::alloc_var: variable x already present in frame"
|
||||
<< xtag("x", x)
|
||||
|
|
@ -76,10 +93,12 @@ namespace xo {
|
|||
constexpr bool c_debug_flag = true;
|
||||
using xo::scope;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag),
|
||||
xtag("llvm_fn", (void*)llvm_fn),
|
||||
xtag("var_name", var_name),
|
||||
xtag("var_type", var_type->short_name()));
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
log && log(xtag("llvm_fn", (void*)llvm_fn),
|
||||
xtag("i_arg", i_arg),
|
||||
xtag("var_name", var_name),
|
||||
xtag("var_type", var_type->short_name()));
|
||||
|
||||
llvm::Type * llvm_var_type = type2llvm::td_to_llvm_type(llvm_cx,
|
||||
var_type);
|
||||
|
|
@ -151,12 +170,16 @@ namespace xo {
|
|||
#endif
|
||||
llvm::IRBuilder<> & tmp_ir_builder)
|
||||
{
|
||||
llvm::Value * i0_slot
|
||||
= llvm::ConstantInt::get(llvm_cx->llvm_cx_ref(),
|
||||
llvm::APInt(32 /*bits*/, 0));
|
||||
|
||||
llvm::Value * i32_slot
|
||||
= llvm::ConstantInt::get(llvm_cx->llvm_cx_ref(),
|
||||
llvm::APInt(32 /*bits*/,
|
||||
i_slot /*value*/));
|
||||
std::array<llvm::Value*, 1> index_v = {
|
||||
{i32_slot /*environment slot #0*/}};
|
||||
std::array<llvm::Value*, 2> index_v = {
|
||||
{i0_slot, i32_slot /*environment slot #0*/}};
|
||||
|
||||
llvm::Value * llvm_localenv_slot_ptr
|
||||
= tmp_ir_builder.CreateInBoundsGEP(localenv_llvm_type,
|
||||
|
|
@ -216,7 +239,7 @@ namespace xo {
|
|||
tmp_ir_builder,
|
||||
i_arg,
|
||||
arg_name,
|
||||
lambda_->fn_arg(i_arg));
|
||||
lambda_->fn_arg(i_arg-1));
|
||||
|
||||
if (!binding.llvm_addr_)
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -58,20 +58,29 @@ namespace xo {
|
|||
* that represented by @p fn_td
|
||||
**/
|
||||
llvm::FunctionType *
|
||||
type2llvm::function_td_to_llvm_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
bool wrapper_flag)
|
||||
type2llvm::function_td_to_lvtype(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
bool wrapper_flag)
|
||||
{
|
||||
int n_fn_arg = fn_td->n_fn_arg();
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
int n_ast_fn_arg = fn_td->n_fn_arg();
|
||||
|
||||
if (log) {
|
||||
log(xtag("fn_td", fn_td->short_name()));
|
||||
log(xtag("n_ast_fn_arg", n_ast_fn_arg));
|
||||
}
|
||||
|
||||
std::vector<llvm::Type *> llvm_argtype_v;
|
||||
llvm_argtype_v.reserve(n_fn_arg + (wrapper_flag ? 1 : 0));
|
||||
llvm_argtype_v.reserve(n_ast_fn_arg + (wrapper_flag ? 1 : 0));
|
||||
|
||||
if (wrapper_flag)
|
||||
llvm_argtype_v.push_back(env_api_llvm_ptr_type(llvm_cx));
|
||||
|
||||
/** check function args are all known **/
|
||||
for (int i = 0; i < n_fn_arg; ++i) {
|
||||
for (int i = 0; i < n_ast_fn_arg; ++i) {
|
||||
TypeDescr arg_td = fn_td->fn_arg(i);
|
||||
|
||||
llvm::Type * llvm_argtype = type2llvm::td_to_llvm_type(llvm_cx, arg_td);
|
||||
|
|
@ -79,12 +88,26 @@ namespace xo {
|
|||
if (!llvm_argtype)
|
||||
return nullptr;
|
||||
|
||||
if (log) {
|
||||
log(xtag("arg_td", arg_td->short_name()));
|
||||
log(xtag("llvm_argtype", "..."));
|
||||
llvm_argtype->dump();
|
||||
log("...done");
|
||||
}
|
||||
|
||||
llvm_argtype_v.push_back(llvm_argtype);
|
||||
}
|
||||
|
||||
TypeDescr retval_td = fn_td->fn_retval();
|
||||
llvm::Type * llvm_retval = type2llvm::td_to_llvm_type(llvm_cx, retval_td);
|
||||
|
||||
if (log) {
|
||||
log(xtag("retval_td", retval_td->short_name()));
|
||||
log(xtag("llvm_retval", "..."));
|
||||
llvm_retval->dump();
|
||||
log("...done");
|
||||
}
|
||||
|
||||
if (!llvm_retval)
|
||||
return nullptr;
|
||||
|
||||
|
|
@ -96,17 +119,23 @@ namespace xo {
|
|||
|
||||
llvm::PointerType *
|
||||
type2llvm::function_td_to_llvm_fnptr_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
TypeDescr fn_td,
|
||||
bool wrapper_flag)
|
||||
TypeDescr /*fn_td*/,
|
||||
bool /*wrapper_flag*/)
|
||||
{
|
||||
auto * llvm_fn_type = function_td_to_llvm_type(llvm_cx, fn_td, wrapper_flag);
|
||||
#ifdef OBSOLETE
|
||||
auto * llvm_fn_type = function_td_to_lvtype(llvm_cx, fn_td, wrapper_flag);
|
||||
#endif
|
||||
|
||||
/** like C: llvm IR doesn't support function-valued variables;
|
||||
* it does however support pointer-to-function-valued variables
|
||||
**/
|
||||
auto * llvm_ptr_type
|
||||
= llvm::PointerType::getUnqual(llvm_cx->llvm_cx_ref());
|
||||
#ifdef OBSOLETE
|
||||
auto * llvm_ptr_type
|
||||
= llvm::PointerType::get(llvm_fn_type,
|
||||
0 /*numbered address space*/);
|
||||
#endif
|
||||
|
||||
return llvm_ptr_type;
|
||||
}
|
||||
|
|
@ -181,20 +210,26 @@ namespace xo {
|
|||
{
|
||||
assert(pointer_td->is_pointer());
|
||||
|
||||
#ifdef OBSOLETE
|
||||
TypeDescr dest_td = pointer_td->fixed_child_td(0);
|
||||
|
||||
llvm::Type * llvm_dest_type = type2llvm::td_to_llvm_type(llvm_cx, dest_td);
|
||||
|
||||
llvm::PointerType * llvm_ptr_type
|
||||
= llvm::PointerType::getUnqual(llvm_dest_type);
|
||||
#endif
|
||||
|
||||
llvm::PointerType * llvm_ptr_type
|
||||
= llvm::PointerType::getUnqual(llvm_cx->llvm_cx_ref());
|
||||
|
||||
return llvm_ptr_type;
|
||||
} /*pointer_td_llvm_type*/
|
||||
|
||||
llvm::PointerType *
|
||||
type2llvm::require_localenv_unwind_llvm_fnptr_type(xo::ref::brw<LlvmContext> llvm_cx,
|
||||
llvm::PointerType * envptr_llvm_type)
|
||||
llvm::PointerType * /*envptr_llvm_type*/)
|
||||
{
|
||||
#ifdef OBSOLETE
|
||||
if (!envptr_llvm_type)
|
||||
envptr_llvm_type = env_api_llvm_ptr_type(llvm_cx);
|
||||
|
||||
|
|
@ -219,6 +254,9 @@ namespace xo {
|
|||
/* _env_api* (*)(_env_api*, i32) */
|
||||
auto * unwind_llvm_fnptr_type
|
||||
= llvm::PointerType::getUnqual(unwind_llvm_type);
|
||||
#endif
|
||||
auto * unwind_llvm_fnptr_type
|
||||
= llvm::PointerType::getUnqual(llvm_cx->llvm_cx_ref());
|
||||
|
||||
return unwind_llvm_fnptr_type;
|
||||
} /*require_localenv_unwind_llvm_fnptr_type*/
|
||||
|
|
@ -231,9 +269,13 @@ namespace xo {
|
|||
= llvm::StructType::get(llvm_cx->llvm_cx_ref(),
|
||||
"_env_api");
|
||||
|
||||
#ifdef OBSOLETE
|
||||
/* _env_api[0]: pointer to a local environment */
|
||||
llvm::PointerType * envptr_llvm_type
|
||||
= llvm::PointerType::getUnqual(env_llvm_type);
|
||||
#endif
|
||||
llvm::PointerType * envptr_llvm_type
|
||||
= llvm::PointerType::getUnqual(llvm_cx->llvm_cx_ref());
|
||||
|
||||
/* _env_api[1]: unwwind/copy function */
|
||||
llvm::PointerType * unwind_llvm_fnptr_type
|
||||
|
|
@ -250,9 +292,11 @@ namespace xo {
|
|||
llvm::PointerType *
|
||||
type2llvm::env_api_llvm_ptr_type(xo::ref::brw<LlvmContext> llvm_cx)
|
||||
{
|
||||
#ifdef OBSOLETE
|
||||
llvm::StructType * env_llvm_type = env_api_llvm_type(llvm_cx);
|
||||
|
||||
return llvm::PointerType::getUnqual(env_llvm_type);
|
||||
#endif
|
||||
return llvm::PointerType::getUnqual(llvm_cx->llvm_cx_ref());
|
||||
} /*env_api_llvm_ptr_type*/
|
||||
|
||||
llvm::StructType *
|
||||
|
|
@ -274,6 +318,10 @@ namespace xo {
|
|||
TypeDescr fn_td,
|
||||
const std::string & hint_name)
|
||||
{
|
||||
constexpr bool c_debug_flag = false;
|
||||
|
||||
scope log(XO_DEBUG(c_debug_flag));
|
||||
|
||||
/* would be precisely correct to use create_localenv_llvm_type()
|
||||
* here. However judged not sufficiently helpful.
|
||||
* Would still
|
||||
|
|
@ -283,9 +331,21 @@ namespace xo {
|
|||
llvm::PointerType * fn_lvtype = function_td_to_llvm_fnptr_type(llvm_cx,
|
||||
fn_td,
|
||||
true /*wrapper_flag*/);
|
||||
llvm::StructType * env_lvtype = env_api_llvm_type(llvm_cx);
|
||||
if (log) {
|
||||
log(xtag("fn_lvtype", "..."));
|
||||
fn_lvtype->dump();
|
||||
log("...done");
|
||||
}
|
||||
|
||||
std::vector<llvm::Type *> member_lvtype_v = { fn_lvtype, env_lvtype };
|
||||
llvm::PointerType * envptr_lvtype = env_api_llvm_ptr_type(llvm_cx);
|
||||
|
||||
if (log) {
|
||||
log(xtag("env_lvtype", "..."));
|
||||
envptr_lvtype->dump();
|
||||
log("...done");
|
||||
}
|
||||
|
||||
std::vector<llvm::Type *> member_lvtype_v = { fn_lvtype, envptr_lvtype };
|
||||
|
||||
llvm::StructType * closure_lvtype
|
||||
= llvm::StructType::get(llvm_cx->llvm_cx_ref(), member_lvtype_v);
|
||||
|
|
@ -295,6 +355,12 @@ namespace xo {
|
|||
if (!hint_name.empty())
|
||||
closure_lvtype->setName(llvm::StringRef(hint_name));
|
||||
|
||||
if (log) {
|
||||
log(xtag("closure_lvtype", "..."));
|
||||
closure_lvtype->dump();
|
||||
log("...done");
|
||||
}
|
||||
|
||||
return closure_lvtype;
|
||||
} /*function_td_to_closureapi_lvtype*/
|
||||
|
||||
|
|
|
|||
|
|
@ -116,12 +116,19 @@ namespace xo {
|
|||
|
||||
//auto rng = xo::rng::xoshiro256ss(seed);
|
||||
|
||||
scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.machpipeline"));
|
||||
scope log(XO_DEBUG2(c_debug_flag, "TEST_CASE.machpipeline.fptr"));
|
||||
//log && log("(A)", xtag("foo", foo));
|
||||
|
||||
auto jit = MachPipeline::make();
|
||||
|
||||
for (std::size_t i_tc = 0, n_tc = s_testcase_v.size(); i_tc < n_tc; ++i_tc) {
|
||||
/** can't share jit across examples,
|
||||
* until we fix treatment of primitives:
|
||||
* now that we build a wrapper for each primitive,
|
||||
* need some bookkeeping to avoid trying to build
|
||||
* the same wrapper twice.
|
||||
**/
|
||||
auto jit = MachPipeline::make();
|
||||
|
||||
TestCase const & testcase = s_testcase_v[i_tc];
|
||||
|
||||
INFO(tostr(xtag("i_tc", i_tc)));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue