From 09f5c141dfd9eaf8561d6472a1f10a31cc8c5470 Mon Sep 17 00:00:00 2001 From: Roland Conybeare Date: Wed, 10 Jul 2024 16:05:00 -0400 Subject: [PATCH] xo-jit: fnptr -> closures for primitives+lambdas throughout --- include/xo/jit/activation_record.hpp | 11 ++ include/xo/jit/type2llvm.hpp | 33 ++-- src/jit/MachPipeline.cpp | 233 +++++++++++++++++---------- src/jit/activation_record.cpp | 39 ++++- src/jit/type2llvm.cpp | 92 +++++++++-- utest/MachPipeline.test.cpp | 11 +- 6 files changed, 295 insertions(+), 124 deletions(-) diff --git a/include/xo/jit/activation_record.hpp b/include/xo/jit/activation_record.hpp index 82763e6c..e3eb7a66 100644 --- a/include/xo/jit/activation_record.hpp +++ b/include/xo/jit/activation_record.hpp @@ -68,6 +68,17 @@ namespace xo { llvm::Type * llvm_type_ = nullptr; }; + inline std::ostream & + operator<<(std::ostream & os, const runtime_binding_detail & x) { + os << ""; + + return os; + } + /** * 1. pattern for a stack frame associated with a user-defined function (some Lambda lm) * diff --git a/include/xo/jit/type2llvm.hpp b/include/xo/jit/type2llvm.hpp index d7a04f05..35a6e739 100644 --- a/include/xo/jit/type2llvm.hpp +++ b/include/xo/jit/type2llvm.hpp @@ -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 llvm_cx, - TypeDescr fn_td, - bool wrapper_flag = false); + static llvm::FunctionType * function_td_to_lvtype(xo::ref::brw 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 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 llvm_cx, - TypeDescr fn_td, - bool wrapper_flag); /** establish llvm representation for a struct type described by @p struct_td **/ diff --git a/src/jit/MachPipeline.cpp b/src/jit/MachPipeline.cpp index b458f9fb..671f38fa 100644 --- a/src/jit/MachPipeline.cpp +++ b/src/jit/MachPipeline.cpp @@ -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 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 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 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 index_v = {{fnptr_slot /*fnptr slot = closure[0]*/}}; + std::array 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 index_v = {{ 0 }}; + + //ir_builder.CreateExtractValue(Value *Agg, ArrayRef 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 index_v = {{envptr_slot /*envptr slot = closure[1]*/}}; + std::array 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 index_v = {{ 1 }}; + + lv_fnenvptr = ir_builder.CreateExtractValue(llvm_closure, + index_v, + "envptr"); } std::vector 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 = ""; + } 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, - 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); diff --git a/src/jit/activation_record.cpp b/src/jit/activation_record.cpp index be1275ef..7b1228e6 100644 --- a/src/jit/activation_record.cpp +++ b/src/jit/activation_record.cpp @@ -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 index_v = { - {i32_slot /*environment slot #0*/}}; + std::array 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; diff --git a/src/jit/type2llvm.cpp b/src/jit/type2llvm.cpp index 6a982b89..e5c07cac 100644 --- a/src/jit/type2llvm.cpp +++ b/src/jit/type2llvm.cpp @@ -58,20 +58,29 @@ namespace xo { * that represented by @p fn_td **/ llvm::FunctionType * - type2llvm::function_td_to_llvm_type(xo::ref::brw llvm_cx, - TypeDescr fn_td, - bool wrapper_flag) + type2llvm::function_td_to_lvtype(xo::ref::brw 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_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 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 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 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 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 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*/ diff --git a/utest/MachPipeline.test.cpp b/utest/MachPipeline.test.cpp index 880aabf1..ad14ac32 100644 --- a/utest/MachPipeline.test.cpp +++ b/utest/MachPipeline.test.cpp @@ -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)));