diff options
author | John Hodge <tpg@mutabah.net> | 2016-08-20 23:07:47 +0800 |
---|---|---|
committer | John Hodge <tpg@mutabah.net> | 2016-08-20 23:07:47 +0800 |
commit | 025ebf7a067d27c07071e46998ac556ea225948c (patch) | |
tree | 19c3ad276ad7fc46098ad77ff3464f7c5e6d3a3f /src/mir/from_hir.cpp | |
parent | 5ae0c86b33d218c05ce91afe73608c5e9f1bd61c (diff) | |
download | mrust-025ebf7a067d27c07071e46998ac556ea225948c.tar.gz |
MIR Gen - Draft drop insertion
Diffstat (limited to 'src/mir/from_hir.cpp')
-rw-r--r-- | src/mir/from_hir.cpp | 323 |
1 files changed, 280 insertions, 43 deletions
diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 7069ef85..f48c5ba3 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -25,9 +25,11 @@ namespace { public: MirBuilder m_builder; private: + const ::std::vector< ::HIR::TypeRef>& m_variable_types; struct LoopDesc { + ScopeHandle scope; ::std::string label; unsigned int cur; unsigned int next; @@ -156,30 +158,41 @@ namespace { // NOTE: This doesn't create a BB, as BBs are not needed for scoping if( node.m_nodes.size() > 0 ) { + auto scope = m_builder.new_scope( node.span() ); + m_block_stack.push_back( {} ); for(unsigned int i = 0; i < node.m_nodes.size()-1; i ++) { auto& subnode = node.m_nodes[i]; const Span& sp = subnode->span(); + + // - Covers just temporaries? (what about variables defined?) + auto stmt_scope = m_builder.new_scope(sp); this->visit_node_ptr(subnode); - if( m_builder.block_active() || m_builder.has_result() ) - { - m_builder.push_stmt_drop( m_builder.lvalue_or_temp(subnode->m_res_type, m_builder.get_result(sp)) ); + if( m_builder.has_result() ) { + m_builder.get_result(sp); + } + + if( m_builder.block_active() ) { + m_builder.terminate_scope(sp, mv$(stmt_scope)); + } + else { + auto _ = mv$(stmt_scope); } } + // - For the last node, don't bother with a statement scope this->visit_node_ptr(node.m_nodes.back()); auto bd = mv$( m_block_stack.back() ); m_block_stack.pop_back(); // Drop all bindings introduced during this block. - // TODO: This should be done in a more generic manner, allowing for drops on panic/return - if( m_builder.block_active() ) - { - for( auto& var_idx : bd.bindings ) { - m_builder.push_stmt_drop( ::MIR::LValue::make_Variable(var_idx) ); - } + if( m_builder.block_active() ) { + m_builder.terminate_scope( node.span(), mv$(scope) ); + } + else { + auto _ = mv$(scope); } // Result maintained from last node @@ -196,7 +209,7 @@ namespace { m_builder.push_stmt_assign( ::MIR::LValue::make_Return({}), m_builder.get_result(node.span()) ); // TODO: Insert drop of all current scopes - //terminate_scope_early( 0 ); + m_builder.terminate_scope_early( node.span(), m_builder.fcn_scope() ); m_builder.end_block( ::MIR::Terminator::make_Return({}) ); } void visit(::HIR::ExprNode_Let& node) override @@ -213,27 +226,24 @@ namespace { void visit(::HIR::ExprNode_Loop& node) override { TRACE_FUNCTION_F("_Loop"); - //auto loop_body_scope = m_builder.new_scope(node.span()); // TODO: Does loop actually need a scope? It usually contains a block. + auto loop_body_scope = m_builder.new_scope(node.span(), true); auto loop_block = m_builder.new_bb_linked(); auto loop_next = m_builder.new_bb_unlinked(); - m_loop_stack.push_back( LoopDesc { node.m_label, loop_block, loop_next } ); + m_loop_stack.push_back( LoopDesc { mv$(loop_body_scope), node.m_label, loop_block, loop_next } ); this->visit_node_ptr(node.m_code); + auto loop_scope = mv$(m_loop_stack.back().scope); m_loop_stack.pop_back(); // If there's a stray result, drop it if( m_builder.has_result() ) { assert( m_builder.block_active() ); - // TODO: Does this result have to be dropped? Ideally this should always be () - //::MIR::RValue res = m_builder.get_result(node.span()); - //if( res.is_Use() ) { - // m_builder.push_stmt_drop( mv$(res.as_Use()) ); - //} } // Terminate block with a jump back to the start if( m_builder.block_active() ) { // TODO: Insert drop of all scopes within the current scope + m_builder.terminate_scope( node.span(), mv$(loop_scope) ); m_builder.end_block( ::MIR::Terminator::make_Goto(loop_block) ); } m_builder.set_cur_block(loop_next); @@ -255,7 +265,7 @@ namespace { } // TODO: Insert drop of all active scopes within the loop - //terminate_scope_early( target_block->scope ); + m_builder.terminate_scope_early( node.span(), target_block->scope ); if( node.m_continue ) { m_builder.end_block( ::MIR::Terminator::make_Goto(target_block->cur) ); } @@ -299,25 +309,36 @@ namespace { auto result_val = m_builder.new_temporary(node.m_res_type); - m_builder.set_cur_block(true_branch); - this->visit_node_ptr(node.m_true); - if( m_builder.block_active() ) + // TODO: Scope handle cases where one arm moves a value but the other doesn't + { - m_builder.push_stmt_assign( result_val.clone(), m_builder.get_result(node.m_true->span()) ); - // TODO: Emit drops for all variables defined within this branch. (Needed? The inner should handle that) - m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); + m_builder.set_cur_block(true_branch); + auto branch_handle = m_builder.new_scope( node.m_true->span(), true ); + this->visit_node_ptr(node.m_true); + if( m_builder.block_active() ) { + m_builder.push_stmt_assign( result_val.clone(), m_builder.get_result(node.m_true->span()) ); + m_builder.terminate_scope(node.m_true->span(), mv$(branch_handle)); + m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); + } + else { + auto _ = mv$(branch_handle); + } } m_builder.set_cur_block(false_branch); if( node.m_false ) { + auto branch_scope = m_builder.new_scope( node.m_true->span(), true ); this->visit_node_ptr(node.m_false); if( m_builder.block_active() ) { m_builder.push_stmt_assign( result_val.clone(), m_builder.get_result(node.m_false->span()) ); - // TODO: Emit drops for all variables defined within this branch. + m_builder.terminate_scope(node.m_true->span(), mv$(branch_scope)); m_builder.end_block( ::MIR::Terminator::make_Goto(next_block) ); } + else { + auto _ = mv$(branch_scope); + } } else { @@ -1192,27 +1213,24 @@ namespace { ::MIR::FunctionPointer LowerMIR(const ::HIR::ExprPtr& ptr, const ::std::vector< ::std::pair< ::HIR::Pattern, ::HIR::TypeRef> >& args) { - ::MIR::Function fcn; + TRACE_FUNCTION; - ExprVisitor_Conv ev { fcn, ptr.m_bindings }; - - // 1. Apply destructuring to arguments - unsigned int i = 0; - for( const auto& arg : args ) - { - ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::make_Argument({i})); - } - - // 2. Destructure code - ::HIR::ExprNode& root_node = const_cast<::HIR::ExprNode&>(*ptr); - root_node.visit( ev ); + ::MIR::Function fcn; - if( ev.m_builder.has_result() ) { - ev.m_builder.push_stmt_assign( ::MIR::LValue::make_Return({}), ev.m_builder.get_result(root_node.span()) ); - } - if( ev.m_builder.block_active() ) + // TODO: Construct builder here and lend to ExprVisitor_Conv { - ev.m_builder.end_block( ::MIR::Terminator::make_Return({}) ); + ExprVisitor_Conv ev { fcn, ptr.m_bindings }; + + // 1. Apply destructuring to arguments + unsigned int i = 0; + for( const auto& arg : args ) + { + ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::make_Argument({i})); + } + + // 2. Destructure code + ::HIR::ExprNode& root_node = const_cast<::HIR::ExprNode&>(*ptr); + root_node.visit( ev ); } return ::MIR::FunctionPointer(new ::MIR::Function(mv$(fcn))); @@ -1292,10 +1310,25 @@ namespace { // -------------------------------------------------------------------- // MirBuilder // -------------------------------------------------------------------- +MirBuilder::~MirBuilder() +{ + if( has_result() ) + { + push_stmt_assign( ::MIR::LValue::make_Return({}), get_result(Span()) ); + } + if( block_active() ) + { + terminate_scope( Span(), mv$(m_fcn_scope) ); + end_block( ::MIR::Terminator::make_Return({}) ); + } +} + ::MIR::LValue MirBuilder::new_temporary(const ::HIR::TypeRef& ty) { unsigned int rv = m_output.temporaries.size(); m_output.temporaries.push_back( ty.clone() ); + temporaries_valid.push_back(false); + m_scopes.at( m_scope_stack.back() ).temporaries.push_back( rv ); return ::MIR::LValue::make_Temporary({rv}); } ::MIR::LValue MirBuilder::lvalue_or_temp(const ::HIR::TypeRef& ty, ::MIR::RValue val) @@ -1344,6 +1377,84 @@ void MirBuilder::push_stmt_assign(::MIR::LValue dst, ::MIR::RValue val) ASSERT_BUG(Span(), m_block_active, "Pushing statement with no active block"); ASSERT_BUG(Span(), dst.tag() != ::MIR::LValue::TAGDEAD, ""); ASSERT_BUG(Span(), val.tag() != ::MIR::RValue::TAGDEAD, ""); + + TU_MATCHA( (val), (e), + (Use, + this->moved_lvalue(e); + ), + (Constant, + ), + (SizedArray, + this->moved_lvalue(e.val); + ), + (Borrow, + ), + (Cast, + this->moved_lvalue(e.val); + ), + (BinOp, + switch(e.op) + { + case ::MIR::eBinOp::EQ: + case ::MIR::eBinOp::NE: + case ::MIR::eBinOp::GT: + case ::MIR::eBinOp::GE: + case ::MIR::eBinOp::LT: + case ::MIR::eBinOp::LE: + break; + default: + this->moved_lvalue(e.val_l); + this->moved_lvalue(e.val_r); + break; + } + ), + (UniOp, + this->moved_lvalue(e.val); + ), + (DstMeta, + // Doesn't move + ), + (MakeDst, + // Doesn't move ptr_val + this->moved_lvalue(e.meta_val); + ), + (Tuple, + for(const auto& val : e.vals) + this->moved_lvalue(val); + ), + (Array, + for(const auto& val : e.vals) + this->moved_lvalue(val); + ), + (Struct, + for(const auto& val : e.vals) + this->moved_lvalue(val); + ) + ) + + // Drop target if not a temporary + TU_IFLET( ::MIR::LValue, dst, Temporary, e, + if( temporaries_valid.size() <= e.idx ) { + temporaries_valid.resize(e.idx + 1); + } + //assert( !temporaries_valid[e.idx] ); + temporaries_valid[e.idx] = true; + ) + else TU_IFLET( ::MIR::LValue, dst, Return, _e, + ) + else TU_IFLET( ::MIR::LValue, dst, Variable, e, + if( variables_valid.size() <= e ) { + variables_valid.resize(e+1); + } + if( variables_valid[e] ) { + //this->push_stmt_drop( dst.clone() ); + } + variables_valid[e] = true; + ) + else { + //this->push_stmt_drop( dst.clone() ); + } + m_output.blocks.at(m_current_block).statements.push_back( ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) ); } void MirBuilder::push_stmt_drop(::MIR::LValue val) @@ -1397,6 +1508,132 @@ void MirBuilder::pause_cur_block() return rv; } +ScopeHandle MirBuilder::new_scope(const Span& sp, bool is_conditional) +{ + unsigned int idx = m_scopes.size(); + m_scopes.push_back( {} ); + m_scopes.back().is_conditional = is_conditional; + m_scope_stack.push_back( idx ); + DEBUG("START scope " << idx); + return ScopeHandle { *this, idx }; +} +void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope) +{ + DEBUG("DONE scope " << scope.idx); + // 1. Check that this is the current scope (at the top of the stack) + if( m_scope_stack.back() != scope.idx ) + { + DEBUG("m_scope_stack = " << m_scope_stack); + auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx ); + if( it == m_scope_stack.end() ) + BUG(sp, "Terminating scope not on the stack - scope " << scope.idx); + BUG(sp, "Terminating scope " << scope.idx << " when not at top of stack, " << (m_scope_stack.end() - it) << " in the way"); + } + m_scope_stack.pop_back(); + + auto& scope_def = m_scopes.at(scope.idx); + ASSERT_BUG( sp, scope_def.complete == false, "Terminating scope which is already terminated" ); + scope_def.complete = true; + + // 2. Emit drops for all non-moved variables (share with below) + drop_scope_values(scope_def); +} +void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope) +{ + DEBUG("EARLY scope " << scope.idx); + + // 1. Ensure that this block is in the stack + auto it = ::std::find( m_scope_stack.begin(), m_scope_stack.end(), scope.idx ); + if( it == m_scope_stack.end() ) { + BUG(sp, "Early-terminating scope not on the stack"); + } + unsigned int slot = it - m_scope_stack.begin(); + + for(unsigned int i = slot; i < m_scope_stack.size(); i ++) + { + } + + bool is_conditional = false; + for(unsigned int i = m_scope_stack.size(); i-- > slot; ) + { + auto idx = m_scope_stack[i]; + auto& scope_def = m_scopes.at( idx ); + + if( !is_conditional ) { + DEBUG("Complete scope " << idx); + scope_def.complete = true; + m_scope_stack.pop_back(); + } + else { + DEBUG("Drop part of scope " << idx); + // TODO: Mark the optional move/termination + } + // If a conditional block is hit, prevent full termination of the rest + if( scope_def.is_conditional ) + is_conditional = true; + + // 2. Emit drops for all non-moved variables within this branch + drop_scope_values(scope_def); + } +} +void MirBuilder::drop_scope_values(const ScopeDef& sd) +{ + for(auto tmp_idx : sd.temporaries) + { + if( temporaries_valid.at(tmp_idx) ) { + //push_stmt_drop( ::MIR::LValue::make_Temporary({ tmp_idx }) ); + temporaries_valid[tmp_idx] = false; + } + } +} +void MirBuilder::moved_lvalue(const ::MIR::LValue& lv) +{ + TU_MATCHA( (lv), (e), + (Variable, + //TODO(Span(), "Mark var as moved"); + ), + (Temporary, + //TODO(Span(), "Mark temp as moved"); + ), + (Argument, + //TODO(Span(), "Mark argument as moved"); + ), + (Static, + //TODO(Span(), "Static - Assert that type is Copy"); + ), + (Return, + BUG(Span(), "Read of return value"); + ), + (Field, + //TODO(Span(), "Mark field of an lvalue as moved (if not Copy) - Partially moved structs?"); + //moved_lvalue(*e.val); + ), + (Deref, + //TODO(Span(), "Mark deref of an lvalue as moved (if not Copy) - Req. DerefMove/&move otherwise"); + //moved_lvalue(*e.val); + ), + (Index, + //TODO(Span(), "Mark index of an lvalue as moved (if not Copy) - Req. IndexGet/IndexMove"); + //moved_lvalue(*e.val); + + moved_lvalue(*e.idx); + ), + (Downcast, + // TODO: What if the inner is Copy? What if the inner is a hidden pointer? + moved_lvalue(*e.val); + ) + ) +} + +ScopeHandle::~ScopeHandle() +{ + if( idx != ~0u ) + { + ASSERT_BUG(Span(), m_builder.m_scopes.size() > idx, "Scope invalid"); + ASSERT_BUG(Span(), m_builder.m_scopes.at(idx).complete, "Scope " << idx << " not completed"); + } +} + // -------------------------------------------------------------------- void HIR_GenerateMIR(::HIR::Crate& crate) |