summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hodge <tpg@mutabah.net>2017-02-04 10:42:21 +0800
committerJohn Hodge <tpg@mutabah.net>2017-02-04 10:42:21 +0800
commitc3c3c98e9346f500ec955a32200f27befd0fd6da (patch)
tree4b73d76dabf3ece9fd0cb9d6d63b5a9c8626bc0d
parentc72c7c2024df0d1ef53249f756f63d07d82fa581 (diff)
downloadmrust-c3c3c98e9346f500ec955a32200f27befd0fd6da.tar.gz
MIR Validate - Slight refactor for cleaner code
-rw-r--r--src/mir/check.cpp654
-rw-r--r--src/mir/helpers.hpp2
2 files changed, 331 insertions, 325 deletions
diff --git a/src/mir/check.cpp b/src/mir/check.cpp
index 4d7c198b..a2d5812a 100644
--- a/src/mir/check.cpp
+++ b/src/mir/check.cpp
@@ -84,6 +84,334 @@ namespace {
}
}
+// [ValState] = Value state tracking (use after move, uninit, ...)
+// - [ValState] No drops or usage of uninitalised values (Uninit, Moved, or Dropped)
+// - [ValState] Temporaries are write-once.
+// - Requires maintaining state information for all variables/temporaries with support for loops
+void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn)
+{
+ // > Iterate through code, creating state maps. Save map at the start of each bb.
+ struct ValStates {
+ enum class State {
+ Invalid,
+ Either,
+ Valid,
+ };
+ State ret_state = State::Invalid;
+ ::std::vector<State> arguments;
+ ::std::vector<State> temporaries;
+ ::std::vector<State> variables;
+
+ ValStates() {}
+ ValStates(size_t n_args, size_t n_temps, size_t n_vars):
+ arguments(n_args, State::Valid),
+ temporaries(n_temps),
+ variables(n_vars)
+ {
+ }
+
+ bool operator==(const ValStates& x) const {
+ if( ret_state != x.ret_state ) return false;
+ if( arguments != x.arguments ) return false;
+ if( temporaries != x.temporaries ) return false;
+ if( variables != x.variables ) return false;
+ return true;
+ }
+
+ bool empty() const {
+ return arguments.empty() && temporaries.empty() && variables.empty();
+ }
+
+ bool merge(ValStates& other)
+ {
+ if( this->empty() )
+ {
+ *this = other;
+ return true;
+ }
+ else if( this->arguments == other.arguments && this->temporaries == other.temporaries && this->variables == other.variables )
+ {
+ return false;
+ }
+ else
+ {
+ bool rv = false;
+ rv |= ValStates::merge_lists(this->arguments, other.arguments);
+ rv |= ValStates::merge_lists(this->temporaries, other.temporaries);
+ rv |= ValStates::merge_lists(this->variables, other.variables);
+ return rv;
+ }
+ }
+
+ void mark_validity(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv, bool is_valid)
+ {
+ TU_MATCH_DEF( ::MIR::LValue, (lv), (e),
+ (
+ ),
+ (Return,
+ ret_state = is_valid ? State::Valid : State::Invalid;
+ ),
+ (Argument,
+ MIR_ASSERT(state, e.idx < this->arguments.size(), "");
+ this->arguments[e.idx] = is_valid ? State::Valid : State::Invalid;
+ ),
+ (Variable,
+ MIR_ASSERT(state, e < this->variables.size(), "");
+ this->variables[e] = is_valid ? State::Valid : State::Invalid;
+ ),
+ (Temporary,
+ MIR_ASSERT(state, e.idx < this->temporaries.size(), "");
+ this->temporaries[e.idx] = is_valid ? State::Valid : State::Invalid;
+ )
+ )
+ }
+ void ensure_valid(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv)
+ {
+ TU_MATCH( ::MIR::LValue, (lv), (e),
+ (Variable,
+ MIR_ASSERT(state, e < this->variables.size(), "");
+ if( this->variables[e] != State::Valid )
+ MIR_BUG(state, "Use of non-valid variable - " << lv);
+ ),
+ (Temporary,
+ MIR_ASSERT(state, e.idx < this->temporaries.size(), "");
+ if( this->temporaries[e.idx] != State::Valid )
+ MIR_BUG(state, "Use of non-valid temporary - " << lv);
+ ),
+ (Argument,
+ MIR_ASSERT(state, e.idx < this->arguments.size(), "");
+ if( this->arguments[e.idx] != State::Valid )
+ MIR_BUG(state, "Use of non-valid argument - " << lv);
+ ),
+ (Return,
+ if( this->ret_state != State::Valid )
+ MIR_BUG(state, "Use of non-valid lvalue - " << lv);
+ ),
+ (Static,
+ ),
+ (Field,
+ ensure_valid(state, *e.val);
+ ),
+ (Deref,
+ ensure_valid(state, *e.val);
+ ),
+ (Index,
+ ensure_valid(state, *e.val);
+ ensure_valid(state, *e.idx);
+ ),
+ (Downcast,
+ ensure_valid(state, *e.val);
+ )
+ )
+ }
+ void move_val(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv)
+ {
+ ensure_valid(state, lv);
+ ::HIR::TypeRef tmp;
+ if( ! state.m_resolve.type_is_copy( state.sp, state.get_lvalue_type(tmp, lv) ) )
+ {
+ mark_validity(state, lv, false);
+ }
+ }
+ private:
+ static bool merge_lists(::std::vector<State>& a, ::std::vector<State>& b)
+ {
+ bool rv = false;
+ assert( a.size() == b.size() );
+ for(unsigned int i = 0; i < a.size(); i++)
+ {
+ if( a[i] != b[i] ) {
+ if( a[i] == State::Either || b[i] == State::Either ) {
+ rv = true;
+ }
+ a[i] = b[i] = State::Either;
+ }
+ }
+ return rv;
+ }
+ };
+ ::std::vector< ValStates> block_start_states( fcn.blocks.size() );
+ struct ToVisit {
+ unsigned int bb;
+ ::std::vector<unsigned int> path;
+ ValStates state;
+ };
+ ::std::vector<ToVisit> to_visit_blocks;
+
+ auto add_to_visit = [&](unsigned int idx, ::std::vector<unsigned int> src_path, auto vs) {
+ for(const auto& b : to_visit_blocks)
+ if( b.bb == idx && b.state == vs)
+ return ;
+ if( block_start_states.at(idx) == vs )
+ return ;
+ src_path.push_back(idx);
+ to_visit_blocks.push_back( ToVisit { idx, mv$(src_path), mv$(vs) } );
+ };
+ add_to_visit( 0, {}, ValStates { state.m_args.size(), fcn.temporaries.size(), fcn.named_variables.size() } );
+ while( to_visit_blocks.size() > 0 )
+ {
+ auto block = to_visit_blocks.back().bb;
+ auto path = mv$(to_visit_blocks.back().path);
+ auto val_state = mv$( to_visit_blocks.back().state );
+ to_visit_blocks.pop_back();
+ assert(block < fcn.blocks.size());
+
+ // 1. Apply current state to `block_start_states` (merging if needed)
+ // - If no change happened, skip.
+ if( ! block_start_states.at(block).merge( val_state ) ) {
+ continue ;
+ }
+ DEBUG("BB" << block << " via [" << path << "]");
+
+ // 2. Using the newly merged state, iterate statements checking the usage and updating state.
+ const auto& bb = fcn.blocks[block];
+ for(unsigned int stmt_idx = 0; stmt_idx < bb.statements.size(); stmt_idx ++)
+ {
+ const auto& stmt = bb.statements[stmt_idx];
+ state.set_cur_stmt(block, stmt_idx);
+
+ switch( stmt.tag() )
+ {
+ case ::MIR::Statement::TAGDEAD:
+ throw "";
+ case ::MIR::Statement::TAG_SetDropFlag:
+ break;
+ case ::MIR::Statement::TAG_Drop:
+ // Invalidate the slot
+ if( stmt.as_Drop().flag_idx == ~0u )
+ {
+ val_state.ensure_valid(state, stmt.as_Drop().slot);
+ }
+ val_state.mark_validity( state, stmt.as_Drop().slot, false );
+ break;
+ case ::MIR::Statement::TAG_Asm:
+ for(const auto& v : stmt.as_Asm().inputs)
+ val_state.ensure_valid(state, v.second);
+ for(const auto& v : stmt.as_Asm().outputs)
+ val_state.mark_validity( state, v.second, true );
+ break;
+ case ::MIR::Statement::TAG_Assign:
+ // Check source (and invalidate sources)
+ TU_MATCH( ::MIR::RValue, (stmt.as_Assign().src), (se),
+ (Use,
+ val_state.move_val(state, se);
+ ),
+ (Constant,
+ ),
+ (SizedArray,
+ val_state.move_val(state, se.val);
+ ),
+ (Borrow,
+ val_state.ensure_valid(state, se.val);
+ ),
+ (Cast,
+ // Well.. it's not exactly moved...
+ val_state.ensure_valid(state, se.val);
+ //val_state.move_val(state, se.val);
+ ),
+ (BinOp,
+ val_state.move_val(state, se.val_l);
+ val_state.move_val(state, se.val_r);
+ ),
+ (UniOp,
+ val_state.move_val(state, se.val);
+ ),
+ (DstMeta,
+ val_state.ensure_valid(state, se.val);
+ ),
+ (DstPtr,
+ val_state.ensure_valid(state, se.val);
+ ),
+ (MakeDst,
+ //val_state.move_val(state, se.ptr_val);
+ val_state.ensure_valid(state, se.ptr_val);
+ val_state.move_val(state, se.meta_val);
+ ),
+ (Tuple,
+ for(const auto& v : se.vals)
+ val_state.move_val(state, v);
+ ),
+ (Array,
+ for(const auto& v : se.vals)
+ val_state.move_val(state, v);
+ ),
+ (Variant,
+ val_state.move_val(state, se.val);
+ ),
+ (Struct,
+ for(const auto& v : se.vals)
+ val_state.move_val(state, v);
+ )
+ )
+ // Mark destination as valid
+ val_state.mark_validity( state, stmt.as_Assign().dst, true );
+ break;
+ }
+ }
+
+ // 3. Pass new state on to destination blocks
+ state.set_cur_stmt_term(block);
+ TU_MATCH(::MIR::Terminator, (bb.terminator), (e),
+ (Incomplete,
+ // Should be impossible here.
+ ),
+ (Return,
+ // Check if the return value has been set
+ val_state.ensure_valid( state, ::MIR::LValue::make_Return({}) );
+ // Ensure that no other non-Copy values are valid
+ for(unsigned int i = 0; i < val_state.variables.size(); i ++)
+ {
+ if( val_state.variables[i] == ValStates::State::Invalid )
+ {
+ }
+ else if( state.m_resolve.type_is_copy(state.sp, fcn.named_variables[i]) )
+ {
+ }
+ else
+ {
+ // TODO: Error, becuase this has just been leaked
+ }
+ }
+ ),
+ (Diverge,
+ // TODO: Ensure that cleanup has been performed.
+ ),
+ (Goto,
+ // Push block with the new state
+ add_to_visit( e, mv$(path), mv$(val_state) );
+ ),
+ (Panic,
+ // What should be done here?
+ ),
+ (If,
+ // Push blocks
+ val_state.ensure_valid( state, e.cond );
+ add_to_visit( e.bb0, path, val_state );
+ add_to_visit( e.bb1, mv$(path), mv$(val_state) );
+ ),
+ (Switch,
+ val_state.ensure_valid( state, e.val );
+ for(const auto& tgt : e.targets)
+ {
+ add_to_visit( tgt, path, val_state );
+ }
+ ),
+ (Call,
+ if( e.fcn.is_Value() )
+ val_state.ensure_valid( state, e.fcn.as_Value() );
+ for(const auto& arg : e.args)
+ val_state.move_val( state, arg );
+ // Push blocks (with return valid only in one)
+ add_to_visit(e.panic_block, path, val_state);
+
+ // TODO: If the function returns !, don't follow the ret_block
+ val_state.mark_validity( state, e.ret_val, true );
+ add_to_visit(e.ret_block, mv$(path), mv$(val_state));
+ )
+ )
+ }
+}
+
void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, const ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type)
{
TRACE_FUNCTION_F(path);
@@ -163,331 +491,7 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path
}
// [ValState] = Value state tracking (use after move, uninit, ...)
- // - [ValState] No drops or usage of uninitalised values (Uninit, Moved, or Dropped)
- // - [ValState] Temporaries are write-once.
- // - Requires maintaining state information for all variables/temporaries with support for loops
- {
- // > Iterate through code, creating state maps. Save map at the start of each bb.
- struct ValStates {
- enum class State {
- Invalid,
- Either,
- Valid,
- };
- State ret_state = State::Invalid;
- ::std::vector<State> arguments;
- ::std::vector<State> temporaries;
- ::std::vector<State> variables;
-
- ValStates() {}
- ValStates(size_t n_args, size_t n_temps, size_t n_vars):
- arguments(n_args, State::Valid),
- temporaries(n_temps),
- variables(n_vars)
- {
- }
-
- bool operator==(const ValStates& x) const {
- if( ret_state != x.ret_state ) return false;
- if( arguments != x.arguments ) return false;
- if( temporaries != x.temporaries ) return false;
- if( variables != x.variables ) return false;
- return true;
- }
-
- bool empty() const {
- return arguments.empty() && temporaries.empty() && variables.empty();
- }
-
- bool merge(ValStates& other)
- {
- if( this->empty() )
- {
- *this = other;
- return true;
- }
- else if( this->arguments == other.arguments && this->temporaries == other.temporaries && this->variables == other.variables )
- {
- return false;
- }
- else
- {
- bool rv = false;
- rv |= ValStates::merge_lists(this->arguments, other.arguments);
- rv |= ValStates::merge_lists(this->temporaries, other.temporaries);
- rv |= ValStates::merge_lists(this->variables, other.variables);
- return rv;
- }
- }
-
- void mark_validity(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv, bool is_valid)
- {
- TU_MATCH_DEF( ::MIR::LValue, (lv), (e),
- (
- ),
- (Return,
- ret_state = is_valid ? State::Valid : State::Invalid;
- ),
- (Argument,
- MIR_ASSERT(state, e.idx < this->arguments.size(), "");
- this->arguments[e.idx] = is_valid ? State::Valid : State::Invalid;
- ),
- (Variable,
- MIR_ASSERT(state, e < this->variables.size(), "");
- this->variables[e] = is_valid ? State::Valid : State::Invalid;
- ),
- (Temporary,
- MIR_ASSERT(state, e.idx < this->temporaries.size(), "");
- this->temporaries[e.idx] = is_valid ? State::Valid : State::Invalid;
- )
- )
- }
- void ensure_valid(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv)
- {
- TU_MATCH( ::MIR::LValue, (lv), (e),
- (Variable,
- MIR_ASSERT(state, e < this->variables.size(), "");
- if( this->variables[e] != State::Valid )
- MIR_BUG(state, "Use of non-valid variable - " << lv);
- ),
- (Temporary,
- MIR_ASSERT(state, e.idx < this->temporaries.size(), "");
- if( this->temporaries[e.idx] != State::Valid )
- MIR_BUG(state, "Use of non-valid temporary - " << lv);
- ),
- (Argument,
- MIR_ASSERT(state, e.idx < this->arguments.size(), "");
- if( this->arguments[e.idx] != State::Valid )
- MIR_BUG(state, "Use of non-valid argument - " << lv);
- ),
- (Return,
- if( this->ret_state != State::Valid )
- MIR_BUG(state, "Use of non-valid lvalue - " << lv);
- ),
- (Static,
- ),
- (Field,
- ensure_valid(state, *e.val);
- ),
- (Deref,
- ensure_valid(state, *e.val);
- ),
- (Index,
- ensure_valid(state, *e.val);
- ensure_valid(state, *e.idx);
- ),
- (Downcast,
- ensure_valid(state, *e.val);
- )
- )
- }
- void move_val(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv)
- {
- ensure_valid(state, lv);
- ::HIR::TypeRef tmp;
- if( ! state.m_resolve.type_is_copy( state.sp, state.get_lvalue_type(tmp, lv) ) )
- {
- mark_validity(state, lv, false);
- }
- }
- private:
- static bool merge_lists(::std::vector<State>& a, ::std::vector<State>& b)
- {
- bool rv = false;
- assert( a.size() == b.size() );
- for(unsigned int i = 0; i < a.size(); i++)
- {
- if( a[i] != b[i] ) {
- if( a[i] == State::Either || b[i] == State::Either ) {
- rv = true;
- }
- a[i] = b[i] = State::Either;
- }
- }
- return rv;
- }
- };
- ::std::vector< ValStates> block_start_states( fcn.blocks.size() );
- struct ToVisit {
- unsigned int bb;
- ::std::vector<unsigned int> path;
- ValStates state;
- };
- ::std::vector<ToVisit> to_visit_blocks;
-
- auto add_to_visit = [&](unsigned int idx, ::std::vector<unsigned int> src_path, auto vs) {
- for(const auto& b : to_visit_blocks)
- if( b.bb == idx && b.state == vs)
- return ;
- if( block_start_states.at(idx) == vs )
- return ;
- src_path.push_back(idx);
- to_visit_blocks.push_back( ToVisit { idx, mv$(src_path), mv$(vs) } );
- };
- add_to_visit( 0, {}, ValStates { args.size(), fcn.temporaries.size(), fcn.named_variables.size() } );
- while( to_visit_blocks.size() > 0 )
- {
- auto block = to_visit_blocks.back().bb;
- auto path = mv$(to_visit_blocks.back().path);
- auto val_state = mv$( to_visit_blocks.back().state );
- to_visit_blocks.pop_back();
- assert(block < fcn.blocks.size());
-
- // 1. Apply current state to `block_start_states` (merging if needed)
- // - If no change happened, skip.
- if( ! block_start_states.at(block).merge( val_state ) ) {
- continue ;
- }
- DEBUG("BB" << block << " via [" << path << "]");
-
- // 2. Using the newly merged state, iterate statements checking the usage and updating state.
- const auto& bb = fcn.blocks[block];
- for(unsigned int stmt_idx = 0; stmt_idx < bb.statements.size(); stmt_idx ++)
- {
- const auto& stmt = bb.statements[stmt_idx];
- state.set_cur_stmt(block, stmt_idx);
-
- switch( stmt.tag() )
- {
- case ::MIR::Statement::TAGDEAD:
- throw "";
- case ::MIR::Statement::TAG_SetDropFlag:
- break;
- case ::MIR::Statement::TAG_Drop:
- // Invalidate the slot
- if( stmt.as_Drop().flag_idx == ~0u )
- {
- val_state.ensure_valid(state, stmt.as_Drop().slot);
- }
- val_state.mark_validity( state, stmt.as_Drop().slot, false );
- break;
- case ::MIR::Statement::TAG_Asm:
- for(const auto& v : stmt.as_Asm().inputs)
- val_state.ensure_valid(state, v.second);
- for(const auto& v : stmt.as_Asm().outputs)
- val_state.mark_validity( state, v.second, true );
- break;
- case ::MIR::Statement::TAG_Assign:
- // Check source (and invalidate sources)
- TU_MATCH( ::MIR::RValue, (stmt.as_Assign().src), (se),
- (Use,
- val_state.move_val(state, se);
- ),
- (Constant,
- ),
- (SizedArray,
- val_state.move_val(state, se.val);
- ),
- (Borrow,
- val_state.ensure_valid(state, se.val);
- ),
- (Cast,
- // Well.. it's not exactly moved...
- val_state.ensure_valid(state, se.val);
- //val_state.move_val(state, se.val);
- ),
- (BinOp,
- val_state.move_val(state, se.val_l);
- val_state.move_val(state, se.val_r);
- ),
- (UniOp,
- val_state.move_val(state, se.val);
- ),
- (DstMeta,
- val_state.ensure_valid(state, se.val);
- ),
- (DstPtr,
- val_state.ensure_valid(state, se.val);
- ),
- (MakeDst,
- //val_state.move_val(state, se.ptr_val);
- val_state.ensure_valid(state, se.ptr_val);
- val_state.move_val(state, se.meta_val);
- ),
- (Tuple,
- for(const auto& v : se.vals)
- val_state.move_val(state, v);
- ),
- (Array,
- for(const auto& v : se.vals)
- val_state.move_val(state, v);
- ),
- (Variant,
- val_state.move_val(state, se.val);
- ),
- (Struct,
- for(const auto& v : se.vals)
- val_state.move_val(state, v);
- )
- )
- // Mark destination as valid
- val_state.mark_validity( state, stmt.as_Assign().dst, true );
- break;
- }
- }
-
- // 3. Pass new state on to destination blocks
- state.set_cur_stmt_term(block);
- TU_MATCH(::MIR::Terminator, (bb.terminator), (e),
- (Incomplete,
- // Should be impossible here.
- ),
- (Return,
- // Check if the return value has been set
- val_state.ensure_valid( state, ::MIR::LValue::make_Return({}) );
- // Ensure that no other non-Copy values are valid
- for(unsigned int i = 0; i < val_state.variables.size(); i ++)
- {
- if( val_state.variables[i] == ValStates::State::Invalid )
- {
- }
- else if( state.m_resolve.type_is_copy(state.sp, fcn.named_variables[i]) )
- {
- }
- else
- {
- // TODO: Error, becuase this has just been leaked
- }
- }
- ),
- (Diverge,
- // TODO: Ensure that cleanup has been performed.
- ),
- (Goto,
- // Push block with the new state
- add_to_visit( e, mv$(path), mv$(val_state) );
- ),
- (Panic,
- // What should be done here?
- ),
- (If,
- // Push blocks
- val_state.ensure_valid( state, e.cond );
- add_to_visit( e.bb0, path, val_state );
- add_to_visit( e.bb1, mv$(path), mv$(val_state) );
- ),
- (Switch,
- val_state.ensure_valid( state, e.val );
- for(const auto& tgt : e.targets)
- {
- add_to_visit( tgt, path, val_state );
- }
- ),
- (Call,
- if( e.fcn.is_Value() )
- val_state.ensure_valid( state, e.fcn.as_Value() );
- for(const auto& arg : e.args)
- val_state.move_val( state, arg );
- // Push blocks (with return valid only in one)
- add_to_visit(e.panic_block, path, val_state);
-
- // TODO: If the function returns !, don't follow the ret_block
- val_state.mark_validity( state, e.ret_val, true );
- add_to_visit(e.ret_block, mv$(path), mv$(val_state));
- )
- )
- }
- }
+ MIR_Validate_ValState(state, fcn);
// [Flat] = Basic checks (just iterates BBs)
// - [Flat] Types must be valid (correct type for slot etc.)
diff --git a/src/mir/helpers.hpp b/src/mir/helpers.hpp
index ab558791..ece20605 100644
--- a/src/mir/helpers.hpp
+++ b/src/mir/helpers.hpp
@@ -48,9 +48,11 @@ public:
const ::HIR::Crate& m_crate;
private:
::FmtLambda m_path;
+public:
const ::HIR::TypeRef& m_ret_type;
const args_t& m_args;
const ::MIR::Function& m_fcn;
+private:
const ::HIR::SimplePath* m_lang_Box = nullptr;
unsigned int bb_idx = 0;