diff --git a/include/xo/jit/MachPipeline.hpp b/include/xo/jit/MachPipeline.hpp index 14003c44..11589e8c 100644 --- a/include/xo/jit/MachPipeline.hpp +++ b/include/xo/jit/MachPipeline.hpp @@ -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_; /** map global names to functions/variables **/ - std::map> global_env_; + ref::rp global_env_; /** map variable names (formal parameters) to * corresponding llvm IR. diff --git a/include/xo/jit/activation_record.hpp b/include/xo/jit/activation_record.hpp index 7c70ba3f..c2aba2dd 100644 --- a/include/xo/jit/activation_record.hpp +++ b/include/xo/jit/activation_record.hpp @@ -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 name2ix_map_; - -#ifdef OBSOLETE /** maps named slots in a stack frame to logical addresses **/ std::map frame_; /* <-> kaleidoscope NamedValues */ -#endif }; /*activation_record*/ } /*namespace jit*/ diff --git a/src/jit/MachPipeline.cpp b/src/jit/MachPipeline.cpp index 76fa68ae..9c6bb1cd 100644 --- a/src/jit/MachPipeline.cpp +++ b/src/jit/MachPipeline.cpp @@ -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_{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> MachPipeline::find_lambdas(ref::brw 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 llvm_cx, - llvm::PointerType * frameptr_llvm_type) - { - std::vector 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 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_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 llvm_cx, - ref::brw 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_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, 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 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; } } diff --git a/src/jit/activation_record.cpp b/src/jit/activation_record.cpp index cc1aaae3..c7f40362 100644 --- a/src/jit/activation_record.cpp +++ b/src/jit/activation_record.cpp @@ -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*/