summaryrefslogtreecommitdiff
path: root/src/mir/helpers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir/helpers.cpp')
-rw-r--r--src/mir/helpers.cpp1292
1 files changed, 1292 insertions, 0 deletions
diff --git a/src/mir/helpers.cpp b/src/mir/helpers.cpp
index 9dac1567..9242ccb7 100644
--- a/src/mir/helpers.cpp
+++ b/src/mir/helpers.cpp
@@ -10,6 +10,7 @@
#include <hir/hir.hpp>
#include <hir/type.hpp>
#include <mir/mir.hpp>
+#include <algorithm> // ::std::find
void ::MIR::TypeResolve::fmt_pos(::std::ostream& os) const
{
@@ -33,6 +34,13 @@ void ::MIR::TypeResolve::print_msg(const char* tag, ::std::function<void(::std::
//throw CheckFailure {};
}
+unsigned int ::MIR::TypeResolve::get_cur_stmt_ofs() const
+{
+ if( this->stmt_idx == STMT_TERM )
+ return m_fcn.blocks.at(this->bb_idx).statements.size();
+ else
+ return this->stmt_idx;
+}
const ::MIR::BasicBlock& ::MIR::TypeResolve::get_block(::MIR::BasicBlockId id) const
{
MIR_ASSERT(*this, id < m_fcn.blocks.size(), "Block ID " << id << " out of range");
@@ -271,3 +279,1287 @@ const ::HIR::TypeRef* ::MIR::TypeResolve::is_type_owned_box(const ::HIR::TypeRef
{
return m_resolve.is_type_owned_box(ty);
}
+
+using namespace MIR::visit;
+
+// --------------------------------------------------------------------
+// MIR_Helper_GetLifetimes
+// --------------------------------------------------------------------
+namespace MIR {
+namespace visit {
+ bool visit_mir_lvalue(const ::MIR::LValue& lv, ValUsage u, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb)
+ {
+ if( cb(lv, u) )
+ return true;
+ TU_MATCHA( (lv), (e),
+ (Variable,
+ ),
+ (Argument,
+ ),
+ (Temporary,
+ ),
+ (Static,
+ ),
+ (Return,
+ ),
+ (Field,
+ return visit_mir_lvalue(*e.val, u, cb);
+ ),
+ (Deref,
+ return visit_mir_lvalue(*e.val, ValUsage::Read, cb);
+ ),
+ (Index,
+ bool rv = false;
+ rv |= visit_mir_lvalue(*e.val, u, cb);
+ rv |= visit_mir_lvalue(*e.idx, ValUsage::Read, cb);
+ return rv;
+ ),
+ (Downcast,
+ return visit_mir_lvalue(*e.val, u, cb);
+ )
+ )
+ return false;
+ }
+
+ bool visit_mir_lvalue(const ::MIR::Param& p, ValUsage u, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb)
+ {
+ if( const auto* e = p.opt_LValue() )
+ {
+ if(cb(*e, ValUsage::Move))
+ return true;
+ return visit_mir_lvalue(*e, u, cb);
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ bool visit_mir_lvalues(const ::MIR::RValue& rval, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb)
+ {
+ bool rv = false;
+ TU_MATCHA( (rval), (se),
+ (Use,
+ if(cb(se, ValUsage::Move))
+ return true;
+ rv |= visit_mir_lvalue(se, ValUsage::Read, cb);
+ ),
+ (Constant,
+ ),
+ (SizedArray,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Read, cb);
+ ),
+ (Borrow,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Borrow, cb);
+ ),
+ (Cast,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Read, cb);
+ ),
+ (BinOp,
+ rv |= visit_mir_lvalue(se.val_l, ValUsage::Read, cb);
+ rv |= visit_mir_lvalue(se.val_r, ValUsage::Read, cb);
+ ),
+ (UniOp,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Read, cb);
+ ),
+ (DstMeta,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Read, cb);
+ ),
+ (DstPtr,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Read, cb);
+ ),
+ (MakeDst,
+ rv |= visit_mir_lvalue(se.ptr_val, ValUsage::Read, cb);
+ rv |= visit_mir_lvalue(se.meta_val, ValUsage::Read, cb);
+ ),
+ (Tuple,
+ for(auto& v : se.vals)
+ rv |= visit_mir_lvalue(v, ValUsage::Read, cb);
+ ),
+ (Array,
+ for(auto& v : se.vals)
+ rv |= visit_mir_lvalue(v, ValUsage::Read, cb);
+ ),
+ (Variant,
+ rv |= visit_mir_lvalue(se.val, ValUsage::Read, cb);
+ ),
+ (Struct,
+ for(auto& v : se.vals)
+ rv |= visit_mir_lvalue(v, ValUsage::Read, cb);
+ )
+ )
+ return rv;
+ }
+
+ bool visit_mir_lvalues(const ::MIR::Statement& stmt, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb)
+ {
+ bool rv = false;
+ TU_MATCHA( (stmt), (e),
+ (Assign,
+ rv |= visit_mir_lvalues(e.src, cb);
+ rv |= visit_mir_lvalue(e.dst, ValUsage::Write, cb);
+ ),
+ (Asm,
+ for(auto& v : e.inputs)
+ rv |= visit_mir_lvalue(v.second, ValUsage::Read, cb);
+ for(auto& v : e.outputs)
+ rv |= visit_mir_lvalue(v.second, ValUsage::Write, cb);
+ ),
+ (SetDropFlag,
+ ),
+ (Drop,
+ rv |= visit_mir_lvalue(e.slot, ValUsage::Move, cb);
+ ),
+ (ScopeEnd,
+ )
+ )
+ return rv;
+ }
+
+ bool visit_mir_lvalues(const ::MIR::Terminator& term, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb)
+ {
+ bool rv = false;
+ TU_MATCHA( (term), (e),
+ (Incomplete,
+ ),
+ (Return,
+ ),
+ (Diverge,
+ ),
+ (Goto,
+ ),
+ (Panic,
+ ),
+ (If,
+ rv |= visit_mir_lvalue(e.cond, ValUsage::Read, cb);
+ ),
+ (Switch,
+ rv |= visit_mir_lvalue(e.val, ValUsage::Read, cb);
+ ),
+ (Call,
+ if( e.fcn.is_Value() ) {
+ rv |= visit_mir_lvalue(e.fcn.as_Value(), ValUsage::Read, cb);
+ }
+ for(auto& v : e.args)
+ rv |= visit_mir_lvalue(v, ValUsage::Read, cb);
+ rv |= visit_mir_lvalue(e.ret_val, ValUsage::Write, cb);
+ )
+ )
+ return rv;
+ }
+ /*
+ void visit_mir_lvalues_mut(::MIR::TypeResolve& state, ::MIR::Function& fcn, ::std::function<bool(::MIR::LValue& , ValUsage)> cb)
+ {
+ for(unsigned int block_idx = 0; block_idx < fcn.blocks.size(); block_idx ++)
+ {
+ auto& block = fcn.blocks[block_idx];
+ for(auto& stmt : block.statements)
+ {
+ state.set_cur_stmt(block_idx, (&stmt - &block.statements.front()));
+ visit_mir_lvalues_mut(stmt, cb);
+ }
+ if( block.terminator.tag() == ::MIR::Terminator::TAGDEAD )
+ continue ;
+ state.set_cur_stmt_term(block_idx);
+ visit_mir_lvalues_mut(block.terminator, cb);
+ }
+ }
+ void visit_mir_lvalues(::MIR::TypeResolve& state, const ::MIR::Function& fcn, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb)
+ {
+ visit_mir_lvalues_mut(state, const_cast<::MIR::Function&>(fcn), [&](auto& lv, auto im){ return cb(lv, im); });
+ }
+ */
+} // namespace visit
+} // namespace MIR
+namespace
+{
+ struct ValueLifetime
+ {
+ ::std::vector<bool> stmt_bitmap;
+ ValueLifetime(size_t stmt_count):
+ stmt_bitmap(stmt_count)
+ {}
+
+ void fill(const ::std::vector<size_t>& block_offsets, size_t bb, size_t first_stmt, size_t last_stmt)
+ {
+ size_t limit = block_offsets[bb+1] - block_offsets[bb] - 1;
+ DEBUG("bb" << bb << " : " << first_stmt << "--" << last_stmt);
+ assert(first_stmt <= limit);
+ assert(last_stmt <= limit);
+ for(size_t stmt = first_stmt; stmt <= last_stmt; stmt++)
+ {
+ stmt_bitmap[block_offsets[bb] + stmt] = true;
+ }
+ }
+
+ void dump_debug(const char* suffix, unsigned i, const ::std::vector<size_t>& block_offsets)
+ {
+ ::std::string name = FMT(suffix << "$" << i);
+ while(name.size() < 3+1+3)
+ name += " ";
+ DEBUG(name << " : " << FMT_CB(os,
+ for(unsigned int j = 0; j < this->stmt_bitmap.size(); j++)
+ {
+ if(j != 0 && ::std::find(block_offsets.begin(), block_offsets.end(), j) != block_offsets.end())
+ os << "|";
+ os << (this->stmt_bitmap[j] ? "X" : " ");
+ }
+ ));
+ }
+ };
+}
+#if 1
+void MIR_Helper_GetLifetimes_DetermineValueLifetime(::MIR::TypeResolve& state, const ::MIR::Function& fcn, size_t bb_idx, size_t stmt_idx, const ::MIR::LValue& lv, const ::std::vector<size_t>& block_offsets, ValueLifetime& vl);
+
+::MIR::ValueLifetimes MIR_Helper_GetLifetimes(::MIR::TypeResolve& state, const ::MIR::Function& fcn, bool dump_debug)
+{
+ TRACE_FUNCTION_F(state);
+
+ size_t statement_count = 0;
+ ::std::vector<size_t> block_offsets;
+ block_offsets.reserve( fcn.blocks.size() );
+ for(const auto& bb : fcn.blocks)
+ {
+ block_offsets.push_back(statement_count);
+ statement_count += bb.statements.size() + 1; // +1 for the terminator
+ }
+ block_offsets.push_back(statement_count); // Store the final limit for later code to use.
+
+ ::std::vector<ValueLifetime> temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) );
+ ::std::vector<ValueLifetime> variable_lifetimes( fcn.named_variables.size(), ValueLifetime(statement_count) );
+
+
+ // Enumerate direct assignments of variables (linear iteration of BB list)
+ for(size_t bb_idx = 0; bb_idx < fcn.blocks.size(); bb_idx ++)
+ {
+ auto assigned_lvalue = [&](size_t bb_idx, size_t stmt_idx, const ::MIR::LValue& lv) {
+ // NOTE: Fills the first statement after running, just to ensure that any assigned value has _a_ lifetime
+ if( const auto* de = lv.opt_Variable() )
+ {
+ MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, variable_lifetimes[*de]);
+ variable_lifetimes[*de].fill(block_offsets, bb_idx, stmt_idx, stmt_idx);
+ }
+ else if( const auto* de = lv.opt_Temporary() )
+ {
+ MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, temporary_lifetimes[de->idx]);
+ temporary_lifetimes[de->idx].fill(block_offsets, bb_idx, stmt_idx, stmt_idx);
+ }
+ else
+ {
+ // Not a direct assignment of a slot
+ }
+ };
+
+ const auto& bb = fcn.blocks[bb_idx];
+ for(size_t stmt_idx = 0; stmt_idx < bb.statements.size(); stmt_idx ++)
+ {
+ state.set_cur_stmt(bb_idx, stmt_idx);
+ const auto& stmt = bb.statements[stmt_idx];
+ if( const auto* se = stmt.opt_Assign() )
+ {
+ // For assigned variables, determine how long that value will live
+ assigned_lvalue(bb_idx, stmt_idx+1, se->dst);
+ }
+ else if( const auto* se = stmt.opt_Asm() )
+ {
+ for(const auto& e : se->outputs)
+ {
+ assigned_lvalue(bb_idx, stmt_idx+1, e.second);
+ }
+ }
+ }
+ state.set_cur_stmt_term(bb_idx);
+
+ // Only Call can assign a value
+ TU_IFLET(::MIR::Terminator, bb.terminator, Call, te,
+ assigned_lvalue(te.ret_block, 0, te.ret_val);
+ )
+ }
+
+ // Dump out variable lifetimes.
+ if( dump_debug )
+ {
+ for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++)
+ {
+ temporary_lifetimes[i].dump_debug("tmp", i, block_offsets);
+ }
+ for(unsigned int i = 0; i < variable_lifetimes.size(); i ++)
+ {
+ variable_lifetimes[i].dump_debug("var", i, block_offsets);
+ }
+ }
+
+
+ ::MIR::ValueLifetimes rv;
+ rv.m_block_offsets = mv$(block_offsets);
+ rv.m_temporaries.reserve( temporary_lifetimes.size() );
+ for(auto& lft : temporary_lifetimes)
+ rv.m_temporaries.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) );
+ rv.m_variables.reserve( variable_lifetimes.size() );
+ for(auto& lft : variable_lifetimes)
+ rv.m_variables.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) );
+ return rv;
+}
+void MIR_Helper_GetLifetimes_DetermineValueLifetime(
+ ::MIR::TypeResolve& mir_res, const ::MIR::Function& fcn,
+ size_t bb_idx, size_t stmt_idx, // First statement in which the value is valid (after the assignment)
+ const ::MIR::LValue& lv, const ::std::vector<size_t>& block_offsets, ValueLifetime& vl
+ )
+{
+ TRACE_FUNCTION_F(mir_res << " " << lv);
+ // Walk the BB tree until:
+ // - Loopback
+ // - Assignment
+ // - Drop
+
+ struct State
+ {
+ const ::std::vector<size_t>& m_block_offsets;
+ ValueLifetime& m_out_vl;
+
+ ::std::vector<unsigned int> bb_history;
+ size_t last_read_ofs; // Statement index
+ bool m_is_borrowed;
+
+ State(const ::std::vector<size_t>& block_offsets, ValueLifetime& vl, size_t init_bb_idx, size_t init_stmt_idx):
+ m_block_offsets(block_offsets),
+ m_out_vl(vl),
+ bb_history(),
+ last_read_ofs(init_stmt_idx),
+ m_is_borrowed(false)
+ {
+ bb_history.push_back(init_bb_idx);
+ }
+ State(State&& x):
+ m_block_offsets(x.m_block_offsets),
+ m_out_vl(x.m_out_vl),
+ bb_history( mv$(x.bb_history) ),
+ last_read_ofs( x.last_read_ofs ),
+ m_is_borrowed( x.m_is_borrowed )
+ {
+ }
+ State& operator=(State&& x) {
+ this->bb_history = mv$(x.bb_history);
+ this->last_read_ofs = x.last_read_ofs;
+ this->m_is_borrowed = x.m_is_borrowed;
+ return *this;
+ }
+
+ State clone() const {
+ State rv { m_block_offsets, m_out_vl, 0, last_read_ofs };
+ rv.bb_history = bb_history;
+ rv.m_is_borrowed = m_is_borrowed;
+ return rv;
+ }
+
+ // Returns true if the variable has been borrowed
+ bool is_borrowed() const {
+ return this->m_is_borrowed;
+ }
+
+ void mark_borrowed(size_t stmt_idx) {
+ if( ! m_is_borrowed )
+ {
+ m_is_borrowed = false;
+ this->fill_to(stmt_idx);
+ }
+ m_is_borrowed = true;
+ }
+ void mark_read(size_t stmt_idx) {
+ if( !m_is_borrowed )
+ {
+ this->fill_to(stmt_idx);
+ }
+ else
+ {
+ m_is_borrowed = false;
+ this->fill_to(stmt_idx);
+ m_is_borrowed = true;
+ }
+ }
+ void fmt(::std::ostream& os) const {
+ os << "BB" << bb_history.front() << "/" << last_read_ofs << "--";
+ os << "[" << bb_history << "]";
+ }
+ void finalise(size_t stmt_idx)
+ {
+ if( m_is_borrowed )
+ {
+ m_is_borrowed = false;
+ this->fill_to(stmt_idx);
+ m_is_borrowed = true;
+ }
+ }
+ private:
+ void fill_to(size_t stmt_idx)
+ {
+ TRACE_FUNCTION_F(FMT_CB(ss, this->fmt(ss);));
+ assert( !m_is_borrowed );
+ assert(bb_history.size() > 0);
+ if( bb_history.size() == 1 )
+ {
+ // only one block
+ m_out_vl.fill(m_block_offsets, bb_history[0], last_read_ofs, stmt_idx);
+ }
+ else
+ {
+ // First block.
+ auto init_bb_idx = bb_history[0];
+ auto limit_0 = m_block_offsets[init_bb_idx+1] - m_block_offsets[init_bb_idx] - 1;
+ m_out_vl.fill(m_block_offsets, init_bb_idx, last_read_ofs, limit_0);
+
+ // Middle blocks
+ for(size_t i = 1; i < bb_history.size()-1; i++)
+ {
+ size_t bb_idx = bb_history[i];
+ assert(bb_idx+1 < m_block_offsets.size());
+ size_t limit = m_block_offsets[bb_idx+1] - m_block_offsets[bb_idx] - 1;
+ m_out_vl.fill(m_block_offsets, bb_idx, 0, limit);
+ }
+
+ // Last block
+ auto bb_idx = bb_history.back();
+ m_out_vl.fill(m_block_offsets, bb_idx, 0, stmt_idx);
+ }
+
+ last_read_ofs = stmt_idx;
+
+ auto cur = this->bb_history.back();
+ this->bb_history.clear();
+ this->bb_history.push_back(cur);
+ }
+ };
+
+ struct Runner
+ {
+ ::MIR::TypeResolve& m_mir_res;
+ const ::MIR::Function& m_fcn;
+ size_t m_init_bb_idx;
+ size_t m_init_stmt_idx;
+ const ::MIR::LValue& m_lv;
+ const ::std::vector<size_t>& m_block_offsets;
+ ValueLifetime& m_lifetimes;
+ bool m_is_copy;
+
+ ::std::vector<bool> m_visited_statements;
+
+ ::std::vector<::std::pair<size_t, State>> m_states_to_do;
+
+ Runner(::MIR::TypeResolve& mir_res, const ::MIR::Function& fcn, size_t init_bb_idx, size_t init_stmt_idx, const ::MIR::LValue& lv, const ::std::vector<size_t>& block_offsets, ValueLifetime& vl):
+ m_mir_res(mir_res),
+ m_fcn(fcn),
+ m_init_bb_idx(init_bb_idx),
+ m_init_stmt_idx(init_stmt_idx),
+ m_lv(lv),
+ m_block_offsets(block_offsets),
+ m_lifetimes(vl),
+
+ m_visited_statements( m_lifetimes.stmt_bitmap.size() )
+ {
+ ::HIR::TypeRef tmp;
+ m_is_copy = m_mir_res.m_resolve.type_is_copy(mir_res.sp, m_mir_res.get_lvalue_type(tmp, lv));
+ }
+
+ void run_block(size_t bb_idx, size_t stmt_idx, State state)
+ {
+ const auto& bb = m_fcn.blocks.at(bb_idx);
+ assert(stmt_idx <= bb.statements.size());
+
+ bool was_moved = false;
+ bool was_updated = false;
+ auto visit_cb = [&](const auto& lv, auto vu) {
+ if(lv == m_lv) {
+ if( vu == ValUsage::Read ) {
+ DEBUG(m_mir_res << "Used");
+ state.mark_read(stmt_idx);
+ was_updated = true;
+ }
+ if( vu == ValUsage::Move ) {
+ DEBUG(m_mir_res << (m_is_copy ? "Read" : "Moved"));
+ state.mark_read(stmt_idx);
+ was_moved = ! m_is_copy;
+ }
+ if( vu == ValUsage::Borrow ) {
+ DEBUG(m_mir_res << "Borrowed");
+ state.mark_borrowed(stmt_idx);
+ was_updated = true;
+ }
+ return true;
+ }
+ return false;
+ };
+
+ for( ; stmt_idx < bb.statements.size(); stmt_idx ++)
+ {
+ const auto& stmt = bb.statements[stmt_idx];
+ m_mir_res.set_cur_stmt(bb_idx, stmt_idx);
+ m_visited_statements[ m_block_offsets.at(bb_idx) + stmt_idx ] = true;
+
+ // Visit and see if the value is read (setting the read flag or end depending on if the value is Copy)
+ visit_mir_lvalues(stmt, visit_cb);
+
+ if( was_moved )
+ {
+ // Moved: Update read position and apply
+ DEBUG(m_mir_res << "Moved, return");
+ state.mark_read(stmt_idx);
+ state.finalise(stmt_idx);
+ return ;
+ }
+
+ TU_MATCHA( (stmt), (se),
+ (Assign,
+ if( se.dst == m_lv )
+ {
+ DEBUG(m_mir_res << "- Assigned to, return");
+ // Value assigned, just apply
+ state.finalise(stmt_idx);
+ return ;
+ }
+ ),
+ (Drop,
+ visit_mir_lvalue(se.slot, ValUsage::Read, visit_cb);
+ if( se.slot == m_lv )
+ {
+ // Value dropped, update read position and apply
+ DEBUG(m_mir_res << "- Dropped, return");
+ // - If it was borrowed, it can't still be borrowed here.
+ // TODO: Enable this once it's known to not cause mis-optimisation. It could currently.
+ //if( state.is_borrowed() ) {
+ // state.clear_borrowed();
+ //}
+ state.mark_read(stmt_idx);
+ state.finalise(stmt_idx);
+ return ;
+ }
+ ),
+ (Asm,
+ //
+ for(const auto& e : se.outputs)
+ {
+ if(e.second == m_lv) {
+ // Assigned, just apply
+ DEBUG(m_mir_res << "- Assigned (asm!), return");
+ state.finalise(stmt_idx);
+ return ;
+ }
+ }
+ ),
+ (SetDropFlag,
+ // Ignore
+ ),
+ (ScopeEnd,
+ // Ignore
+ )
+ )
+ }
+ m_mir_res.set_cur_stmt_term(bb_idx);
+ m_visited_statements[ m_block_offsets.at(bb_idx) + stmt_idx ] = true;
+
+ visit_mir_lvalues(bb.terminator, visit_cb);
+
+ if( was_moved )
+ {
+ // Moved: Update read position and apply
+ DEBUG(m_mir_res << "- Moved, return");
+ state.mark_read(stmt_idx);
+ state.finalise(stmt_idx);
+ return ;
+ }
+
+ // Terminator
+ TU_MATCHA( (bb.terminator), (te),
+ (Incomplete,
+ // TODO: Isn't this a bug?
+ DEBUG(m_mir_res << "Incomplete");
+ state.finalise(stmt_idx);
+ ),
+ (Return,
+ DEBUG(m_mir_res << "Return");
+ state.finalise(stmt_idx);
+ ),
+ (Diverge,
+ DEBUG(m_mir_res << "Diverge");
+ state.finalise(stmt_idx);
+ ),
+ (Goto,
+ m_states_to_do.push_back( ::std::make_pair(te, mv$(state)) );
+ ),
+ (Panic,
+ m_states_to_do.push_back( ::std::make_pair(te.dst, mv$(state)) );
+ ),
+ (If,
+ m_states_to_do.push_back( ::std::make_pair(te.bb0, state.clone()) );
+ m_states_to_do.push_back( ::std::make_pair(te.bb1, mv$(state)) );
+ ),
+ (Switch,
+ for(size_t i = 0; i < te.targets.size(); i ++)
+ {
+ auto s = (i == te.targets.size()-1)
+ ? mv$(state)
+ : state.clone();
+ m_states_to_do.push_back( ::std::make_pair(te.targets[i], mv$(s)) );
+ }
+ ),
+ (Call,
+ if( te.ret_val == m_lv )
+ {
+ DEBUG(m_mir_res << "Assigned (Call), return");
+ // Value assigned, just apply
+ state.finalise(stmt_idx);
+ return ;
+ }
+ if( m_fcn.blocks.at(te.panic_block).statements.size() == 0 && m_fcn.blocks.at(te.panic_block).terminator.is_Diverge() ) {
+ // Shortcut: Don't create a new state if the panic target is Diverge
+ }
+ else {
+ m_states_to_do.push_back( ::std::make_pair(te.panic_block, state.clone()) );
+ }
+ m_states_to_do.push_back( ::std::make_pair(te.ret_block, mv$(state)) );
+ )
+ )
+ }
+ };
+
+ ::std::vector<bool> use_bitmap(vl.stmt_bitmap.size()); // Bitmap of locations where this value is used.
+ {
+ size_t pos = 0;
+ for(const auto& bb : fcn.blocks)
+ {
+ for(const auto& stmt : bb.statements)
+ {
+ use_bitmap[pos] = visit_mir_lvalues(stmt, [&](const ::MIR::LValue& tlv, auto vu){ return tlv == lv && vu != ValUsage::Write; });
+ pos ++;
+ }
+ use_bitmap[pos] = visit_mir_lvalues(bb.terminator, [&](const ::MIR::LValue& tlv, auto vu){ return tlv == lv && vu != ValUsage::Write; });
+ pos ++;
+ }
+ }
+
+ Runner runner(mir_res, fcn, bb_idx, stmt_idx, lv, block_offsets, vl);
+ ::std::vector< ::std::pair<size_t,State>> post_check_list;
+
+ // TODO: Have a bitmap of visited statements. If a visted statement is hit, stop the current state
+ // - Use the same rules as loopback.
+
+ // Fill the first statement, to ensure that there is at least one bit set.
+ runner.run_block(bb_idx, stmt_idx, State(block_offsets, vl, bb_idx, stmt_idx));
+
+ while( ! runner.m_states_to_do.empty() )
+ {
+ auto bb_idx = runner.m_states_to_do.back().first;
+ auto state = mv$(runner.m_states_to_do.back().second);
+ runner.m_states_to_do.pop_back();
+
+ DEBUG("state.bb_history=[" << state.bb_history << "], -> BB" << bb_idx);
+ state.bb_history.push_back(bb_idx);
+
+ if( runner.m_visited_statements.at( block_offsets.at(bb_idx) + 0 ) )
+ {
+ if( vl.stmt_bitmap.at( block_offsets.at(bb_idx) + 0) )
+ {
+ DEBUG("Looped (to already valid)");
+ state.mark_read(0);
+ state.finalise(0);
+ continue ;
+ }
+ else if( state.is_borrowed() )
+ {
+ DEBUG("Looped (borrowed)");
+ state.mark_read(0);
+ state.finalise(0);
+ continue ;
+ }
+ else
+ {
+ // Put this state elsewhere and check if the variable is known valid at that point.
+ DEBUG("Looped (after last read), push for later");
+ post_check_list.push_back( ::std::make_pair(bb_idx, mv$(state)) );
+ continue ;
+ }
+ }
+
+#if 0
+ // TODO: Have a bitmap of if a BB mentions this value. If there are no unvisited BBs that mention this value, stop early.
+ // - CATCH: The original BB contains a reference, but might not have been visited (if it was the terminating call that triggered)
+ // - Also, we don't want to give up early (if we loop back to the start of the first block)
+ // - A per-statement bitmap would solve this. Return early if `!vl.stmt_bitmap & usage_stmt_bitmap == 0`
+ // > Requires filling the bitmap as we go (for maximum efficiency)
+ {
+ bool found_non_visited = false;
+ for(size_t i = 0; i < use_bitmap.size(); i ++)
+ {
+ // If a place where the value is used is not present in the output bitmap
+ if( !vl.stmt_bitmap[i] && use_bitmap[i] )
+ {
+ DEBUG("- Still used at +" << i);
+ found_non_visited = true;
+ }
+ }
+ // If there were no uses of the variable that aren't covered by the lifetime bitmap
+ if( ! found_non_visited )
+ {
+ // Terminate early
+ DEBUG("Early terminate - All possible lifetimes covered");
+ state.finalise(0);
+ for(auto& s : runner.m_states_to_do)
+ {
+ s.second.bb_history.push_back(bb_idx);
+ s.second.finalise(0);
+ }
+ return ;
+ }
+ }
+#endif
+
+ // Special case for when doing multiple runs on the same output
+ if( vl.stmt_bitmap.at( block_offsets.at(bb_idx) + 0) )
+ {
+ DEBUG("Already valid in BB" << bb_idx);
+ state.mark_read(0);
+ state.finalise(0);
+ continue;
+ }
+#if 0
+ // TODO: Have a way of knowing if a state will never find a use (the negative of the above)
+ // - Requires knowing for sure that a BB doesn't end up using the value.
+ // - IDEA: Store a fork count and counts of Yes/No for each BB.
+ // > If ForkCount == No, the value isn't used in that branch.
+ if( runner.m_bb_counts[bb_idx].visit_count > 0
+ && runner.m_bb_counts[bb_idx].visit_count == runner.m_bb_counts[bb_idx].val_unused_count )
+ {
+ DEBUG("Target BB known to be not valid");
+ runner.apply_state(state, 0);
+ continue ;
+ }
+ runner.m_bb_counts[bb_idx].visit_count ++;
+#endif
+
+ runner.run_block(bb_idx, 0, mv$(state));
+ }
+
+ // Iterate while there are items in the post_check list
+ while( !post_check_list.empty() )
+ {
+ bool change = false;
+ for(auto it = post_check_list.begin(); it != post_check_list.end(); )
+ {
+ auto bb_idx = it->first;
+ auto& state = it->second;
+ // If the target of this loopback is valid, then the entire route to the loopback must have been valid
+ if( vl.stmt_bitmap.at( block_offsets.at(bb_idx) + 0) )
+ {
+ change = true;
+ DEBUG("Looped (now valid)");
+ state.mark_read(0);
+ state.finalise(0);
+
+ it = post_check_list.erase(it);
+ }
+ else
+ {
+ ++ it;
+ }
+ }
+ // Keep going while changes happen
+ if( !change )
+ break;
+ }
+}
+
+#else
+
+::MIR::ValueLifetimes MIR_Helper_GetLifetimes(::MIR::TypeResolve& state, const ::MIR::Function& fcn, bool dump_debug)
+{
+ TRACE_FUNCTION_F(state);
+ // New algorithm notes:
+ // ---
+ // The lifetime of a value starts when it is written, and ends the last time it is read
+ // - When a variable is read, end any existing lifetime and start a new one.
+ // - When the value is read, update the end of its lifetime.
+ // ---
+ // A lifetime is a range in the call graph (with a start and end, including list of blocks)
+ // - Representation: Bitmap with a bit per statement.
+ // - Record the current block path in general state, along with known active lifetimes
+
+ // TODO: If a value is borrowed, assume it lives forevermore
+ // - Ideally there would be borrow tracking to determine its actual required lifetime.
+ // - NOTE: This doesn't impact the borrows themselves, just the borrowee
+
+ // TODO: Add a statement type StorageDead (or similar?) that indicates the point where a values scope ends
+
+ // Scan through all possible paths in the graph (with loopback detection using a memory of the path)
+ // - If a loop is detected, determine if there were changes to the lifetime set during that pass
+ // > Changes are noticed by recording in the state structure when it triggers a change in the lifetime
+ // map.
+ struct Position
+ {
+ size_t path_index = 0; // index into the block path.
+ unsigned int stmt_idx = 0;
+
+ bool operator==(const Position& x) const {
+ return path_index == x.path_index && stmt_idx == x.stmt_idx;
+ }
+ };
+ struct ProtoLifetime
+ {
+ Position start;
+ Position end;
+
+ bool is_empty() const {
+ return start == end;
+ }
+ bool is_borrowed() const {
+ return this->end == Position { ~0u, ~0u };
+ }
+ };
+ static unsigned NEXT_INDEX = 0;
+ struct State
+ {
+ unsigned int index = 0;
+ ::std::vector<unsigned int> block_path;
+ ::std::vector<unsigned int> block_change_idx;
+ unsigned int cur_change_idx = 0;
+
+ // if read, update. If set, save and update
+ ::std::vector<ProtoLifetime> tmp_ends;
+ ::std::vector<ProtoLifetime> var_ends;
+
+ State(const ::MIR::Function& fcn):
+ tmp_ends( fcn.temporaries.size(), ProtoLifetime() ),
+ var_ends( fcn.named_variables.size(), ProtoLifetime() )
+ {
+ }
+
+ State clone() const {
+ auto rv = *this;
+ rv.index = ++NEXT_INDEX;
+ return rv;
+ }
+ };
+ NEXT_INDEX = 0;
+
+ size_t statement_count = 0;
+ ::std::vector<size_t> block_offsets;
+ block_offsets.reserve( fcn.blocks.size() );
+ for(const auto& bb : fcn.blocks)
+ {
+ block_offsets.push_back(statement_count);
+ statement_count += bb.statements.size() + 1; // +1 for the terminator
+ }
+
+ ::std::vector<ValueLifetime> temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) );
+ ::std::vector<ValueLifetime> variable_lifetimes( fcn.named_variables.size(), ValueLifetime(statement_count) );
+
+ struct BlockSeenLifetimes {
+ bool m_has_state = false;
+ const ::std::vector<size_t>& block_offsets;
+ ::std::vector< ::std::vector<unsigned int> > tmp;
+ ::std::vector< ::std::vector<unsigned int> > var;
+
+ BlockSeenLifetimes(const ::std::vector<size_t>& block_offsets, const ::MIR::Function& fcn):
+ block_offsets( block_offsets ),
+ tmp( fcn.temporaries.size() ),
+ var( fcn.named_variables.size() )
+ {}
+
+ bool has_state() const
+ {
+ return m_has_state;
+ }
+
+ bool try_merge(const State& val_state) const
+ {
+ // TODO: This logic isn't quite correct. Just becase a value's existing end is already marked as valid,
+ // doesn't mean that we have no new information.
+ // - Wait, doesn't it?
+ auto try_merge_lft = [&](const ProtoLifetime& lft, const ::std::vector<unsigned int>& seen)->bool {
+ if(lft.is_empty()) return false;
+ // TODO: What should be done for borrow flagged values
+ if(lft.is_borrowed()) return false;
+ auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx;
+
+ auto it = ::std::find(seen.begin(), seen.end(), end_idx);
+ return (it == seen.end());
+ };
+ for(size_t i = 0; i < val_state.tmp_ends.size(); i++)
+ {
+ if( try_merge_lft(val_state.tmp_ends[i], this->tmp[i]) )
+ return true;
+ }
+ for(size_t i = 0; i < val_state.var_ends.size(); i++)
+ {
+ if( try_merge_lft(val_state.var_ends[i], this->var[i]) )
+ return true;
+ }
+ return false;
+ }
+
+ bool merge(const State& val_state)
+ {
+ bool rv = false;
+ auto merge_lft = [&](const ProtoLifetime& lft, ::std::vector<unsigned int>& seen)->bool {
+ if(lft.is_empty()) return false;
+ // TODO: What should be done for borrow flagged values
+ if(lft.end == Position { ~0u, ~0u }) return false;
+ auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx;
+
+ auto it = ::std::find(seen.begin(), seen.end(), end_idx);
+ if( it == seen.end() )
+ {
+ seen.push_back( end_idx );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ };
+ for(size_t i = 0; i < val_state.tmp_ends.size(); i++)
+ {
+ rv |= merge_lft(val_state.tmp_ends[i], this->tmp[i]);
+ }
+ for(size_t i = 0; i < val_state.var_ends.size(); i++)
+ {
+ rv |= merge_lft(val_state.var_ends[i], this->var[i]);
+ }
+ m_has_state = true;
+ return rv;
+ }
+ };
+ ::std::vector<BlockSeenLifetimes> block_seen_lifetimes( fcn.blocks.size(), BlockSeenLifetimes(block_offsets, fcn) );
+
+ State init_state(fcn);
+
+ ::std::vector<::std::pair<unsigned int, State>> todo_queue;
+ todo_queue.push_back(::std::make_pair( 0, mv$(init_state) ));
+
+ while(!todo_queue.empty())
+ {
+ auto bb_idx = todo_queue.back().first;
+ auto val_state = mv$(todo_queue.back().second);
+ todo_queue.pop_back();
+ state.set_cur_stmt(bb_idx, 0);
+
+ // Fill alive time in the bitmap
+ // TODO: Maybe also store the range (as a sequence of {block,start,end})
+ auto add_lifetime_s = [&](State& val_state, const ::MIR::LValue& lv, const Position& start, const Position& end) {
+ assert(start.path_index <= end.path_index);
+ assert(start.path_index < end.path_index || start.stmt_idx <= end.stmt_idx);
+ if(start.path_index == end.path_index && start.stmt_idx == end.stmt_idx)
+ return;
+ DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ")");
+ ValueLifetime* lft;
+ if(const auto* e = lv.opt_Temporary())
+ {
+ lft = &temporary_lifetimes[e->idx];
+ }
+ else if(const auto* e = lv.opt_Variable())
+ {
+ lft = &variable_lifetimes[*e];
+ }
+ else
+ {
+ MIR_TODO(state, "[add_lifetime] " << lv);
+ return;
+ }
+
+ // Fill lifetime map for this temporary in the indicated range
+ bool did_set = false;
+ unsigned int j = start.stmt_idx;
+ unsigned int i = start.path_index;
+ while( i <= end.path_index && i < val_state.block_path.size() )
+ {
+ auto bb_idx = val_state.block_path.at(i);
+ const auto& bb = fcn.blocks[bb_idx];
+ MIR_ASSERT(state, j <= bb.statements.size(), "");
+ MIR_ASSERT(state, bb_idx < block_offsets.size(), "");
+
+ auto block_base = block_offsets.at(bb_idx);
+ auto idx = block_base + j;
+ if( !lft->stmt_bitmap.at(idx) )
+ {
+ lft->stmt_bitmap[idx] = true;
+ did_set = true;
+ }
+
+ if( i == end.path_index && j == (end.stmt_idx != ~0u ? end.stmt_idx : bb.statements.size()) )
+ break;
+
+ // If the current index is the terminator (one after the size)
+ if(j == bb.statements.size())
+ {
+ j = 0;
+ i++;
+ }
+ else
+ {
+ j ++;
+ }
+ }
+
+ // - If the above set a new bit, increment `val_state.cur_change_idx`
+ if( did_set )
+ {
+ DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ") - New information");
+ val_state.cur_change_idx += 1;
+ }
+ };
+ auto add_lifetime = [&](const ::MIR::LValue& lv, const Position& start, const Position& end) {
+ add_lifetime_s(val_state, lv, start, end);
+ };
+
+ auto apply_state = [&](State& state) {
+ // Apply all changes in this state, just in case there was new information
+ for(unsigned i = 0; i < fcn.temporaries.size(); i++)
+ add_lifetime_s( state, ::MIR::LValue::make_Temporary({i}), state.tmp_ends[i].start, state.tmp_ends[i].end );
+ for(unsigned i = 0; i < fcn.named_variables.size(); i++)
+ add_lifetime_s( state, ::MIR::LValue::make_Variable({i}), state.var_ends[i].start, state.var_ends[i].end );
+ };
+ auto add_to_visit = [&](unsigned int new_bb_idx, State new_state) {
+ auto& bb_memory_ent = block_seen_lifetimes[new_bb_idx];
+ if( !bb_memory_ent.has_state() )
+ {
+ // No recorded state, needs to be visited
+ DEBUG(state << " state" << new_state.index << " -> bb" << new_bb_idx << " (no existing state)");
+ }
+ else if( bb_memory_ent.try_merge(new_state) )
+ {
+ // This state has new information, needs to be visited
+ DEBUG(state << " state" << new_state.index << " -> bb" << new_bb_idx << " (new info)");
+ }
+ else
+ {
+ // Skip
+ // TODO: Acquire from the target block the actual end of any active lifetimes, then apply them.
+ DEBUG(state << " state" << new_state.index << " -> bb" << new_bb_idx << " - No new state, no push");
+ // - For all variables currently active, check if they're valid in the first statement of the target block.
+ // - If so, mark as valid at the end of the current block
+ auto bm_idx = block_offsets[new_bb_idx];
+ Position cur_pos;
+ cur_pos.path_index = val_state.block_path.size() - 1;
+ cur_pos.stmt_idx = fcn.blocks[bb_idx].statements.size();
+ for(unsigned i = 0; i < fcn.temporaries.size(); i++) {
+ if( ! new_state.tmp_ends[i].is_empty() && temporary_lifetimes[i].stmt_bitmap[bm_idx] ) {
+ DEBUG("- tmp$" << i << " - Active in target, assume active");
+ new_state.tmp_ends[i].end = cur_pos;
+ }
+ }
+ for(unsigned i = 0; i < fcn.named_variables.size(); i++) {
+ if( ! new_state.var_ends[i].is_empty() && variable_lifetimes[i].stmt_bitmap[bm_idx] ) {
+ DEBUG("- var$" << i << " - Active in target, assume active");
+ new_state.var_ends[i].end = cur_pos;
+ }
+ }
+ // - Apply whatever state was still active
+ apply_state(new_state);
+ return ;
+ }
+ todo_queue.push_back(::std::make_pair( new_bb_idx, mv$(new_state) ));
+ };
+
+ // Compare this state to a composite list of lifetimes seen in this block
+ // - Just compares the end of each proto lifetime
+ {
+ auto& bb_memory_ent = block_seen_lifetimes[bb_idx];
+ bool had_state = bb_memory_ent.has_state();
+ bool has_new = bb_memory_ent.merge(val_state);
+
+ if( !has_new && had_state )
+ {
+ DEBUG(state << " state" << val_state.index << " - No new entry state");
+ apply_state(val_state);
+
+ continue ;
+ }
+ }
+
+ // Check if this state has visited this block before, and if anything changed since last time
+ {
+ auto it = ::std::find(val_state.block_path.rbegin(), val_state.block_path.rend(), bb_idx);
+ if( it != val_state.block_path.rend() )
+ {
+ auto idx = &*it - &val_state.block_path.front();
+ if( val_state.block_change_idx[idx] == val_state.cur_change_idx )
+ {
+ DEBUG(state << " " << val_state.index << " Loop and no change");
+ continue ;
+ }
+ else
+ {
+ assert( val_state.block_change_idx[idx] < val_state.cur_change_idx );
+ DEBUG(state << " " << val_state.index << " --- Loop, " << val_state.cur_change_idx - val_state.block_change_idx[idx] << " changes");
+ }
+ }
+ else
+ {
+ DEBUG(state << " " << val_state.index << " ---");
+ }
+ val_state.block_path.push_back(bb_idx);
+ val_state.block_change_idx.push_back( val_state.cur_change_idx );
+ }
+
+ Position cur_pos;
+ cur_pos.path_index = val_state.block_path.size() - 1;
+ cur_pos.stmt_idx = 0;
+ auto lvalue_read = [&](const ::MIR::LValue& lv) {
+ ProtoLifetime* slot;
+ if(const auto* e = lv.opt_Temporary()) {
+ slot = &val_state.tmp_ends.at(e->idx);
+ }
+ else if(const auto* e = lv.opt_Variable()) {
+ slot = &val_state.var_ends.at(*e);
+ }
+ else {
+ return ;
+ }
+ // Update the last read location
+ //DEBUG("Update END " << lv << " to " << cur_pos);
+ slot->end = cur_pos;
+ };
+ auto lvalue_set = [&](const ::MIR::LValue& lv) {
+ ProtoLifetime* slot;
+ if(const auto* e = lv.opt_Temporary()) {
+ slot = &val_state.tmp_ends.at(e->idx);
+ }
+ else if(const auto* e = lv.opt_Variable()) {
+ slot = &val_state.var_ends.at(*e);
+ }
+ else {
+ return ;
+ }
+ // End whatever value was originally there, and insert this new one
+ slot->end = cur_pos;
+ add_lifetime(lv, slot->start, slot->end);
+ slot->start = cur_pos;
+ };
+ auto lvalue_borrow = [&](const ::MIR::LValue& lv) {
+ ProtoLifetime* slot;
+ if(const auto* e = lv.opt_Temporary()) {
+ slot = &val_state.tmp_ends.at(e->idx);
+ }
+ else if(const auto* e = lv.opt_Variable()) {
+ slot = &val_state.var_ends.at(*e);
+ }
+ else {
+ return ;
+ }
+ // TODO: Flag this value as currently being borrowed (a flag that never clears)
+ slot->end = Position { ~0u, ~0u };
+ };
+ auto visit_lval_cb = [&](const auto& lv, ValUsage vu)->bool{
+ if(vu == ValUsage::Read)
+ lvalue_read(lv);
+ if(vu == ValUsage::Borrow)
+ lvalue_borrow(lv);
+ if(vu == ValUsage::Write)
+ lvalue_set(lv);
+ return false;
+ };
+
+ // Run statements
+ for(const auto& stmt : fcn.blocks[bb_idx].statements)
+ {
+ auto stmt_idx = &stmt - &fcn.blocks[bb_idx].statements.front();
+ cur_pos.stmt_idx = stmt_idx;
+ state.set_cur_stmt(bb_idx, stmt_idx);
+ DEBUG(state << " " << stmt);
+
+ if( const auto* e = stmt.opt_Drop() )
+ {
+ visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{
+ if(vu == ValUsage::Read)
+ lvalue_read(lv);
+ return false;
+ });
+ lvalue_read(e->slot);
+ lvalue_set(e->slot);
+ }
+ else
+ {
+ visit_mir_lvalues(stmt, visit_lval_cb);
+ }
+ }
+ cur_pos.stmt_idx = fcn.blocks[bb_idx].statements.size();
+
+ state.set_cur_stmt_term(bb_idx);
+ DEBUG(state << "TERM " << fcn.blocks[bb_idx].terminator);
+ TU_MATCH(::MIR::Terminator, (fcn.blocks[bb_idx].terminator), (e),
+ (Incomplete,
+ // Should be impossible here.
+ ),
+ (Return,
+ // End all active lifetimes at their previous location.
+ apply_state(val_state);
+ ),
+ (Diverge,
+ apply_state(val_state);
+ ),
+ (Goto,
+ add_to_visit(e, mv$(val_state));
+ ),
+ (Panic,
+ // What should be done here?
+ ),
+ (If,
+ visit_mir_lvalue(e.cond, ValUsage::Read, visit_lval_cb);
+
+ // Push blocks
+ add_to_visit(e.bb0, val_state.clone());
+ add_to_visit(e.bb1, mv$(val_state));
+ ),
+ (Switch,
+ visit_mir_lvalue(e.val, ValUsage::Read, visit_lval_cb);
+ ::std::set<unsigned int> tgts;
+ for(const auto& tgt : e.targets)
+ tgts.insert(tgt);
+
+ for(const auto& tgt : tgts)
+ {
+ auto vs = (tgt == *tgts.rbegin() ? mv$(val_state) : val_state.clone());
+ add_to_visit(tgt, mv$(vs));
+ }
+ ),
+ (Call,
+ if( const auto* f = e.fcn.opt_Value() )
+ visit_mir_lvalue(*f, ValUsage::Read, visit_lval_cb);
+ for(const auto& arg : e.args)
+ if( const auto* e = arg.opt_LValue() )
+ visit_mir_lvalue(*e, ValUsage::Read, visit_lval_cb);
+
+ // Push blocks (with return valid only in one)
+ add_to_visit(e.panic_block, val_state.clone());
+
+ // TODO: If the function returns !, don't follow the ret_block
+ lvalue_set(e.ret_val);
+ add_to_visit(e.ret_block, mv$(val_state));
+ )
+ )
+ }
+
+ // Dump out variable lifetimes.
+ if( dump_debug )
+ {
+ for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++)
+ {
+ temporary_lifetimes[i].dump_debug("tmp", i, block_offsets);
+ }
+ for(unsigned int i = 0; i < variable_lifetimes.size(); i ++)
+ {
+ variable_lifetimes[i].dump_debug("var", i, block_offsets);
+ }
+ }
+
+ // Move lifetime bitmaps into the variable for the below code
+ ::MIR::ValueLifetimes rv;
+ rv.m_block_offsets = mv$(block_offsets);
+ rv.m_temporaries.reserve( temporary_lifetimes.size() );
+ for(auto& lft : temporary_lifetimes)
+ rv.m_temporaries.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) );
+ rv.m_variables.reserve( variable_lifetimes.size() );
+ for(auto& lft : variable_lifetimes)
+ rv.m_variables.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) );
+
+ return rv;
+}
+#endif