diff options
-rw-r--r-- | Notes/MIR-PackedLValue.txt | 65 | ||||
-rw-r--r-- | src/hir/deserialise.cpp | 32 | ||||
-rw-r--r-- | src/hir/serialise.cpp | 41 | ||||
-rw-r--r-- | src/hir_conv/bind.cpp | 27 | ||||
-rw-r--r-- | src/hir_conv/constant_evaluation.cpp | 112 | ||||
-rw-r--r-- | src/include/tagged_union.hpp | 7 | ||||
-rw-r--r-- | src/mir/check.cpp | 173 | ||||
-rw-r--r-- | src/mir/check_full.cpp | 230 | ||||
-rw-r--r-- | src/mir/cleanup.cpp | 82 | ||||
-rw-r--r-- | src/mir/dump.cpp | 35 | ||||
-rw-r--r-- | src/mir/from_hir.cpp | 76 | ||||
-rw-r--r-- | src/mir/from_hir.hpp | 15 | ||||
-rw-r--r-- | src/mir/from_hir_match.cpp | 112 | ||||
-rw-r--r-- | src/mir/helpers.cpp | 135 | ||||
-rw-r--r-- | src/mir/helpers.hpp | 11 | ||||
-rw-r--r-- | src/mir/mir.cpp | 189 | ||||
-rw-r--r-- | src/mir/mir.hpp | 402 | ||||
-rw-r--r-- | src/mir/mir_builder.cpp | 597 | ||||
-rw-r--r-- | src/mir/optimise.cpp | 587 | ||||
-rw-r--r-- | src/trans/auto_impls.cpp | 20 | ||||
-rw-r--r-- | src/trans/codegen_c.cpp | 287 | ||||
-rw-r--r-- | src/trans/codegen_mmir.cpp | 103 | ||||
-rw-r--r-- | src/trans/enumerate.cpp | 340 | ||||
-rw-r--r-- | src/trans/monomorphise.cpp | 40 |
24 files changed, 1863 insertions, 1855 deletions
diff --git a/Notes/MIR-PackedLValue.txt b/Notes/MIR-PackedLValue.txt new file mode 100644 index 00000000..ce889aee --- /dev/null +++ b/Notes/MIR-PackedLValue.txt @@ -0,0 +1,65 @@ +Problem statement: +- MIR LValues are very common, and suffer from excessive indirection when + dereferences and field accesses are present +- Many MIR analysis passes care most about the inner values +- Pointer chasing ruins cache locality + +Solution: Replace the tagged union tree with a flatteded structure + +Quirk: Indexing takes two LValues to produce one BUT one of those is of a +lesser class, so doesn't need to be treated the same. + + + +Structure proposal: +---- +A LValue is made up of: +- A root value (referencing a local, argument, static, or the return value) +- And a list of wrappers (dereference, field, downcast, index) + +Root values are encoded as a packed pointer/value and tag, with the tag stored in the low 2 bits of the pointer +- This allows a 32-bit pointer to a word to be stored, using the alignment bits as tag +- Arguments and locals are encoded with the argument/local index in the "data" bits +- Return value is encoded as argument `-1` (all 1 bits in data) +- Statics are encoded as a pointer to a `::HIR::Path` + - This adds a new pointer access and allocation vs the existing LValue structure + - HIR::Path already has a bunch of pointers in it, may not hurt that much (and may help by keeping the normal size + of the LValue down) + +Wrappers are stored as a vector of words, packed in a similar way to root values +- Dereference is stored just as an entry (could pack multiple derefs into one, but that would make handling more + difficult) +- Field/Downcast is stored with the field/variant index in the "data" bits +- Indexing is stored as a pointer to a LValue + - ALTERNATIVE: Could require that indexing always uses a local, and just store the local index + - This would vastly reduce complexity in handling the Index wrapper, BUT would add a new statement in some cases + - A quick scan of the MMIR output of libstd crates, shows that the vast majority of indexing cases are with a local + directly. + - Doing so would simplify monomorph/clone/serialise (no need to clone the wrapper list, just copy it). + - Would also improve comparison times + + + +Usecase comparisons +------ +(Using 32-bit architecture) + +NOTES: +- Existing `LValue` structure is 3 words long (1 tag, plus 2 pointers for largest variant) + - WRONG. It's actually far larger due to the ::HIR::Path embedded in it (estimate at least 8 pointers, very likely + more). That should be fixed given the number of LValue-s that exist + - Fixed now, had a slight improvement in compile times (and memory usage?) +- New structure is 4 words long (root value, plus len/cap/ptr for vector) + +- Field access via `&self` + - Old: Field(Deref(Argument(0)), 0) + - 12 + 12 + 12 = 36 bytes w/ 2 pointers + - New: LValue( Argument(0), { Deref, Field(0) } ) + - 16 + 8 = 24 bytes w/ 1 pointer + +- Array stored in `&self` (common in librand) + - `(*arg0).2[var16].0` + - Old: Field( Index(Field(Deref(Argument(0)), 2), Local(16)), 0 ) + - 12 * 5 + 12 = 72 bytes 2/ 5 pointers + - New: LValue( Argument(0), { Deref, Field(2), Index(16), Field(0) } ) + - 16 + 16 = 32 bytes w/ 1 pointer diff --git a/src/hir/deserialise.cpp b/src/hir/deserialise.cpp index 56671a04..35aeacca 100644 --- a/src/hir/deserialise.cpp +++ b/src/hir/deserialise.cpp @@ -464,32 +464,15 @@ rv = deserialise_mir_lvalue_(); return rv; } + ::MIR::LValue::Wrapper deserialise_mir_lvalue_wrapper() + { + return ::MIR::LValue::Wrapper::from_inner(m_in.read_count()); + } ::MIR::LValue deserialise_mir_lvalue_() { - switch(auto tag = m_in.read_tag()) - { - #define _(x, ...) case ::MIR::LValue::TAG_##x: return ::MIR::LValue::make_##x( __VA_ARGS__ ); - _(Return, {}) - _(Argument, { static_cast<unsigned int>(m_in.read_count()) } ) - _(Local, static_cast<unsigned int>(m_in.read_count()) ) - _(Static, box$(deserialise_path()) ) - _(Field, { - box$( deserialise_mir_lvalue() ), - static_cast<unsigned int>(m_in.read_count()) - } ) - _(Deref, { box$( deserialise_mir_lvalue() ) }) - _(Index, { - box$( deserialise_mir_lvalue() ), - box$( deserialise_mir_lvalue() ) - } ) - _(Downcast, { - box$( deserialise_mir_lvalue() ), - static_cast<unsigned int>(m_in.read_count()) - } ) - #undef _ - default: - BUG(Span(), "Bad tag for MIR::LValue - " << tag); - } + auto root_v = m_in.read_count(); + auto root = (root_v == 3 ? ::MIR::LValue::Storage::new_Static(deserialise_path()) : ::MIR::LValue::Storage::from_inner(root_v)); + return ::MIR::LValue( mv$(root), deserialise_vec<::MIR::LValue::Wrapper>() ); } ::MIR::RValue deserialise_mir_rvalue() { @@ -827,6 +810,7 @@ template<> DEF_D( ::HIR::TraitValueItem, return d.deserialise_traitvalueitem(); ) template<> DEF_D( ::MIR::Param, return d.deserialise_mir_param(); ) + template<> DEF_D( ::MIR::LValue::Wrapper, return d.deserialise_mir_lvalue_wrapper(); ) template<> DEF_D( ::MIR::LValue, return d.deserialise_mir_lvalue(); ) template<> DEF_D( ::MIR::Statement, return d.deserialise_mir_statement(); ) template<> DEF_D( ::MIR::BasicBlock, return d.deserialise_mir_basicblock(); ) diff --git a/src/hir/serialise.cpp b/src/hir/serialise.cpp index c15630d9..fc53e245 100644 --- a/src/hir/serialise.cpp +++ b/src/hir/serialise.cpp @@ -722,35 +722,18 @@ void serialise(const ::MIR::LValue& lv) { TRACE_FUNCTION_F("LValue = "<<lv); - m_out.write_tag( static_cast<int>(lv.tag()) ); - TU_MATCHA( (lv), (e), - (Return, - ), - (Argument, - m_out.write_count(e.idx); - ), - (Local, - m_out.write_count(e); - ), - (Static, - serialise_path(*e); - ), - (Field, - serialise(e.val); - m_out.write_count(e.field_index); - ), - (Deref, - serialise(e.val); - ), - (Index, - serialise(e.val); - serialise(e.idx); - ), - (Downcast, - serialise(e.val); - m_out.write_count(e.variant_index); - ) - ) + if( lv.m_root.is_Static() ) { + m_out.write_count(3); + serialise_path(lv.m_root.as_Static()); + } + else { + m_out.write_count( lv.m_root.get_inner() ); + } + serialise_vec(lv.m_wrappers); + } + void serialise(const ::MIR::LValue::Wrapper& w) + { + m_out.write_count(w.get_inner()); } void serialise(const ::MIR::RValue& val) { diff --git a/src/hir_conv/bind.cpp b/src/hir_conv/bind.cpp index 84854315..e3441157 100644 --- a/src/hir_conv/bind.cpp +++ b/src/hir_conv/bind.cpp @@ -704,30 +704,9 @@ namespace { struct H { static void visit_lvalue(Visitor& upper_visitor, ::MIR::LValue& lv) { - TU_MATCHA( (lv), (e), - (Return, - ), - (Local, - ), - (Argument, - ), - (Static, - upper_visitor.visit_path(*e, ::HIR::Visitor::PathContext::VALUE); - ), - (Field, - H::visit_lvalue(upper_visitor, *e.val); - ), - (Deref, - H::visit_lvalue(upper_visitor, *e.val); - ), - (Index, - H::visit_lvalue(upper_visitor, *e.val); - H::visit_lvalue(upper_visitor, *e.idx); - ), - (Downcast, - H::visit_lvalue(upper_visitor, *e.val); - ) - ) + if( lv.m_root.is_Static() ) { + upper_visitor.visit_path(lv.m_root.as_Static(), ::HIR::Visitor::PathContext::VALUE); + } } static void visit_constant(Visitor& upper_visitor, ::MIR::Constant& e) { diff --git a/src/hir_conv/constant_evaluation.cpp b/src/hir_conv/constant_evaluation.cpp index b97a6ae0..500ac490 100644 --- a/src/hir_conv/constant_evaluation.cpp +++ b/src/hir_conv/constant_evaluation.cpp @@ -293,61 +293,66 @@ namespace HIR { ::HIR::Literal& get_lval(const ::MIR::LValue& lv) { - TU_MATCHA( (lv), (e), + ::HIR::Literal* lit_ptr; + TRACE_FUNCTION_FR(lv, *lit_ptr); + TU_MATCHA( (lv.m_root), (e), (Return, - return retval; + lit_ptr = &retval; ), (Local, - if( e >= locals.size() ) - MIR_BUG(state, "Local index out of range - " << e << " >= " << locals.size()); - return locals[e]; + MIR_ASSERT(state, e < locals.size(), "Local index out of range - " << e << " >= " << locals.size()); + lit_ptr = &locals[e]; ), (Argument, - if( e.idx >= args.size() ) - MIR_BUG(state, "Local index out of range - " << e.idx << " >= " << args.size()); - return args[e.idx]; + MIR_ASSERT(state, e < args.size(), "Argument index out of range - " << e << " >= " << args.size()); + lit_ptr = &args[e]; ), (Static, - MIR_TODO(state, "LValue::Static - " << *e); - ), - (Field, - auto& val = get_lval(*e.val); - MIR_ASSERT(state, val.is_List(), "LValue::Field on non-list literal - " << val.tag_str() << " - " << lv); - auto& vals = val.as_List(); - MIR_ASSERT(state, e.field_index < vals.size(), "LValue::Field index out of range"); - return vals[ e.field_index ]; - ), - (Deref, - auto& val = get_lval(*e.val); - TU_MATCH_DEF( ::HIR::Literal, (val), (ve), - ( - MIR_TODO(state, "LValue::Deref - " << lv << " { " << val << " }"); - ), - (BorrowData, - return *ve; - ), - (String, - // Just clone the string (hack) - // - TODO: Create a list? - return val; - ) - ) - ), - (Index, - auto& val = get_lval(*e.val); - MIR_ASSERT(state, val.is_List(), "LValue::Index on non-list literal - " << val.tag_str() << " - " << lv); - auto& idx = get_lval(*e.idx); - MIR_ASSERT(state, idx.is_Integer(), "LValue::Index with non-integer index literal - " << idx.tag_str() << " - " << lv); - auto& vals = val.as_List(); - auto idx_v = static_cast<size_t>( idx.as_Integer() ); - MIR_ASSERT(state, idx_v < vals.size(), "LValue::Index index out of range"); - return vals[ idx_v ]; - ), - (Downcast, - MIR_TODO(state, "LValue::Downcast - " << lv); + MIR_TODO(state, "LValue::Static - " << e); ) ) - throw ""; + + for(const auto& w : lv.m_wrappers) + { + auto& val = *lit_ptr; + TU_MATCH_HDRA( (w), {) + TU_ARMA(Field, e) { + MIR_ASSERT(state, val.is_List(), "LValue::Field on non-list literal - " << val.tag_str() << " - " << lv); + auto& vals = val.as_List(); + MIR_ASSERT(state, e < vals.size(), "LValue::Field index out of range"); + lit_ptr = &vals[ e ]; + } + TU_ARMA(Deref, e) { + TU_MATCH_DEF( ::HIR::Literal, (val), (ve), + ( + MIR_TODO(state, "LValue::Deref - " << lv << " { " << val << " }"); + ), + (BorrowData, + lit_ptr = &*ve; + ), + (String, + // Just clone the string (hack) + // - TODO: Create a list? + lit_ptr = &val; + ) + ) + } + TU_ARMA(Index, e) { + MIR_ASSERT(state, val.is_List(), "LValue::Index on non-list literal - " << val.tag_str() << " - " << lv); + MIR_ASSERT(state, e < locals.size(), "LValue::Index index local out of range"); + auto& idx = locals[e]; + MIR_ASSERT(state, idx.is_Integer(), "LValue::Index with non-integer index literal - " << idx.tag_str() << " - " << lv); + auto& vals = val.as_List(); + auto idx_v = static_cast<size_t>( idx.as_Integer() ); + MIR_ASSERT(state, idx_v < vals.size(), "LValue::Index index out of range"); + lit_ptr = &vals[ idx_v ]; + } + TU_ARMA(Downcast, e) { + MIR_TODO(state, "LValue::Downcast - " << lv); + } + } + } + return *lit_ptr; } ::HIR::Literal read_lval(const ::MIR::LValue& lv) { @@ -481,17 +486,14 @@ namespace HIR { MIR_BUG(state, "Only shared borrows are allowed in constants"); } - if( e.type != ::HIR::BorrowType::Shared ) { - MIR_BUG(state, "Only shared borrows are allowed in constants"); - } - if( const auto* p = e.val.opt_Deref() ) { - if( p->val->is_Deref() ) - MIR_TODO(state, "Undo nested deref coercion - " << *p->val); - val = local_state.read_lval(*p->val); + if( !e.val.m_wrappers.empty() && e.val.m_wrappers.back().is_Deref() ) { + //if( p->val->is_Deref() ) + // MIR_TODO(state, "Undo nested deref coercion - " << *p->val); + val = local_state.read_lval(e.val.clone_unwrapped()); } - else if( const auto* p = e.val.opt_Static() ) { + else if( e.val.m_wrappers.empty() && e.val.m_root.is_Static() ){ // Borrow of a static, emit BorrowPath with the same path - val = ::HIR::Literal::make_BorrowPath( (*p)->clone() ); + val = ::HIR::Literal::make_BorrowPath( e.val.m_root.as_Static().clone() ); } else { auto inner_val = local_state.read_lval(e.val); diff --git a/src/include/tagged_union.hpp b/src/include/tagged_union.hpp index dab7f8ea..5a3359b8 100644 --- a/src/include/tagged_union.hpp +++ b/src/include/tagged_union.hpp @@ -28,7 +28,6 @@ #define TU_CASE(mod, class, var, name,src, ...) TU_CASE_BODY(mod,class,var, TU_CASE_ITEM(src,mod,var,name) __VA_ARGS__) #define TU_CASE2(mod, class, var, n1,s1, n2,s2, ...) TU_CASE_BODY(mod,class,var, TU_CASE_ITEM(s1,mod,var,n1) TU_CASE_ITEM(s2,mod,var,n2) __VA_ARGS__) - // Argument iteration #define TU_DISP0(n) #define TU_DISP1(n, _1) n _1 @@ -105,7 +104,7 @@ */ TU_MATCH_ARMS(CLASS, VAR, NAME, __VA_ARGS__)/* */ default: {TU_EXP DEF;} break;/* */} -#define TU_MATCH_BIND1(TAG, VAR, NAME) /*MATCH_BIND*/ auto& NAME = (VAR).as_##TAG(); (void)&NAME; +#define TU_MATCH_BIND1(TAG, VAR, NAME) /*MATCH_BIND*/ decltype((VAR).as_##TAG()) NAME = (VAR).as_##TAG(); (void)&NAME; #define TU_MATCH_BIND2_(TAG, v1,v2, n1,n2) TU_MATCH_BIND1(TAG, v1, n1) TU_MATCH_BIND1(TAG, v2, n2) #define TU_MATCH_BIND2(...) TU_EXP1( TU_MATCH_BIND2_(__VA_ARGS__) ) // << Exists to cause expansion of the vars #define TU_MATCH_ARM(CLASS, VAR, NAME, TAG, ...) case CLASS::TAG_##TAG: {/* @@ -119,12 +118,12 @@ #define TU_MATCH_HDR(VARS, brace) TU_MATCH_HDR_(::std::remove_reference<decltype(TU_FIRST VARS)>::type, VARS, brace) #define TU_MATCH_HDR_(CLASS, VARS, brace) switch( (TU_FIRST VARS).tag() ) brace case CLASS::TAGDEAD: assert(!"ERROR: destructed tagged union used"); // Evil hack: two for loops, the inner stops the outer after it's done. -#define TU_ARM(VAR, TAG, NAME) break; case ::std::remove_reference<decltype(VAR)>::type::TAG_##TAG: for(bool tu_lc = true; tu_lc; tu_lc=false) for(auto& NAME = (VAR).as_##TAG(); (void)NAME, tu_lc; tu_lc=false) +#define TU_ARM(VAR, TAG, NAME) break; case ::std::remove_reference<decltype(VAR)>::type::TAG_##TAG: for(bool tu_lc = true; tu_lc; tu_lc=false) for(decltype((VAR).as_##TAG()) NAME = (VAR).as_##TAG(); (void)NAME, tu_lc; tu_lc=false) #define TU_MATCH_HDRA(VARS, brace) TU_MATCH_HDRA_(::std::remove_reference<decltype(TU_FIRST VARS)>::type, VARS, brace) #define TU_MATCH_HDRA_(CLASS, VARS, brace) auto& tu_match_hdr2_v = (TU_FIRST VARS); switch( tu_match_hdr2_v.tag() ) brace case CLASS::TAGDEAD: assert(!"ERROR: destructed tagged union used"); // Evil hack: two for loops, the inner stops the outer after it's done. -#define TU_ARMA(TAG, NAME) break; case ::std::remove_reference<decltype(tu_match_hdr2_v)>::type::TAG_##TAG: for(bool tu_lc = true; tu_lc; tu_lc=false) for(auto& NAME = tu_match_hdr2_v.as_##TAG(); (void)NAME, tu_lc; tu_lc=false) +#define TU_ARMA(TAG, NAME) break; case ::std::remove_reference<decltype(tu_match_hdr2_v)>::type::TAG_##TAG: for(bool tu_lc = true; tu_lc; tu_lc=false) for(decltype(tu_match_hdr2_v.as_##TAG()) NAME = tu_match_hdr2_v.as_##TAG(); (void)NAME, tu_lc; tu_lc=false) //#define TU_TEST(VAL, ...) (VAL.is_##TAG() && VAL.as_##TAG() TEST) #define TU_TEST1(VAL, TAG1, TEST) (VAL.is_##TAG1() && VAL.as_##TAG1() TEST) diff --git a/src/mir/check.cpp b/src/mir/check.cpp index 0b032bbc..6ed34563 100644 --- a/src/mir/check.cpp +++ b/src/mir/check.cpp @@ -116,6 +116,11 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn { } + explicit ValStates(const ValStates& v) = default; + ValStates(ValStates&& v) = default; + ValStates& operator=(const ValStates& v) = delete; + ValStates& operator=(ValStates&& v) = default; + void fmt(::std::ostream& os) { os << "ValStates { "; switch(ret_state) @@ -159,12 +164,13 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn return locals.empty() && args.empty(); } + // NOTE: Moves if this state is empty bool merge(unsigned bb_idx, ValStates& other) { DEBUG("bb" << bb_idx << " this=" << FMT_CB(ss,this->fmt(ss);) << ", other=" << FMT_CB(ss,other.fmt(ss);)); if( this->empty() ) { - *this = other; + *this = ValStates(other); return true; } else if( *this == other ) @@ -183,34 +189,38 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn void mark_validity(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv, bool is_valid) { - TU_MATCH_DEF( ::MIR::LValue, (lv), (e), - ( - ), + if( !lv.m_wrappers.empty()) + { + return ; + } + TU_MATCHA( (lv.m_root), (e), (Return, ret_state = is_valid ? State::Valid : State::Invalid; ), (Argument, - MIR_ASSERT(state, e.idx < this->args.size(), "Argument index out of range"); - DEBUG("arg$" << e.idx << " = " << (is_valid ? "Valid" : "Invalid")); - this->args[e.idx] = is_valid ? State::Valid : State::Invalid; + MIR_ASSERT(state, e < this->args.size(), "Argument index out of range " << lv); + DEBUG("arg$" << e << " = " << (is_valid ? "Valid" : "Invalid")); + this->args[e] = is_valid ? State::Valid : State::Invalid; ), (Local, - MIR_ASSERT(state, e < this->locals.size(), "Local index out of range"); + MIR_ASSERT(state, e < this->locals.size(), "Local index out of range - " << lv); DEBUG("_" << e << " = " << (is_valid ? "Valid" : "Invalid")); this->locals[e] = is_valid ? State::Valid : State::Invalid; + ), + (Static, ) ) } void ensure_valid(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv) { - TU_MATCH( ::MIR::LValue, (lv), (e), + TU_MATCHA( (lv.m_root), (e), (Return, if( this->ret_state != State::Valid ) MIR_BUG(state, "Use of non-valid lvalue - " << lv); ), (Argument, - MIR_ASSERT(state, e.idx < this->args.size(), "Arg index out of range"); - if( this->args[e.idx] != State::Valid ) + MIR_ASSERT(state, e < this->args.size(), "Arg index out of range"); + if( this->args[e] != State::Valid ) MIR_BUG(state, "Use of non-valid lvalue - " << lv); ), (Local, @@ -219,27 +229,22 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn 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); ) ) + + for(const auto& w : lv.m_wrappers) + { + if( w.is_Index() ) + { + if( this->locals[w.as_Index()] != State::Valid ) + MIR_BUG(state, "Use of non-valid lvalue - " << ::MIR::LValue::new_Local(w.as_Index())); + } + } } 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) ) ) + if( ! state.lvalue_is_copy(lv) ) { mark_validity(state, lv, false); } @@ -284,20 +289,29 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn ::std::vector<unsigned int> path; ValStates state; }; + // TODO: Remove this? The path is useful, but the cloned states are really expensive + // - Option: Keep the paths, but only ever use the pre-set entry state? ::std::vector<ToVisit> to_visit_blocks; // TODO: Check that all used locals are also set (anywhere at all) - auto add_to_visit = [&](unsigned int idx, ::std::vector<unsigned int> src_path, auto vs) { + auto add_to_visit = [&](unsigned int idx, ::std::vector<unsigned int> src_path, ValStates& vs, bool can_move) { 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) } ); + // TODO: Update the target block, and only visit if we've induced a change + to_visit_blocks.push_back( ToVisit { idx, mv$(src_path), (can_move ? mv$(vs) : ValStates(vs)) } ); + }; + auto add_to_visit_move = [&](unsigned int idx, ::std::vector<unsigned int> src_path, ValStates vs) { + add_to_visit(idx, mv$(src_path), vs, true); + }; + auto add_to_visit_copy = [&](unsigned int idx, ::std::vector<unsigned int> src_path, ValStates& vs) { + add_to_visit(idx, mv$(src_path), vs, false); }; - add_to_visit( 0, {}, ValStates { state.m_args.size(), fcn.locals.size() } ); + add_to_visit_move( 0, {}, ValStates { state.m_args.size(), fcn.locals.size() } ); while( to_visit_blocks.size() > 0 ) { auto block = to_visit_blocks.back().bb; @@ -311,7 +325,8 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn if( ! block_start_states.at(block).merge(block, val_state) ) { continue ; } - DEBUG("BB" << block << " via [" << path << "]"); + ASSERT_BUG(Span(), val_state.locals.size() == fcn.locals.size(), ""); + DEBUG("BB" << block << " via [" << path << "] " << FMT_CB(ss,val_state.fmt(ss);)); // 2. Using the newly merged state, iterate statements checking the usage and updating state. const auto& bb = fcn.blocks[block]; @@ -411,13 +426,13 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn // 3. Pass new state on to destination blocks state.set_cur_stmt_term(block); DEBUG(state << bb.terminator); - TU_MATCH(::MIR::Terminator, (bb.terminator), (e), - (Incomplete, + TU_MATCH_HDRA( (bb.terminator), { ) + TU_ARMA(Incomplete, e) { // Should be impossible here. - ), - (Return, + } + TU_ARMA(Return, e) { // Check if the return value has been set - val_state.ensure_valid( state, ::MIR::LValue::make_Return({}) ); + val_state.ensure_valid( state, ::MIR::LValue::new_Return() ); // Ensure that no other non-Copy values are valid for(unsigned int i = 0; i < val_state.locals.size(); i ++) { @@ -432,51 +447,51 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn // TODO: Error, becuase this has just been leaked } } - ), - (Diverge, + } + TU_ARMA(Diverge, e) { // TODO: Ensure that cleanup has been performed. - ), - (Goto, + } + TU_ARMA(Goto, e) { // Push block with the new state - add_to_visit( e, mv$(path), mv$(val_state) ); - ), - (Panic, + add_to_visit_move( e, mv$(path), mv$(val_state) ); + } + TU_ARMA(Panic, e) { // What should be done here? - ), - (If, + } + TU_ARMA(If, e) { // 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, + add_to_visit_copy( e.bb0, path, val_state ); + add_to_visit_move( e.bb1, mv$(path), mv$(val_state) ); + } + TU_ARMA(Switch, e) { val_state.ensure_valid( state, e.val ); for(const auto& tgt : e.targets) { - add_to_visit( tgt, path, val_state ); + add_to_visit( tgt, path, val_state, (&tgt == &e.targets.back()) ); } - ), - (SwitchValue, + } + TU_ARMA(SwitchValue, e) { val_state.ensure_valid( state, e.val ); for(const auto& tgt : e.targets) { - add_to_visit( tgt, path, val_state ); + add_to_visit_copy( tgt, path, val_state ); + } + add_to_visit_move( e.def_target, path, mv$(val_state) ); } - add_to_visit( e.def_target, path, val_state ); - ), - (Call, + TU_ARMA(Call, e) { 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); + add_to_visit_copy(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)); - ) - ) + add_to_visit_move(e.ret_block, mv$(path), mv$(val_state)); + } + } } } @@ -838,33 +853,33 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path state.set_cur_stmt_term(bb_idx); DEBUG(state << bb.terminator); - TU_MATCH(::MIR::Terminator, (bb.terminator), (e), - (Incomplete, - ), - (Return, + TU_MATCH_HDRA( (bb.terminator), {) + TU_ARMA(Incomplete, e) { + } + TU_ARMA(Return, e) { // TODO: Check if the function can return (i.e. if its return type isn't an empty type) - ), - (Diverge, - ), - (Goto, - ), - (Panic, - ), - (If, + } + TU_ARMA(Diverge, e) { + } + TU_ARMA(Goto, e) { + } + TU_ARMA(Panic, e) { + } + TU_ARMA(If, e) { // Check that condition lvalue is a bool ::HIR::TypeRef tmp; const auto& ty = state.get_lvalue_type(tmp, e.cond); if( ty != ::HIR::CoreType::Bool ) { MIR_BUG(state, "Type mismatch in `If` - expected bool, got " << ty); } - ), - (Switch, + } + TU_ARMA(Switch, e) { // Check that the condition is an enum - ), - (SwitchValue, + } + TU_ARMA(SwitchValue, e) { // Check that the condition's type matches the values - ), - (Call, + } + TU_ARMA(Call, e) { if( e.fcn.is_Value() ) { ::HIR::TypeRef tmp; @@ -875,8 +890,8 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path } } // Typecheck arguments and return value - ) - ) + } + } } } diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index 24a4930a..0cc83c6f 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -446,68 +446,54 @@ namespace } const State& get_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const { - TU_MATCHA( (lv), (e), + const State* state_p = nullptr; + TU_MATCHA( (lv.m_root), (e), (Return, - return return_value; + state_p = &return_value; ), (Argument, - return args.at(e.idx); + state_p = &args.at(e); ), (Local, - return locals.at(e); + state_p = &locals.at(e); ), (Static, static State state_of_static(true); return state_of_static; - ), - (Field, - const auto& vs = get_lvalue_state(mir_res, *e.val); - if( vs.is_composite() ) - { - const auto& states = this->get_composite(mir_res, vs); - MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); - return states[e.field_index]; - } - else - { - return vs; + ) + ) + + for(const auto& w : lv.m_wrappers) + { + if( state_p->is_composite() ) { + break; } - ), - (Deref, - const auto& vs = get_lvalue_state(mir_res, *e.val); - if( vs.is_composite() ) - { + const auto& vs = *state_p; + state_p = nullptr; + + TU_MATCHA( (w), (e), + (Field, + const auto& states = this->get_composite(mir_res, vs); + MIR_ASSERT(mir_res, e < states.size(), "Field index out of range"); + state_p = &states[e]; + ), + (Deref, MIR_TODO(mir_res, "Deref with composite state"); - } - else - { - return vs; - } - ), - (Index, - const auto& vs_v = get_lvalue_state(mir_res, *e.val); - const auto& vs_i = get_lvalue_state(mir_res, *e.idx); - MIR_ASSERT(mir_res, !vs_v.is_composite(), ""); - MIR_ASSERT(mir_res, !vs_i.is_composite(), ""); - //return State(vs_v.is_valid() && vs_i.is_valid()); - MIR_ASSERT(mir_res, vs_i.is_valid(), "Indexing with an invalidated value"); - return vs_v; - ), - (Downcast, - const auto& vs_v = get_lvalue_state(mir_res, *e.val); - if( vs_v.is_composite() ) - { - const auto& states = this->get_composite(mir_res, vs_v); - MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size - " << StateFmt(*this, vs_v)); - return states[0]; - } - else - { - return vs_v; - } + ), + (Index, + const auto& vs_i = get_lvalue_state(mir_res, ::MIR::LValue::new_Local(e)); + MIR_ASSERT(mir_res, vs_i.is_valid(), "Indexing with an invalidated value"); + MIR_BUG(mir_res, "Indexing a composite state"); + ), + (Downcast, + const auto& states = this->get_composite(mir_res, vs); + MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size - " << StateFmt(*this, vs)); + state_p = &states[0]; + ) ) - ) - throw ""; + assert(state_p); + } + return *state_p; } void clear_state(const ::MIR::TypeResolve& mir_res, State& s) { @@ -522,42 +508,47 @@ namespace void set_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv, State new_vs) { TRACE_FUNCTION_F(lv << " = " << StateFmt(*this, new_vs) << " (from " << StateFmt(*this, get_lvalue_state(mir_res, lv)) << ")"); - TU_MATCHA( (lv), (e), + State* state_p = nullptr; + TU_MATCHA( (lv.m_root), (e), (Return, - this->clear_state(mir_res, return_value); - return_value = mv$(new_vs); + state_p = &return_value; ), (Argument, - auto& slot = args.at(e.idx); - this->clear_state(mir_res, slot); - slot = mv$(new_vs); + state_p = &args.at(e); ), (Local, - auto& slot = locals.at(e); - this->clear_state(mir_res, slot); - slot = mv$(new_vs); + state_p = &locals.at(e); ), (Static, - // Ignore. - ), - (Field, - const auto& cur_vs = get_lvalue_state(mir_res, *e.val); + return ; + ) + ) + + for(const auto& w : lv.m_wrappers) + { + auto& cur_vs = *state_p; + + // If this is not a composite, and it matches the new state if( !cur_vs.is_composite() && cur_vs == new_vs ) { - // Not a composite, and no state change + // Early return + return; } - else - { - ::std::vector<State>* states_p; + + state_p = nullptr; + TU_MATCHA( (w), (e), + (Field, + // Current isn't a composite, we need to change that if( !cur_vs.is_composite() ) { ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, *e.val); + const auto& ty = mir_res.get_lvalue_type(tmp, lv, /*wrapper_skip_count=*/(&lv.m_wrappers.back() - &w)); unsigned int n_fields = 0; if( const auto* e = ty.m_data.opt_Tuple() ) { n_fields = e->size(); } + // TODO: Fixed-size arrays else if( ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Struct() ) { const auto& e = ty.m_data.as_Path().binding.as_Struct(); @@ -573,94 +564,59 @@ namespace ) ) } - else { + else + { MIR_BUG(mir_res, "Unknown type being accessed with Field - " << ty); } - auto new_cur_vs = this->allocate_composite(n_fields, cur_vs); - set_lvalue_state(mir_res, *e.val, State(new_cur_vs)); - states_p = &this->get_composite(mir_res, new_cur_vs); - } - else - { - states_p = &this->get_composite(mir_res, cur_vs); + cur_vs = State(this->allocate_composite(n_fields, cur_vs)); } // Get composite state and assign into it - auto& states = *states_p; - MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); - this->clear_state(mir_res, states[e.field_index]); - states[e.field_index] = mv$(new_vs); - } - ), - (Deref, - const auto& cur_vs = get_lvalue_state(mir_res, *e.val); - if( !cur_vs.is_composite() && cur_vs == new_vs ) - { - // Not a composite, and no state change - } - else - { - ::std::vector<State>* states_p; + auto& states = this->get_composite(mir_res, cur_vs); + MIR_ASSERT(mir_res, e< states.size(), "Field index out of range"); + state_p = &states[e]; + ), + (Deref, if( !cur_vs.is_composite() ) { + // TODO: Should this check if the type is Box? //::HIR::TypeRef tmp; //const auto& ty = mir_res.get_lvalue_type(tmp, *e.val); - // TODO: Should this check if the type is Box? - auto new_cur_vs = this->allocate_composite(2, cur_vs); - set_lvalue_state(mir_res, *e.val, State(new_cur_vs)); - states_p = &this->get_composite(mir_res, new_cur_vs); - } - else - { - states_p = &this->get_composite(mir_res, cur_vs); + cur_vs = State(this->allocate_composite(2, cur_vs)); } // Get composite state and assign into it - auto& states = *states_p; + auto& states = this->get_composite(mir_res, cur_vs); MIR_ASSERT(mir_res, states.size() == 2, "Deref with invalid state list size"); - this->clear_state(mir_res, states[1]); - states[1] = mv$(new_vs); - } - ), - (Index, - const auto& vs_v = get_lvalue_state(mir_res, *e.val); - const auto& vs_i = get_lvalue_state(mir_res, *e.idx); - MIR_ASSERT(mir_res, !vs_v.is_composite(), ""); - MIR_ASSERT(mir_res, !vs_i.is_composite(), ""); + state_p = &states[1]; + ), + (Index, + const auto& vs_i = get_lvalue_state(mir_res, ::MIR::LValue::new_Local(e)); + MIR_ASSERT(mir_res, !cur_vs.is_composite(), ""); + MIR_ASSERT(mir_res, !vs_i.is_composite(), ""); - MIR_ASSERT(mir_res, vs_v.is_valid(), "Indexing an invalid value"); - MIR_ASSERT(mir_res, vs_i.is_valid(), "Indexing with an invalid index"); + MIR_ASSERT(mir_res, cur_vs.is_valid(), "Indexing an invalid value"); + MIR_ASSERT(mir_res, vs_i.is_valid(), "Indexing with an invalid index"); - // NOTE: Ignore - ), - (Downcast, - const auto& cur_vs = get_lvalue_state(mir_res, *e.val); - if( !cur_vs.is_composite() && cur_vs == new_vs ) - { - // Not a composite, and no state change - } - else - { - ::std::vector<State>* states_p; + // NOTE: Ignore + return ; + ), + (Downcast, if( !cur_vs.is_composite() ) { - auto new_cur_vs = this->allocate_composite(1, cur_vs); - set_lvalue_state(mir_res, *e.val, State(new_cur_vs)); - states_p = &this->get_composite(mir_res, new_cur_vs); + cur_vs = State(this->allocate_composite(1, cur_vs)); } - else - { - states_p = &this->get_composite(mir_res, cur_vs); - } - // Get composite state and assign into it - auto& states = *states_p; - MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size - " << *e.val << " - " << this->fmt_state(mir_res, *e.val)); + auto& states = this->get_composite(mir_res, cur_vs); + MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size - " << lv << " - " << StateFmt(*this, cur_vs)); this->clear_state(mir_res, states[0]); states[0] = mv$(new_vs); - } + ) ) - ) + assert(state_p); + } + this->clear_state(mir_res, *state_p); + *state_p = mv$(new_vs); } }; @@ -939,7 +895,7 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio (Incomplete, ), (Return, - state.ensure_lvalue_valid(mir_res, ::MIR::LValue::make_Return({})); + state.ensure_lvalue_valid(mir_res, ::MIR::LValue::new_Return()); if( ENABLE_LEAK_DETECTOR ) { auto ensure_dropped = [&](const State& s, const ::MIR::LValue& lv) { @@ -955,10 +911,10 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio } }; for(unsigned i = 0; i < state.locals.size(); i ++ ) { - ensure_dropped(state.locals[i], ::MIR::LValue::make_Local(i)); + ensure_dropped(state.locals[i], ::MIR::LValue::new_Local(i)); } for(unsigned i = 0; i < state.args.size(); i ++ ) { - ensure_dropped(state.args[i], ::MIR::LValue::make_Argument({i})); + ensure_dropped(state.args[i], ::MIR::LValue::new_Argument(i)); } } ), diff --git a/src/mir/cleanup.cpp b/src/mir/cleanup.cpp index 5d32d0c8..b535c7ce 100644 --- a/src/mir/cleanup.cpp +++ b/src/mir/cleanup.cpp @@ -33,7 +33,7 @@ struct MirMutator ::MIR::LValue new_temporary(::HIR::TypeRef ty) { - auto rv = ::MIR::LValue::make_Local( static_cast<unsigned int>(m_fcn.locals.size()) ); + auto rv = ::MIR::LValue::new_Local( static_cast<unsigned int>(m_fcn.locals.size()) ); m_fcn.locals.push_back( mv$(ty) ); return rv; } @@ -493,12 +493,13 @@ const ::HIR::Literal* MIR_Cleanup_GetConstant(const MIR::TypeResolve& state, con // Allocate a temporary for the vtable pointer itself auto vtable_lv = mutator.new_temporary( mv$(vtable_ty) ); // - Load the vtable and store it - auto ptr_lv = ::MIR::LValue::make_Deref({ box$(receiver_lvp.clone()) }); + auto ptr_lv = ::MIR::LValue::new_Deref( receiver_lvp.clone() ); MIR_Cleanup_LValue(state, mutator, ptr_lv); - auto vtable_rval = ::MIR::RValue::make_DstMeta({ mv$(*ptr_lv.as_Deref().val) }); + ptr_lv.m_wrappers.pop_back(); + auto vtable_rval = ::MIR::RValue::make_DstMeta({ mv$(ptr_lv) }); mutator.push_statement( ::MIR::Statement::make_Assign({ vtable_lv.clone(), mv$(vtable_rval) }) ); - auto fcn_lval = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(vtable_lv) })), vtable_idx }); + auto fcn_lval = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref( mv$(vtable_lv) ), vtable_idx ); ::HIR::TypeRef tmp; const auto& ty = state.get_lvalue_type(tmp, fcn_lval); @@ -526,23 +527,23 @@ const ::HIR::Literal* MIR_Cleanup_GetConstant(const MIR::TypeResolve& state, con ), (Tuple, for(unsigned int i = 0; i < se.size(); i ++ ) { - auto val = (i == se.size() - 1 ? mv$(lv) : lv.clone()); + auto val = ::MIR::LValue::new_Field( (i == se.size() - 1 ? mv$(lv) : lv.clone()), i ); if( i == str.m_struct_markings.coerce_unsized_index ) { - vals.push_back( H::get_unit_ptr(state, mutator, monomorph(se[i].ent), ::MIR::LValue::make_Field({ box$(val), i }) ) ); + vals.push_back( H::get_unit_ptr(state, mutator, monomorph(se[i].ent), mv$(val)) ); } else { - vals.push_back( ::MIR::LValue::make_Field({ box$(val), i }) ); + vals.push_back( mv$(val) ); } } ), (Named, for(unsigned int i = 0; i < se.size(); i ++ ) { - auto val = (i == se.size() - 1 ? mv$(lv) : lv.clone()); + auto val = ::MIR::LValue::new_Field( (i == se.size() - 1 ? mv$(lv) : lv.clone()), i ); if( i == str.m_struct_markings.coerce_unsized_index ) { - vals.push_back( H::get_unit_ptr(state, mutator, monomorph(se[i].second.ent), ::MIR::LValue::make_Field({ box$(val), i }) ) ); + vals.push_back( H::get_unit_ptr(state, mutator, monomorph(se[i].second.ent), mv$(val) ) ); } else { - vals.push_back( ::MIR::LValue::make_Field({ box$(val), i }) ); + vals.push_back( mv$(val) ); } } ) @@ -777,7 +778,7 @@ bool MIR_Cleanup_Unsize_GetMetadata(const ::MIR::TypeResolve& state, MirMutator& auto ty_d = monomorphise_type_with(state.sp, se[i].ent, monomorph_cb_d, false); auto ty_s = monomorphise_type_with(state.sp, se[i].ent, monomorph_cb_s, false); - auto new_rval = MIR_Cleanup_CoerceUnsized(state, mutator, ty_d, ty_s, ::MIR::LValue::make_Field({ box$(value.clone()), i })); + auto new_rval = MIR_Cleanup_CoerceUnsized(state, mutator, ty_d, ty_s, ::MIR::LValue::new_Field(value.clone(), i)); auto new_lval = mutator.in_temporary( mv$(ty_d), mv$(new_rval) ); ents.push_back( mv$(new_lval) ); @@ -793,7 +794,7 @@ bool MIR_Cleanup_Unsize_GetMetadata(const ::MIR::TypeResolve& state, MirMutator& } else { - ents.push_back( ::MIR::LValue::make_Field({ box$(value.clone()), i}) ); + ents.push_back( ::MIR::LValue::new_Field(value.clone(), i) ); } } ), @@ -806,7 +807,7 @@ bool MIR_Cleanup_Unsize_GetMetadata(const ::MIR::TypeResolve& state, MirMutator& auto ty_d = monomorphise_type_with(state.sp, se[i].second.ent, monomorph_cb_d, false); auto ty_s = monomorphise_type_with(state.sp, se[i].second.ent, monomorph_cb_s, false); - auto new_rval = MIR_Cleanup_CoerceUnsized(state, mutator, ty_d, ty_s, ::MIR::LValue::make_Field({ box$(value.clone()), i })); + auto new_rval = MIR_Cleanup_CoerceUnsized(state, mutator, ty_d, ty_s, ::MIR::LValue::new_Field(value.clone(), i)); auto new_lval = mutator.new_temporary( mv$(ty_d) ); mutator.push_statement( ::MIR::Statement::make_Assign({ new_lval.clone(), mv$(new_rval) }) ); @@ -823,7 +824,7 @@ bool MIR_Cleanup_Unsize_GetMetadata(const ::MIR::TypeResolve& state, MirMutator& } else { - ents.push_back( ::MIR::LValue::make_Field({ box$(value.clone()), i}) ); + ents.push_back( ::MIR::LValue::new_Field(value.clone(), i) ); } } ) @@ -865,7 +866,7 @@ bool MIR_Cleanup_Unsize_GetMetadata(const ::MIR::TypeResolve& state, MirMutator& void MIR_Cleanup_LValue(const ::MIR::TypeResolve& state, MirMutator& mutator, ::MIR::LValue& lval) { - TU_MATCHA( (lval), (le), + TU_MATCHA( (lval.m_root), (le), (Return, ), (Argument, @@ -873,30 +874,22 @@ void MIR_Cleanup_LValue(const ::MIR::TypeResolve& state, MirMutator& mutator, :: (Local, ), (Static, - ), - (Field, - MIR_Cleanup_LValue(state, mutator, *le.val); - ), - (Deref, - MIR_Cleanup_LValue(state, mutator, *le.val); - ), - (Index, - MIR_Cleanup_LValue(state, mutator, *le.val); - MIR_Cleanup_LValue(state, mutator, *le.idx); - ), - (Downcast, - MIR_Cleanup_LValue(state, mutator, *le.val); ) ) - // If this is a deref of Box, unpack and deref the inner pointer - if( lval.is_Deref() ) + for(size_t i = 0; i < lval.m_wrappers.size(); i ++) { - auto& le = lval.as_Deref(); + if( !lval.m_wrappers[i].is_Deref() ) { + continue ; + } + + // If this is a deref of Box, unpack and deref the inner pointer ::HIR::TypeRef tmp; - const auto& ty = state.get_lvalue_type(tmp, *le.val); + const auto& ty = state.get_lvalue_type(tmp, lval, lval.m_wrappers.size() - i); if( state.m_resolve.is_type_owned_box(ty) ) { + unsigned num_injected_fld_zeros = 0; + // Handle Box by extracting it to its pointer. // - Locate (or remember) which field in Box is the pointer, and replace the inner by that field // > Dumb idea, assume it's always the first field. Keep accessing until located. @@ -924,11 +917,16 @@ void MIR_Cleanup_LValue(const ::MIR::TypeResolve& state, MirMutator& mutator, :: tmp = monomorphise_type(state.sp, str.m_params, te.path.m_data.as_Generic().m_params, *ty_tpl); typ = &tmp; - auto new_lval = ::MIR::LValue::make_Field({ mv$(le.val), 0 }); - le.val = box$(new_lval); + num_injected_fld_zeros ++; } MIR_ASSERT(state, typ->m_data.is_Pointer(), "First non-path field in Box wasn't a pointer - " << *typ); // We have reached the pointer. Good. + + // Inject all of the field zero accesses (before the deref) + while(num_injected_fld_zeros--) + { + lval.m_wrappers.insert( lval.m_wrappers.begin() + i, ::MIR::LValue::Wrapper::new_Field(0) ); + } } } } @@ -1030,9 +1028,9 @@ void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ), (DstMeta, // HACK: Ensure that the box Deref conversion fires here. - auto v = ::MIR::LValue::make_Deref({ box$(re.val) }); - MIR_Cleanup_LValue(state, mutator, v); - re.val = mv$( *v.as_Deref().val ); + re.val.m_wrappers.push_back( ::MIR::LValue::Wrapper::new_Deref() ); + MIR_Cleanup_LValue(state, mutator, re.val); + re.val.m_wrappers.pop_back(); // If the type is an array (due to a monomorpised generic?) then replace. ::HIR::TypeRef tmp; @@ -1053,9 +1051,9 @@ void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ), (DstPtr, // HACK: Ensure that the box Deref conversion fires here. - auto v = ::MIR::LValue::make_Deref({ box$(re.val) }); - MIR_Cleanup_LValue(state, mutator, v); - re.val = mv$( *v.as_Deref().val ); + re.val.m_wrappers.push_back( ::MIR::LValue::Wrapper::new_Deref() ); + MIR_Cleanup_LValue(state, mutator, re.val); + re.val.m_wrappers.pop_back(); ), (MakeDst, MIR_Cleanup_Param(state, mutator, re.ptr_val); @@ -1218,13 +1216,13 @@ void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, e.args.reserve( fcn_ty.m_arg_types.size() ); for(unsigned int i = 0; i < fcn_ty.m_arg_types.size(); i ++) { - e.args.push_back( ::MIR::LValue::make_Field({ box$(args_lvalue.clone()), i }) ); + e.args.push_back( ::MIR::LValue::new_Field(args_lvalue.clone(), i) ); } // If the trait is Fn/FnMut, dereference the input value. if( pe.trait.m_path == resolve.m_lang_FnOnce ) e.fcn = mv$(fcn_lvalue); else - e.fcn = ::MIR::LValue::make_Deref({ box$(fcn_lvalue) }); + e.fcn = ::MIR::LValue::new_Deref( mv$(fcn_lvalue) ); } } ) diff --git a/src/mir/dump.cpp b/src/mir/dump.cpp index b02c1e5b..69ffa850 100644 --- a/src/mir/dump.cpp +++ b/src/mir/dump.cpp @@ -176,40 +176,7 @@ namespace { #undef FMT } void fmt_val(::std::ostream& os, const ::MIR::LValue& lval) { - TU_MATCHA( (lval), (e), - (Return, - os << "RETURN"; - ), - (Argument, - os << "arg$" << e.idx; - ), - (Local, - os << "_$" << e; - ), - (Static, - os << *e; - ), - (Field, - os << "("; - fmt_val(os, *e.val); - os << ")." << e.field_index; - ), - (Deref, - os << "*"; - fmt_val(os, *e.val); - ), - (Index, - os << "("; - fmt_val(os, *e.val); - os << ")["; - fmt_val(os, *e.idx); - os << "]"; - ), - (Downcast, - fmt_val(os, *e.val); - os << " as variant" << e.variant_index; - ) - ) + os << lval; } void fmt_val(::std::ostream& os, const ::MIR::Constant& e) { TU_MATCHA( (e), (ce), diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index 9e749811..d511a32a 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -177,7 +177,7 @@ namespace { for(size_t i = 0; i < pat.m_binding.m_implicit_deref_count; i ++) { - lval = ::MIR::LValue::make_Deref({ box$(lval) }); + lval = ::MIR::LValue::new_Deref(mv$(lval)); } switch( pat.m_binding.m_type ) @@ -215,35 +215,35 @@ namespace { for(size_t i = 0; i < pat.m_implicit_deref_count; i ++) { - lval = ::MIR::LValue::make_Deref({ box$( mv$(lval) ) }); + lval = ::MIR::LValue::new_Deref(mv$(lval)); } TU_MATCH_HDRA( (pat.m_data), {) TU_ARMA(Any, e) { } TU_ARMA(Box, e) { - destructure_from_ex(sp, *e.sub, ::MIR::LValue::make_Deref({ box$( mv$(lval) ) }), allow_refutable); + destructure_from_ex(sp, *e.sub, ::MIR::LValue::new_Deref(mv$(lval)), allow_refutable); } TU_ARMA(Ref, e) { - destructure_from_ex(sp, *e.sub, ::MIR::LValue::make_Deref({ box$( mv$(lval) ) }), allow_refutable); + destructure_from_ex(sp, *e.sub, ::MIR::LValue::new_Deref(mv$(lval)), allow_refutable); } TU_ARMA(Tuple, e) { for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ ) { - destructure_from_ex(sp, e.sub_patterns[i], ::MIR::LValue::make_Field({ box$( lval.clone() ), i}), allow_refutable); + destructure_from_ex(sp, e.sub_patterns[i], ::MIR::LValue::new_Field(lval.clone(), i), allow_refutable); } } TU_ARMA(SplitTuple, e) { assert(e.total_size >= e.leading.size() + e.trailing.size()); for(unsigned int i = 0; i < e.leading.size(); i ++ ) { - destructure_from_ex(sp, e.leading[i], ::MIR::LValue::make_Field({ box$( lval.clone() ), i}), allow_refutable); + destructure_from_ex(sp, e.leading[i], ::MIR::LValue::new_Field(lval.clone(), i), allow_refutable); } // TODO: Is there a binding in the middle? unsigned int ofs = e.total_size - e.trailing.size(); for(unsigned int i = 0; i < e.trailing.size(); i ++ ) { - destructure_from_ex(sp, e.trailing[i], ::MIR::LValue::make_Field({ box$( lval.clone() ), ofs+i}), allow_refutable); + destructure_from_ex(sp, e.trailing[i], ::MIR::LValue::new_Field(lval.clone(), ofs+i), allow_refutable); } } TU_ARMA(StructValue, e) { @@ -252,7 +252,7 @@ namespace { TU_ARMA(StructTuple, e) { for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ ) { - destructure_from_ex(sp, e.sub_patterns[i], ::MIR::LValue::make_Field({ box$( lval.clone() ), i}), allow_refutable); + destructure_from_ex(sp, e.sub_patterns[i], ::MIR::LValue::new_Field(lval.clone(), i), allow_refutable); } } TU_ARMA(Struct, e) { @@ -264,7 +264,7 @@ namespace { for(const auto& fld_pat : e.sub_patterns) { unsigned idx = ::std::find_if( fields.begin(), fields.end(), [&](const auto&x){ return x.first == fld_pat.first; } ) - fields.begin(); - destructure_from_ex(sp, fld_pat.second, ::MIR::LValue::make_Field({ box$( lval.clone() ), idx}), allow_refutable); + destructure_from_ex(sp, fld_pat.second, ::MIR::LValue::new_Field(lval.clone(), idx), allow_refutable); } } } @@ -302,10 +302,10 @@ namespace { ERROR(sp, E0000, "Variant " << variants[i].name << " not handled"); } } - auto lval_var = ::MIR::LValue::make_Downcast({ box$(mv$(lval)), e.binding_idx }); + auto lval_var = ::MIR::LValue::new_Downcast(mv$(lval), e.binding_idx); for(unsigned int i = 0; i < e.sub_patterns.size(); i ++ ) { - destructure_from_ex(sp, e.sub_patterns[i], ::MIR::LValue::make_Field({ box$( lval_var.clone() ), i}), allow_refutable); + destructure_from_ex(sp, e.sub_patterns[i], ::MIR::LValue::new_Field(lval_var.clone(), i), allow_refutable); } } TU_ARMA(EnumStruct, e) { @@ -316,11 +316,11 @@ namespace { const auto& str = *var.type.m_data.as_Path().binding.as_Struct(); ASSERT_BUG(sp, str.m_data.is_Named(), "Struct pattern on non-Named struct - " << e.path); const auto& fields = str.m_data.as_Named(); - auto lval_var = ::MIR::LValue::make_Downcast({ box$(mv$(lval)), e.binding_idx }); + auto lval_var = ::MIR::LValue::new_Downcast(mv$(lval), e.binding_idx); for(const auto& fld_pat : e.sub_patterns) { unsigned idx = ::std::find_if( fields.begin(), fields.end(), [&](const auto&x){ return x.first == fld_pat.first; } ) - fields.begin(); - destructure_from_ex(sp, fld_pat.second, ::MIR::LValue::make_Field({ box$( lval_var.clone() ), idx}), allow_refutable); + destructure_from_ex(sp, fld_pat.second, ::MIR::LValue::new_Field(lval_var.clone(), idx), allow_refutable); } } TU_ARMA(Slice, e) { @@ -335,7 +335,7 @@ namespace { for(unsigned int i = 0; i < e.sub_patterns.size(); i ++) { const auto& subpat = e.sub_patterns[i]; - destructure_from_ex(sp, subpat, ::MIR::LValue::make_Field({ box$(lval.clone()), i }), allow_refutable ); + destructure_from_ex(sp, subpat, ::MIR::LValue::new_Field(lval.clone(), i), allow_refutable ); } } else @@ -346,7 +346,7 @@ namespace { for(unsigned int i = 0; i < e.sub_patterns.size(); i ++) { const auto& subpat = e.sub_patterns[i]; - destructure_from_ex(sp, subpat, ::MIR::LValue::make_Field({ box$(lval.clone()), i }), allow_refutable ); + destructure_from_ex(sp, subpat, ::MIR::LValue::new_Field(lval.clone(), i), allow_refutable ); } } } @@ -374,7 +374,7 @@ namespace { for(unsigned int i = 0; i < e.leading.size(); i ++) { unsigned int idx = 0 + i; - destructure_from_ex(sp, e.leading[i], ::MIR::LValue::make_Field({ box$(lval.clone()), idx }), allow_refutable ); + destructure_from_ex(sp, e.leading[i], ::MIR::LValue::new_Field(lval.clone(), idx), allow_refutable ); } if( e.extra_bind.is_valid() ) { @@ -383,7 +383,7 @@ namespace { for(unsigned int i = 0; i < e.trailing.size(); i ++) { unsigned int idx = array_size - e.trailing.size() + i; - destructure_from_ex(sp, e.trailing[i], ::MIR::LValue::make_Field({ box$(lval.clone()), idx }), allow_refutable ); + destructure_from_ex(sp, e.trailing[i], ::MIR::LValue::new_Field(lval.clone(), idx), allow_refutable ); } } else @@ -409,13 +409,13 @@ namespace { ::MIR::LValue len_lval; if( e.extra_bind.is_valid() || e.trailing.size() > 0 ) { - len_lval = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, lval).clone() })); + len_lval = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, lval) })); } for(unsigned int i = 0; i < e.leading.size(); i ++) { unsigned int idx = i; - destructure_from_ex(sp, e.leading[i], ::MIR::LValue::make_Field({ box$(lval.clone()), idx }), allow_refutable ); + destructure_from_ex(sp, e.leading[i], ::MIR::LValue::new_Field(lval.clone(), idx), allow_refutable ); } if( e.extra_bind.is_valid() ) { @@ -427,7 +427,7 @@ namespace { ::HIR::BorrowType bt = H::get_borrow_type(sp, e.extra_bind); ::MIR::LValue ptr_val = m_builder.lvalue_or_temp(sp, ::HIR::TypeRef::new_borrow( bt, inner_type.clone() ), - ::MIR::RValue::make_Borrow({ 0, bt, ::MIR::LValue::make_Field({ box$(lval.clone()), static_cast<unsigned int>(e.leading.size()) }) }) + ::MIR::RValue::make_Borrow({ 0, bt, ::MIR::LValue::new_Field( lval.clone(), static_cast<unsigned int>(e.leading.size()) ) }) ); // TODO: Cast to raw pointer? Or keep as a borrow? @@ -442,7 +442,7 @@ namespace { auto sub_val = ::MIR::Param(::MIR::Constant::make_Uint({ e.trailing.size() - i, ::HIR::CoreType::Usize })); ::MIR::LValue ofs_val = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_BinOp({ len_lval.clone(), ::MIR::eBinOp::SUB, mv$(sub_val) }) ); // Recurse with the indexed value - destructure_from_ex(sp, e.trailing[i], ::MIR::LValue::make_Index({ box$(lval.clone()), box$(ofs_val) }), allow_refutable); + destructure_from_ex(sp, e.trailing[i], ::MIR::LValue::new_Index( lval.clone(), ofs_val.m_root.as_Local() ), allow_refutable); } } } @@ -574,7 +574,7 @@ namespace { TRACE_FUNCTION_F("_Return"); this->visit_node_ptr(node.m_value); - m_builder.push_stmt_assign( node.span(), ::MIR::LValue::make_Return({}), m_builder.get_result(node.span()) ); + m_builder.push_stmt_assign( node.span(), ::MIR::LValue::new_Return(), m_builder.get_result(node.span()) ); m_builder.terminate_scope_early( node.span(), m_builder.fcn_scope() ); m_builder.end_block( ::MIR::Terminator::make_Return({}) ); } @@ -1474,7 +1474,7 @@ namespace { limit_val = ::MIR::Constant::make_Uint({ e.size_val, ::HIR::CoreType::Usize }); ), (Slice, - limit_val = ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(node.m_value->span(), value).clone() }); + limit_val = ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(node.m_value->span(), value) }); ) ) @@ -1508,7 +1508,13 @@ namespace { m_builder.set_cur_block( arm_continue ); } - m_builder.set_result( node.span(), ::MIR::LValue::make_Index({ box$(value), box$(index) }) ); + if( !index.is_Local()) + { + auto local_idx = m_builder.new_temporary(::HIR::CoreType::Usize); + m_builder.push_stmt_assign(node.span(), local_idx.clone(), mv$(index)); + index = mv$(local_idx); + } + m_builder.set_result( node.span(), ::MIR::LValue::new_Index( mv$(value), index.m_root.as_Local() ) ); } void visit(::HIR::ExprNode_Deref& node) override @@ -1591,7 +1597,7 @@ namespace { } } - m_builder.set_result( node.span(), ::MIR::LValue::make_Deref({ box$(val) }) ); + m_builder.set_result( node.span(), ::MIR::LValue::new_Deref( mv$(val) ) ); } void visit(::HIR::ExprNode_Emplace& node) override @@ -1702,7 +1708,7 @@ namespace { // 3. Get the value and assign it into `place_raw` node.m_value->visit(*this); auto val = m_builder.get_result(node.span()); - m_builder.push_stmt_assign( node.span(), ::MIR::LValue::make_Deref({ box$(place_raw.clone()) }), mv$(val) ); + m_builder.push_stmt_assign( node.span(), ::MIR::LValue::new_Deref(place_raw.clone()), mv$(val), /*drop_destination=*/false ); // 3. Return a call to `finalize` ::HIR::Path finalize_path(::HIR::GenericPath {}); @@ -1806,9 +1812,7 @@ namespace { auto place = m_builder.new_temporary( place_type ); m_builder.push_stmt_assign(node.span(), place.clone(), ::MIR::RValue::make_Cast({ mv$(place_raw), place_type.clone() })); // 3. Do a non-dropping write into the target location (i.e. just a MIR assignment) - // TODO: This should indicate that the destination shouldn't be dropped, but since drops don't happen on - // reassign currently, that's not yet an issue. - m_builder.push_stmt_assign(node.span(), ::MIR::LValue::make_Deref({box$(place.clone())}), mv$(val)); + m_builder.push_stmt_assign(node.span(), ::MIR::LValue::new_Deref(place.clone()), mv$(val), /*drop_destination=*/false); // 4. Convert the pointer into an `owned_box` auto res_type = ::HIR::TypeRef::new_path(::HIR::GenericPath(lang_owned_box, mv$(trait_params_data)), &m_builder.crate().get_struct_by_path(node.span(), lang_owned_box)); auto res = m_builder.new_temporary(res_type); @@ -2056,20 +2060,20 @@ namespace { unsigned int idx; if( ::std::isdigit(node.m_field.c_str()[0]) ) { ::std::stringstream(node.m_field.c_str()) >> idx; - m_builder.set_result( node.span(), ::MIR::LValue::make_Field({ box$(val), idx }) ); + m_builder.set_result( node.span(), ::MIR::LValue::new_Field( mv$(val), idx ) ); } else if( const auto* bep = val_ty.m_data.as_Path().binding.opt_Struct() ) { const auto& str = **bep; const auto& fields = str.m_data.as_Named(); idx = ::std::find_if( fields.begin(), fields.end(), [&](const auto& x){ return x.first == node.m_field; } ) - fields.begin(); - m_builder.set_result( node.span(), ::MIR::LValue::make_Field({ box$(val), idx }) ); + m_builder.set_result( node.span(), ::MIR::LValue::new_Field( mv$(val), idx ) ); } else if( const auto* bep = val_ty.m_data.as_Path().binding.opt_Union() ) { const auto& unm = **bep; const auto& fields = unm.m_variants; idx = ::std::find_if( fields.begin(), fields.end(), [&](const auto& x){ return x.first == node.m_field; } ) - fields.begin(); - m_builder.set_result( node.span(), ::MIR::LValue::make_Downcast({ box$(val), idx }) ); + m_builder.set_result( node.span(), ::MIR::LValue::new_Downcast( mv$(val), idx ) ); } else { BUG(node.span(), "Field access on non-union/struct - " << val_ty); @@ -2202,7 +2206,7 @@ namespace { m_builder.set_result( node.span(), mv$(tmp) ); ), (Static, - m_builder.set_result( node.span(), ::MIR::LValue::make_Static(box$(node.m_path.clone())) ); + m_builder.set_result( node.span(), ::MIR::LValue::new_Static(node.m_path.clone()) ); ), (StructConstant, // TODO: Why is this still a PathValue? @@ -2321,7 +2325,7 @@ namespace { ASSERT_BUG(sp, str.m_data.is_Named(), ""); const ::HIR::t_struct_fields& fields = str.m_data.as_Named(); - ::MIR::LValue base_val; + auto base_val = ::MIR::LValue::new_Return(); if( node.m_base_value ) { DEBUG("_StructLiteral - base"); @@ -2362,7 +2366,7 @@ namespace { if( !node.m_base_value) { ERROR(node.span(), E0000, "Field '" << fields[i].first << "' not specified"); } - values[i] = ::MIR::LValue::make_Field({ box$( base_val.clone() ), i }); + values[i] = ::MIR::LValue::new_Field( base_val.clone(), i ); } else { // Partial move support will handle dropping the rest? @@ -2545,7 +2549,7 @@ namespace { else { ev.define_vars_from(ptr->span(), arg.first); - ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::make_Argument({i})); + ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::new_Argument(i)); } i ++; } diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp index 194fd0e0..a1f9a10f 100644 --- a/src/mir/from_hir.hpp +++ b/src/mir/from_hir.hpp @@ -188,13 +188,10 @@ public: // - Values ::MIR::LValue get_variable(const Span& sp, unsigned idx) const { - // DIASBLED: State tracking doesn't support arguments in loops/splits -#if 1 auto it = m_var_arg_mappings.find(idx); if(it != m_var_arg_mappings.end()) - return ::MIR::LValue::make_Argument({ it->second }); -#endif - return ::MIR::LValue::make_Local( idx ); + return ::MIR::LValue::new_Argument(it->second); + return ::MIR::LValue::new_Local(idx); } ::MIR::LValue new_temporary(const ::HIR::TypeRef& ty); ::MIR::LValue lvalue_or_temp(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::RValue val); @@ -224,7 +221,7 @@ public: // - Statements // Push an assignment. NOTE: This also marks the rvalue as moved - void push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val); + void push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val, bool drop_destination=true); // Push a drop (likely only used by scope cleanup) void push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int drop_flag=~0u); // Push a shallow drop (for Box) @@ -296,7 +293,7 @@ private: const VarState& get_slot_state(const Span& sp, unsigned int idx, SlotType type, unsigned int skip_count=0) const; VarState& get_slot_state_mut(const Span& sp, unsigned int idx, SlotType type); - const VarState& get_val_state(const Span& sp, const ::MIR::LValue& lv, unsigned int skip_count=0); + VarState* get_val_state_mut_p(const Span& sp, const ::MIR::LValue& lv); VarState& get_val_state_mut(const Span& sp, const ::MIR::LValue& lv); void terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_loop); @@ -306,11 +303,11 @@ private: void complete_scope(ScopeDef& sd); public: - void with_val_type(const Span& sp, const ::MIR::LValue& val, ::std::function<void(const ::HIR::TypeRef&)> cb) const; + void with_val_type(const Span& sp, const ::MIR::LValue& val, ::std::function<void(const ::HIR::TypeRef&)> cb, const ::MIR::LValue::Wrapper* stop_wrapper=nullptr) const; bool lvalue_is_copy(const Span& sp, const ::MIR::LValue& lv) const; // Obtain the base fat poiner for a dst reference. Errors if it wasn't via a fat pointer - const ::MIR::LValue& get_ptr_to_dst(const Span& sp, const ::MIR::LValue& lv) const; + ::MIR::LValue get_ptr_to_dst(const Span& sp, const ::MIR::LValue& lv) const; }; class MirConverter: diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index e3ddce30..2bfb1a27 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -1732,13 +1732,13 @@ namespace { ), (Tuple, ASSERT_BUG(sp, idx < e.size(), "Tuple index out of range"); - lval = ::MIR::LValue::make_Field({ box$(lval), idx }); + lval = ::MIR::LValue::new_Field(mv$(lval), idx); cur_ty = &e[idx]; ), (Path, if( idx == FIELD_DEREF ) { // TODO: Check that the path is Box - lval = ::MIR::LValue::make_Deref({ box$(lval) }); + lval = ::MIR::LValue::new_Deref( mv$(lval) ); cur_ty = &e.path.m_data.as_Generic().m_params.m_types.at(0); break; } @@ -1773,7 +1773,7 @@ namespace { else { cur_ty = &fld.ent; } - lval = ::MIR::LValue::make_Field({ box$(lval), idx }); + lval = ::MIR::LValue::new_Field(mv$(lval), idx); ), (Named, assert( idx < fields.size() ); @@ -1785,7 +1785,7 @@ namespace { else { cur_ty = &fld.ent; } - lval = ::MIR::LValue::make_Field({ box$(lval), idx }); + lval = ::MIR::LValue::new_Field(mv$(lval), idx); ) ) ), @@ -1804,7 +1804,7 @@ namespace { else { cur_ty = &fld.second.ent; } - lval = ::MIR::LValue::make_Downcast({ box$(lval), idx }); + lval = ::MIR::LValue::new_Downcast(mv$(lval), idx); ), (Enum, auto monomorph_to_ptr = [&](const auto& ty)->const auto* { @@ -1824,7 +1824,7 @@ namespace { const auto& var = variants[idx]; cur_ty = monomorph_to_ptr(var.type); - lval = ::MIR::LValue::make_Downcast({ box$(lval), idx }); + lval = ::MIR::LValue::new_Downcast(mv$(lval), idx); ) ) ), @@ -1841,7 +1841,7 @@ namespace { assert(idx < e.size_val); cur_ty = &*e.inner; if( idx < FIELD_INDEX_MAX ) - lval = ::MIR::LValue::make_Field({ box$(lval), idx }); + lval = ::MIR::LValue::new_Field(mv$(lval), idx); else { idx -= FIELD_INDEX_MAX; idx = FIELD_INDEX_MAX - idx; @@ -1851,16 +1851,16 @@ namespace { (Slice, cur_ty = &*e.inner; if( idx < FIELD_INDEX_MAX ) - lval = ::MIR::LValue::make_Field({ box$(lval), idx }); + lval = ::MIR::LValue::new_Field(mv$(lval), idx); else { idx -= FIELD_INDEX_MAX; idx = FIELD_INDEX_MAX - idx; // 1. Create an LValue containing the size of this slice subtract `idx` - auto len_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ builder.get_ptr_to_dst(sp, lval).clone() })); + auto len_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ builder.get_ptr_to_dst(sp, lval) })); auto sub_val = ::MIR::Param(::MIR::Constant::make_Uint({ idx, ::HIR::CoreType::Usize })); auto ofs_val = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_BinOp({ mv$(len_lval), ::MIR::eBinOp::SUB, mv$(sub_val) }) ); // 2. Return _Index with that value - lval = ::MIR::LValue::make_Index({ box$(lval), box$(ofs_val) }); + lval = ::MIR::LValue::new_Index(mv$(lval), ofs_val.as_Local()); } ), (Borrow, @@ -1874,7 +1874,7 @@ namespace { cur_ty = &*e.inner; } DEBUG(i << " " << *cur_ty); - lval = ::MIR::LValue::make_Deref({ box$(lval) }); + lval = ::MIR::LValue::new_Deref(mv$(lval)); ), (Pointer, ERROR(sp, E0000, "Attempting to match over a pointer"); @@ -1978,14 +1978,14 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& DEBUG("ty = " << ity << ", val = " << val); const auto& ty = ity; - TU_MATCHA( (ty.m_data), (te), - (Infer, + TU_MATCH_HDRA( (ty.m_data), {) + TU_ARMA(Infer, _te) { BUG(sp, "Hit _ in type - " << ty); - ), - (Diverge, + } + TU_ARMA(Diverge, _te) { BUG(sp, "Matching over !"); - ), - (Primitive, + } + TU_ARMA(Primitive, te) { switch(te) { case ::HIR::CoreType::Bool: { @@ -2152,17 +2152,20 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& case ::HIR::CoreType::Str: { ASSERT_BUG(sp, rule.is_Value() && rule.as_Value().is_StaticString(), ""); const auto& v = rule.as_Value(); + ASSERT_BUG(sp, val.is_Deref(), ""); + val.m_wrappers.pop_back(); + auto str_val = mv$(val); auto succ_bb = builder.new_bb_unlinked(); auto test_val = ::MIR::Param(::MIR::Constant( v.as_StaticString() )); - auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ mv$(*val.as_Deref().val), ::MIR::eBinOp::EQ, mv$(test_val) })); + auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ mv$(str_val), ::MIR::eBinOp::EQ, mv$(test_val) })); builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval), succ_bb, fail_bb }) ); builder.set_cur_block(succ_bb); } break; } - ), - (Path, + } + TU_ARMA(Path, te) { TU_MATCHA( (te.binding), (pbe), (Unbound, BUG(sp, "Encounterd unbound path - " << te.path); @@ -2221,26 +2224,26 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& // Recurse with the new ruleset MIR_LowerHIR_Match_Simple__GeneratePattern(builder, sp, re.sub_rules.data(), re.sub_rules.size(), - var_ty_m, ::MIR::LValue::make_Downcast({ box$(val.clone()), var_idx }), rule.field_path.size()+1, + var_ty_m, ::MIR::LValue::new_Downcast(val.clone(), var_idx), rule.field_path.size()+1, fail_bb ); } ) // TypePathBinding::Enum ) - ), // Type::Data::Path - (Generic, + } // Type::Data::Path + TU_ARMA(Generic, _te) { BUG(sp, "Attempting to match a generic"); - ), - (TraitObject, + } + TU_ARMA(TraitObject, te) { BUG(sp, "Attempting to match a trait object"); - ), - (ErasedType, + } + TU_ARMA(ErasedType, te) { BUG(sp, "Attempting to match an erased type"); - ), - (Array, + } + TU_ARMA(Array, te) { TODO(sp, "Match directly on array?"); - ), - (Slice, + } + TU_ARMA(Slice, te) { ASSERT_BUG(sp, rule.is_Slice() || rule.is_SplitSlice() || (rule.is_Value() && rule.as_Value().is_Bytes()), "Can only match slice with Bytes or Slice rules - " << rule); if( rule.is_Value() ) { ASSERT_BUG(sp, *te.inner == ::HIR::CoreType::U8, "Bytes pattern on non-&[u8]"); @@ -2249,7 +2252,8 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& auto succ_bb = builder.new_bb_unlinked(); - auto inner_val = val.as_Deref().val->clone(); + ASSERT_BUG(sp, val.is_Deref(), "Slice pattern on non-Deref - " << val); + auto inner_val = val.clone_unwrapped(); auto slice_rval = ::MIR::RValue::make_MakeDst({ mv$(cloned_val), mv$(size_val) }); auto test_lval = builder.lvalue_or_temp(sp, ::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, ty.clone()), mv$(slice_rval)); @@ -2262,7 +2266,7 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& // Compare length auto test_val = ::MIR::Param( ::MIR::Constant::make_Uint({ re.len, ::HIR::CoreType::Usize }) ); - auto len_val = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ builder.get_ptr_to_dst(sp, val).clone() })); + auto len_val = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ builder.get_ptr_to_dst(sp, val) })); auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ mv$(len_val), ::MIR::eBinOp::EQ, mv$(test_val) })); auto len_succ_bb = builder.new_bb_unlinked(); @@ -2281,7 +2285,7 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& // Compare length auto test_val = ::MIR::Param( ::MIR::Constant::make_Uint({ re.min_len, ::HIR::CoreType::Usize}) ); - auto len_val = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ builder.get_ptr_to_dst(sp, val).clone() })); + auto len_val = builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ builder.get_ptr_to_dst(sp, val) })); auto cmp_lval = builder.lvalue_or_temp(sp, ::HIR::CoreType::Bool, ::MIR::RValue::make_BinOp({ mv$(len_val), ::MIR::eBinOp::LT, mv$(test_val) })); auto len_succ_bb = builder.new_bb_unlinked(); @@ -2302,23 +2306,23 @@ int MIR_LowerHIR_Match_Simple__GeneratePattern(MirBuilder& builder, const Span& else { BUG(sp, "Invalid rule type for slice - " << rule); } - ), - (Tuple, + } // Type::Data::Array + TU_ARMA(Tuple, te) { TODO(sp, "Match directly on tuple?"); - ), - (Borrow, + } + TU_ARMA(Borrow, te) { TODO(sp, "Match directly on borrow?"); - ), // Type::Data::Borrow - (Pointer, + } // Type::Data::Borrow + TU_ARMA(Pointer, te) { BUG(sp, "Attempting to match a pointer - " << rule << " against " << ty); - ), - (Function, + } + TU_ARMA(Function, te) { BUG(sp, "Attempting to match a function pointer - " << rule << " against " << ty); - ), - (Closure, + } + TU_ARMA(Closure, te) { BUG(sp, "Attempting to match a closure"); - ) - ) + } + } } return 0; } @@ -3035,10 +3039,10 @@ void MatchGenGrouped::gen_dispatch__primitive(::HIR::TypeRef ty, ::MIR::LValue v } m_builder.end_block( ::MIR::Terminator::make_Goto(def_blk) ); } break; - case ::HIR::CoreType::Str: + case ::HIR::CoreType::Str: { // Remove the deref on the &str - auto oval = mv$(val); - auto val = mv$(*oval.as_Deref().val); + ASSERT_BUG(sp, !val.m_wrappers.empty() && val.m_wrappers.back().is_Deref(), "&str match on non-Deref lvalue - " << val); + val.m_wrappers.pop_back(); ::std::vector< ::MIR::BasicBlockId> targets; ::std::vector< ::std::string> values; @@ -3062,7 +3066,7 @@ void MatchGenGrouped::gen_dispatch__primitive(::HIR::TypeRef ty, ::MIR::LValue v m_builder.end_block( ::MIR::Terminator::make_SwitchValue({ mv$(val), def_blk, mv$(targets), ::MIR::SwitchValues(mv$(values)) }) ); - break; + } break; } } @@ -3100,7 +3104,7 @@ void MatchGenGrouped::gen_dispatch__enum(::HIR::TypeRef ty, ::MIR::LValue val, c void MatchGenGrouped::gen_dispatch__slice(::HIR::TypeRef ty, ::MIR::LValue val, const ::std::vector<t_rules_subset>& rules, size_t ofs, const ::std::vector<::MIR::BasicBlockId>& arm_targets, ::MIR::BasicBlockId def_blk) { - auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val).clone() })); + auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val) })); // TODO: Re-sort the rules list to interleve Constant::Bytes and Slice @@ -3162,8 +3166,8 @@ void MatchGenGrouped::gen_dispatch__slice(::HIR::TypeRef ty, ::MIR::LValue val, m_builder.set_cur_block(succ_blk); // TODO: What if `val` isn't a Deref? - ASSERT_BUG(sp, val.is_Deref(), "TODO: Handle non-Deref matches of byte strings"); - cmp_lval_eq = this->push_compare( val.as_Deref().val->clone(), ::MIR::eBinOp::EQ, mv$(cmp_slice_val) ); + ASSERT_BUG(sp, !val.m_wrappers.empty() && val.m_wrappers.back().is_Deref(), "TODO: Handle non-Deref matches of byte strings - " << val); + cmp_lval_eq = this->push_compare( val.clone_unwrapped(), ::MIR::eBinOp::EQ, mv$(cmp_slice_val) ); m_builder.end_block( ::MIR::Terminator::make_If({ mv$(cmp_lval_eq), arm_targets[tgt_ofs], def_blk }) ); m_builder.set_cur_block(next_cmp_blk); @@ -3273,7 +3277,7 @@ void MatchGenGrouped::gen_dispatch_splitslice(const field_path_t& field_path, co ASSERT_BUG(sp, ty.m_data.is_Slice(), "SplitSlice pattern on non-slice - " << ty); // Obtain slice length - auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val).clone() })); + auto val_len = m_builder.lvalue_or_temp(sp, ::HIR::CoreType::Usize, ::MIR::RValue::make_DstMeta({ m_builder.get_ptr_to_dst(sp, val) })); // 1. Check that length is sufficient for the pattern to be used // `IF len < min_len : def_blk, next diff --git a/src/mir/helpers.cpp b/src/mir/helpers.cpp index 1453acb3..6f0ef891 100644 --- a/src/mir/helpers.cpp +++ b/src/mir/helpers.cpp @@ -12,9 +12,12 @@ #include <mir/mir.hpp> #include <algorithm> // ::std::find -void ::MIR::TypeResolve::fmt_pos(::std::ostream& os) const +void ::MIR::TypeResolve::fmt_pos(::std::ostream& os, bool include_path/*=false*/) const { - os << this->m_path << " BB" << this->bb_idx << "/"; + if( include_path ) { + os << this->m_path << " "; + } + os << "BB" << this->bb_idx << "/"; if( this->stmt_idx == STMT_TERM ) { os << "TERM"; } @@ -27,7 +30,7 @@ void ::MIR::TypeResolve::print_msg(const char* tag, ::std::function<void(::std:: { auto& os = ::std::cerr; os << "MIR " << tag << ": "; - fmt_pos(os); + fmt_pos(os, true); cb(os); os << ::std::endl; abort(); @@ -67,25 +70,39 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_static_type(::HIR::TypeRef& tmp, c ) throw ""; } -const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue& val) const +const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue& val, unsigned wrapper_skip_count/*=0*/) const { - TU_MATCH(::MIR::LValue, (val), (e), + const ::HIR::TypeRef* rv = nullptr; + TU_MATCHA( (val.m_root), (e), (Return, - return m_ret_type; + rv = &m_ret_type; ), (Argument, - MIR_ASSERT(*this, e.idx < m_args.size(), "Argument " << val << " out of range (" << m_args.size() << ")"); - return m_args.at(e.idx).second; + MIR_ASSERT(*this, e < m_args.size(), "Argument " << val << " out of range (" << m_args.size() << ")"); + rv = &m_args.at(e).second; ), (Local, MIR_ASSERT(*this, e < m_fcn.locals.size(), "Local " << val << " out of range (" << m_fcn.locals.size() << ")"); - return m_fcn.locals.at(e); + rv = &m_fcn.locals.at(e); ), (Static, - return get_static_type(tmp, *e); - ), - (Field, - const auto& ty = this->get_lvalue_type(tmp, *e.val); + rv = &get_static_type(tmp, e); + ) + ) + assert(wrapper_skip_count <= val.m_wrappers.size()); + const auto* stop_wrapper = &val.m_wrappers[ val.m_wrappers.size() - wrapper_skip_count ]; + for(const auto& w : val.m_wrappers) + { + if( &w == stop_wrapper ) + break; + rv = &this->get_unwrapped_type(tmp, w, *rv); + } + return *rv; +} +const ::HIR::TypeRef& ::MIR::TypeResolve::get_unwrapped_type(::HIR::TypeRef& tmp, const ::MIR::LValue::Wrapper& w, const ::HIR::TypeRef& ty) const +{ + TU_MATCH_HDRA( (w), {) + TU_ARMA(Field, field_index) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( MIR_BUG(*this, "Field access on unexpected type - " << ty); @@ -98,14 +115,14 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c return *te.inner; ), (Tuple, - MIR_ASSERT(*this, e.field_index < te.size(), "Field index out of range in tuple " << e.field_index << " >= " << te.size()); - return te[e.field_index]; + MIR_ASSERT(*this, field_index < te.size(), "Field index out of range in tuple " << field_index << " >= " << te.size()); + return te[field_index]; ), (Path, if( const auto* tep = te.binding.opt_Struct() ) { const auto& str = **tep; - auto monomorph = [&](const auto& ty)->const auto& { + auto maybe_monomorph = [&](const auto& ty)->const auto& { if( monomorphise_type_needed(ty) ) { tmp = monomorphise_type(sp, str.m_params, te.path.m_data.as_Generic().m_params, ty); m_resolve.expand_associated_types(sp, tmp); @@ -120,12 +137,12 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c MIR_BUG(*this, "Field on unit-like struct - " << ty); ), (Tuple, - MIR_ASSERT(*this, e.field_index < se.size(), "Field index out of range in tuple-struct " << te.path); - return monomorph(se[e.field_index].ent); + MIR_ASSERT(*this, field_index < se.size(), "Field index out of range in tuple-struct " << te.path); + return maybe_monomorph(se[field_index].ent); ), (Named, - MIR_ASSERT(*this, e.field_index < se.size(), "Field index out of range in struct " << te.path); - return monomorph(se[e.field_index].second.ent); + MIR_ASSERT(*this, field_index < se.size(), "Field index out of range in struct " << te.path); + return maybe_monomorph(se[field_index].second.ent); ) ) } @@ -142,8 +159,8 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c return t; } }; - MIR_ASSERT(*this, e.field_index < unm.m_variants.size(), "Field index out of range for union"); - return maybe_monomorph(unm.m_variants.at(e.field_index).second.ent); + MIR_ASSERT(*this, field_index < unm.m_variants.size(), "Field index out of range for union"); + return maybe_monomorph(unm.m_variants.at(field_index).second.ent); } else { @@ -151,9 +168,8 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c } ) ) - ), - (Deref, - const auto& ty = this->get_lvalue_type(tmp, *e.val); + } + TU_ARMA(Deref, _e) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( MIR_BUG(*this, "Deref on unexpected type - " << ty); @@ -174,9 +190,8 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c return *te.inner; ) ) - ), - (Index, - const auto& ty = this->get_lvalue_type(tmp, *e.val); + } + TU_ARMA(Index, index_local) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( MIR_BUG(*this, "Index on unexpected type - " << ty); @@ -188,9 +203,8 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c return *te.inner; ) ) - ), - (Downcast, - const auto& ty = this->get_lvalue_type(tmp, *e.val); + } + TU_ARMA(Downcast, variant_index) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( MIR_BUG(*this, "Downcast on unexpected type - " << ty); @@ -202,8 +216,8 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c const auto& enm = *te.binding.as_Enum(); MIR_ASSERT(*this, enm.m_data.is_Data(), "Downcast on non-data enum - " << ty); const auto& variants = enm.m_data.as_Data(); - MIR_ASSERT(*this, e.variant_index < variants.size(), "Variant index out of range for " << ty); - const auto& variant = variants[e.variant_index]; + MIR_ASSERT(*this, variant_index < variants.size(), "Variant index out of range for " << ty); + const auto& variant = variants[variant_index]; const auto& var_ty = variant.type; if( monomorphise_type_needed(var_ty) ) { @@ -218,12 +232,13 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c else { const auto& unm = *te.binding.as_Union(); - MIR_ASSERT(*this, e.variant_index < unm.m_variants.size(), "Variant index out of range"); - const auto& variant = unm.m_variants[e.variant_index]; + MIR_ASSERT(*this, variant_index < unm.m_variants.size(), "Variant index out of range"); + const auto& variant = unm.m_variants[variant_index]; const auto& var_ty = variant.second.ent; + //return m_resolve.maybe_monomorph(sp, tmp, unm.m_params, te.path.m_data.as_Generic().m_params, var_ty); if( monomorphise_type_needed(var_ty) ) { - tmp = monomorphise_type(sp, unm.m_params, te.path.m_data.as_Generic().m_params, variant.second.ent); + tmp = monomorphise_type(sp, unm.m_params, te.path.m_data.as_Generic().m_params, var_ty); m_resolve.expand_associated_types(sp, tmp); return tmp; } @@ -233,8 +248,8 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c } ) ) - ) - ) + } + } throw ""; } const ::HIR::TypeRef& MIR::TypeResolve::get_param_type(::HIR::TypeRef& tmp, const ::MIR::Param& val) const @@ -383,6 +398,15 @@ namespace visit { { if( cb(lv, u) ) return true; +#if 1 + for(const auto& w : lv.m_wrappers) + { + if( w.is_Index() ) + { + cb(LValue::new_Local(w.as_Index()), ValUsage::Read); + } + } +#else TU_MATCHA( (lv), (e), (Return, ), @@ -408,6 +432,7 @@ namespace visit { return visit_mir_lvalue(*e.val, u, cb); ) ) +#endif return false; } @@ -625,32 +650,15 @@ void MIR_Helper_GetLifetimes_DetermineValueLifetime(::MIR::TypeResolve& state, c { 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_Local() ) + if( lv.m_root.is_Local() ) { - if( !mask || mask->at(*de) ) + auto de = lv.m_root.as_Local(); + if( !mask || mask->at(de) ) { - MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, slot_lifetimes[*de]); - slot_lifetimes[*de].fill(block_offsets, bb_idx, stmt_idx, stmt_idx); + MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, slot_lifetimes[de]); + slot_lifetimes[de].fill(block_offsets, bb_idx, stmt_idx, stmt_idx); } } - else - { - // Not a direct assignment of a slot. But check if a slot is mutated as part of this. - ::MIR::visit::visit_mir_lvalue(lv, ValUsage::Write, [&](const auto& ilv, ValUsage vu) { - if( const auto* de = ilv.opt_Local() ) - { - if( vu == ValUsage::Write ) - { - if( !mask || mask->at(*de) ) - { - MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, slot_lifetimes[*de]); - slot_lifetimes[*de].fill(block_offsets, bb_idx, stmt_idx, stmt_idx); - } - } - } - return false; - }); - } }; const auto& bb = fcn.blocks[bb_idx]; @@ -673,11 +681,12 @@ void MIR_Helper_GetLifetimes_DetermineValueLifetime(::MIR::TypeResolve& state, c else if( const auto* se = stmt.opt_Drop() ) { // HACK: Mark values as valid wherever there's a drop (prevents confusion by simple validator) - if( const auto* de = se->slot.opt_Local() ) + if( se->slot.m_wrappers.empty() && se->slot.m_root.is_Local() ) { - if( !mask || mask->at(*de) ) + auto de = se->slot.m_root.as_Local(); + if( !mask || mask->at(de) ) { - slot_lifetimes[*de].fill(block_offsets, bb_idx, stmt_idx,stmt_idx); + slot_lifetimes[de].fill(block_offsets, bb_idx, stmt_idx,stmt_idx); } } } diff --git a/src/mir/helpers.hpp b/src/mir/helpers.hpp index 58eee9b9..0296a1c4 100644 --- a/src/mir/helpers.hpp +++ b/src/mir/helpers.hpp @@ -104,7 +104,7 @@ public: } unsigned int get_cur_stmt_ofs() const; - void fmt_pos(::std::ostream& os) const; + void fmt_pos(::std::ostream& os, bool include_path=false) const; void print_bug(::std::function<void(::std::ostream& os)> cb) const { print_msg("ERROR", cb); } @@ -116,7 +116,14 @@ public: const ::MIR::BasicBlock& get_block(::MIR::BasicBlockId id) const; const ::HIR::TypeRef& get_static_type(::HIR::TypeRef& tmp, const ::HIR::Path& path) const; - const ::HIR::TypeRef& get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue& val) const; + const ::HIR::TypeRef& get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue& val, unsigned wrapper_skip_count=0) const; + const ::HIR::TypeRef& get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue::CRef& val) const { + return get_lvalue_type(tmp, val.lv(), val.lv().m_wrappers.size() - val.wrapper_count()); + } + const ::HIR::TypeRef& get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue::MRef& val) const { + return get_lvalue_type(tmp, val.lv(), val.lv().m_wrappers.size() - val.wrapper_count()); + } + const ::HIR::TypeRef& get_unwrapped_type(::HIR::TypeRef& tmp, const ::MIR::LValue::Wrapper& w, const ::HIR::TypeRef& ty) const; const ::HIR::TypeRef& get_param_type(::HIR::TypeRef& tmp, const ::MIR::Param& val) const; ::HIR::TypeRef get_const_type(const ::MIR::Constant& c) const; diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index 83f7e1f0..8e3045d6 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -91,119 +91,86 @@ namespace MIR { throw ""; } - ::std::ostream& operator<<(::std::ostream& os, const LValue& x) + void LValue::RefCommon::fmt(::std::ostream& os) const { - TU_MATCHA( (x), (e), + TU_MATCHA( (m_lv->m_root), (e), (Return, os << "retval"; ), (Argument, - os << "a" << e.idx; + os << "a" << e; ), (Local, os << "_" << e; ), (Static, - os << "(" << *e << ")"; - ), - (Field, - os << *e.val << "." << e.field_index; - ), - (Deref, - os << *e.val << "*"; - ), - (Index, - os << *e.val << "[" << *e.idx << "]"; - ), - (Downcast, - os << *e.val << "#" << e.variant_index; + os << "(" << e << ")"; ) ) + for(size_t i = 0; i < m_wrapper_count; i ++) + { + const LValue::Wrapper& w = m_lv->m_wrappers.at(i); + TU_MATCHA( (w), (e), + (Field, + os << "." << e; + ), + (Deref, + os << "*"; + ), + (Index, + os << "[_" << e << "]"; + ), + (Downcast, + os << "#" << e; + ) + ) + } + } + + ::std::ostream& operator<<(::std::ostream& os, const LValue& x) + { + LValue::CRef(x).fmt(os); return os; } - bool operator<(const LValue& a, const LValue& b) + + Ordering LValue::Storage::ord(const LValue::Storage& x) const { - if( a.tag() != b.tag() ) - return a.tag() < b.tag(); - TU_MATCHA( (a, b), (ea, eb), - (Return, - return false; - ), - (Argument, - return ea.idx < eb.idx; - ), - (Local, - return ea < eb; - ), - (Static, - return ea < eb; - ), - (Field, - if( *ea.val != *eb.val ) - return *ea.val < *eb.val; - if( ea.field_index != eb.field_index ) - return ea.field_index < eb.field_index; - return true; - ), - (Deref, - return *ea.val < *eb.val; - ), - (Index, - if( *ea.val != *eb.val ) - return *ea.val < *eb.val; - return *ea.idx < *eb.idx; - ), - (Downcast, - if( *ea.val != *eb.val ) - return *ea.val < *eb.val; - return ea.variant_index < eb.variant_index; - ) - ) - throw ""; + if( x.is_Static() ) + { + if( this->is_Static() ) + return this->as_Static().ord( x.as_Static() ); + else + return OrdLess; + } + else + { + if( this->is_Static() ) + return OrdGreater; + } + + return ::ord(this->val, x.val); } - bool operator==(const LValue& a, const LValue& b) + Ordering LValue::ord(const LValue& x) const { - if( a.tag() != b.tag() ) - return false; - TU_MATCHA( (a, b), (ea, eb), - (Return, - return true; - ), - (Argument, - return ea.idx == eb.idx; - ), - (Local, - return ea == eb; - ), - (Static, - return ea == eb; - ), - (Field, - if( *ea.val != *eb.val ) - return false; - if( ea.field_index != eb.field_index ) - return false; - return true; - ), - (Deref, - return *ea.val == *eb.val; - ), - (Index, - if( *ea.val != *eb.val ) - return false; - if( *ea.idx != *eb.idx ) - return false; - return true; - ), - (Downcast, - if( *ea.val != *eb.val ) - return false; - if( ea.variant_index != eb.variant_index ) - return false; - return true; - ) - ) - throw ""; + auto rv = m_root.ord(x.m_root); + if( rv != OrdEqual ) + return rv; + return ::ord(m_wrappers, x.m_wrappers); + } + Ordering LValue::RefCommon::ord(const LValue::RefCommon& x) const + { + Ordering rv; + //TRACE_FUNCTION_FR(FMT_CB(ss, this->fmt(ss); ss << " ? "; x.fmt(ss);), rv); + rv = m_lv->m_root.ord(x.m_lv->m_root); + if( rv != OrdEqual ) + return rv; + for(size_t i = 0; i < ::std::min(m_wrapper_count, x.m_wrapper_count); i ++) + { + rv = m_lv->m_wrappers[i].ord(x.m_lv->m_wrappers[i]); + if( rv != OrdEqual ) + return rv; + } + return (rv = ::ord(m_wrapper_count, x.m_wrapper_count)); } ::std::ostream& operator<<(::std::ostream& os, const Param& x) @@ -537,30 +504,14 @@ namespace MIR { } } -::MIR::LValue MIR::LValue::clone() const +::MIR::LValue::Storage MIR::LValue::Storage::clone() const { - TU_MATCHA( (*this), (e), - (Return, return LValue(e); ), - (Argument, return LValue(e); ), - (Local, return LValue(e); ), - (Static, return LValue(box$(e->clone())); ), - (Field, return LValue::make_Field({ - box$( e.val->clone() ), - e.field_index - }); ), - (Deref, return LValue::make_Deref({ - box$( e.val->clone() ) - }); ), - (Index, return LValue::make_Index({ - box$( e.val->clone() ), - box$( e.idx->clone() ) - }); ), - (Downcast, return LValue::make_Downcast({ - box$( e.val->clone() ), - e.variant_index - }); ) - ) - throw ""; + if( is_Static() ) { + return new_Static(as_Static().clone()); + } + else { + return Storage(this->val); + } } ::MIR::Constant MIR::Constant::clone() const { diff --git a/src/mir/mir.hpp b/src/mir/mir.hpp index 63acf89d..f3d61dfa 100644 --- a/src/mir/mir.hpp +++ b/src/mir/mir.hpp @@ -12,21 +12,27 @@ #include <memory> // std::unique_ptr #include <hir/type.hpp> +class MonomorphState; + namespace MIR { typedef unsigned int RegionId; typedef unsigned int BasicBlockId; -#if 0 -// TODO: Store LValues as: +// Store LValues as: // - A packed root value (one word, using the low bits as an enum descriminator) // - A list of (inner to outer) wrappers struct LValue { class Storage { + public: + const static uintptr_t MAX_ARG = (1 << 30) - 1; // max value of 30 bits + private: + uintptr_t val; - static uintptr_t MAX_ARG = (1 << 30) - 1; // max value of 30 bits + + Storage(uintptr_t v): val(v) {} public: Storage(const Storage&) = delete; Storage& operator=(const Storage&) = delete; @@ -40,41 +46,89 @@ struct LValue this->~Storage(); this->val = x.val; x.val = 0; + return *this; } ~Storage() { if( is_Static() ) { - delete reinterpret_cast<::HIR::Path*>(val & ~3u); + delete reinterpret_cast<::HIR::Path*>(val & ~3ull); val = 0; } } - static Storage new_Return() { return Storage { MAX_ARG << 2 }; } - static Storage new_Argument(unsigned idx) { assert(idx < MAX_ARG); return Storage { idx << 2 }; ) - static Storage new_Local(unsigned idx) { assert(idx <= MAX_ARG); return Storage { (idx << 2) | 1 } }; + static Storage new_Return() { return Storage(0 << 2); } + static Storage new_Argument(unsigned idx) { assert(idx < MAX_ARG); return Storage((idx+1) << 2); } + static Storage new_Local(unsigned idx) { assert(idx <= MAX_ARG); return Storage((idx << 2) | 1); } static Storage new_Static(::HIR::Path p) { ::HIR::Path* ptr = new ::HIR::Path(::std::move(p)); - return Storage { static_cast<uintptr_t>(ptr) | 2; } + return Storage(reinterpret_cast<uintptr_t>(ptr) | 2); + } + + Storage clone() const; + + uintptr_t get_inner() const { + assert(!is_Static()); + return val; + } + static Storage from_inner(uintptr_t v) { + assert( (v & 3) < 2 ); + return Storage(v); + } + + enum Tag { + TAG_Argument, + TAG_Local, + TAG_Static, + TAG_Return, + TAGDEAD, + }; + Tag tag() const { + if(val == 0) + return TAG_Return; + return static_cast<Tag>(val & 3); } - bool is_Return() const { return val == (MAX_ARG << 2) /*&& (val & 3) == 0*/; } - bool is_Argument() const { return val != (MAX_ARG << 2) && (val & 3) == 0; } + bool is_Return() const { return val == 0; } + bool is_Argument() const { return val != 0 && (val & 3) == 0; } bool is_Local() const { return (val & 3) == 1; } bool is_Static() const { return (val & 3) == 2; } - // No as_Return - unsigned as_Argument() const { assert(is_Argument()); return val >> 2; } - unsigned as_Local() const { assert(is_Local()); return val >> 2; } - const ::HIR::Path& as_Static() const { assert(is_Static()); return *reinterpret_cast<const ::HIR::Path*>(val & ~3u); } + const char as_Return () const { assert(is_Return()); return 0; } + const unsigned as_Argument() const { assert(is_Argument()); return (val >> 2) - 1; } + const unsigned as_Local () const { assert(is_Local()); return val >> 2; } + + const ::HIR::Path& as_Static() const { assert(is_Static()); return *reinterpret_cast<const ::HIR::Path*>(val & ~3llu); } + ::HIR::Path& as_Static() { assert(is_Static()); return *reinterpret_cast< ::HIR::Path*>(val & ~3llu); } + + Ordering ord(const Storage& x) const; + bool operator==(const Storage& x) const { return this->ord(x) == OrdEqual; } + bool operator!=(const Storage& x) const { return this->ord(x) != OrdEqual; } }; class Wrapper { - uintptr_t val; + uint32_t val; + Wrapper(uint32_t v): val(v) {} public: - static Wrapper new_Deref() { return Wrapper { 0 }; } - static Wrapepr new_Field (unsigned idx) { return Wrapper { (idx << 2) | 1 }; } - static Wrapepr new_Downcast(unsigned idx) { return Wrapper { (idx << 2) | 2 }; } - static Wrapepr new_Index (unsigned idx) { return Wrapper { (idx << 2) | 3 }; } + static Wrapper new_Deref() { return Wrapper( 0 ); } + static Wrapper new_Field (unsigned idx) { return Wrapper( (idx << 2) | 1 ); } + static Wrapper new_Downcast(unsigned idx) { return Wrapper( (idx << 2) | 2 ); } + static Wrapper new_Index (unsigned idx) { if(idx == ~0u) idx = Storage::MAX_ARG; return Wrapper( (idx << 2) | 3 ); } + + uint32_t get_inner() const { return val; } + static Wrapper from_inner(uint32_t v) { + return Wrapper(v); + } + + enum Tag { + TAG_Deref, + TAG_Field, + TAG_Downcast, + TAG_Index, + TAGDEAD, + }; + Tag tag() const { + return static_cast<Tag>(val & 3); + } bool is_Deref () const { return (val & 3) == 0; } // Stores the field index @@ -84,79 +138,269 @@ struct LValue // Stores a Local index bool is_Index () const { return (val & 3) == 3; } - // no as_Deref() - const unsigned as_Field() const { assert(is_Field()); return (val >> 2); } + const char as_Deref () const { assert(is_Deref()); return 0; } + const unsigned as_Field () const { assert(is_Field()); return (val >> 2); } const unsigned as_Downcast() const { assert(is_Downcast()); return (val >> 2); } // TODO: Should this return a LValue? - const unsigned as_Index() const { assert(is_Index()); return (val >> 2); } + const unsigned as_Index () const { assert(is_Index()); unsigned rv = (val >> 2); return rv; } + + void inc_Field () { assert(is_Field ()); *this = Wrapper::new_Field (as_Field () + 1); } + void inc_Downcast() { assert(is_Downcast()); *this = Wrapper::new_Downcast(as_Downcast() + 1); } + + Ordering ord(const Wrapper& x) const { return ::ord(val, x.val); } }; Storage m_root; ::std::vector<Wrapper> m_wrappers; - static LValue new_Return() { return LValue { Storage::new_Return(), {} }; } - static LValue new_Argument(unsigned idx) { return LValue { Storage::new_Argument(idx), {} }; } - static LValue new_Local(unsigned idx) { return LValue { Storage::new_Local(idx), {} }; } - static LValue new_Static(::HIR::Path p) { return LValue { Storage::new_Static(::std::move(p)), {} }; } + LValue() + :m_root( Storage::new_Return() ) + { + } + LValue(Storage root, ::std::vector<Wrapper> wrappers) + :m_root( ::std::move(root) ) + ,m_wrappers( ::std::move(wrappers) ) + { + } + + static LValue new_Return () { return LValue(Storage::new_Return(), {}); } + static LValue new_Argument(unsigned idx ) { return LValue(Storage::new_Argument(idx), {}); } + static LValue new_Local (unsigned idx ) { return LValue(Storage::new_Local(idx), {}); } + static LValue new_Static (::HIR::Path p) { return LValue(Storage::new_Static(::std::move(p)), {}); } + + static LValue new_Deref(LValue lv) { lv.m_wrappers.push_back(Wrapper::new_Deref()); return lv; } + static LValue new_Field(LValue lv, unsigned idx) { lv.m_wrappers.push_back(Wrapper::new_Field(idx)); return lv; } + static LValue new_Downcast(LValue lv, unsigned idx) { lv.m_wrappers.push_back(Wrapper::new_Downcast(idx)); return lv; } + static LValue new_Index(LValue lv, unsigned local_idx) { lv.m_wrappers.push_back(Wrapper::new_Index(local_idx)); return lv; } - static LValue new_Deref(LValue lv) { lv.m_wrappers.push_back(Wrapper::new_Deref()); } - static LValue new_Field(LValue lv, unsigned idx) { lv.m_wrappers.push_back(Wrapper::new_Field(idx)); } - static LValue new_Downcast(LValue lv, unsigned idx) { lv.m_wrappers.push_back(Wrapper::new_Downcast(idx)); } - static LValue new_Index(LValue lv, unsigned local_idx) { lv.m_wrappers.push_back(Wrapper::new_Index(local_idx)); } + bool is_Return() const { return m_wrappers.empty() && m_root.is_Return(); } + bool is_Local () const { return m_wrappers.empty() && m_root.is_Local(); } + const unsigned as_Local() const { assert(m_wrappers.empty()); return m_root.as_Local(); } + + bool is_Deref () const { return m_wrappers.size() > 0 && m_wrappers.back().is_Deref(); } + bool is_Field () const { return m_wrappers.size() > 0 && m_wrappers.back().is_Field(); } + bool is_Downcast() const { return m_wrappers.size() > 0 && m_wrappers.back().is_Downcast(); } + const unsigned as_Field() const { assert(!m_wrappers.empty()); return m_wrappers.back().as_Field(); } + + void inc_Field () { assert(m_wrappers.size() > 0); m_wrappers.back().inc_Field (); } + void inc_Downcast() { assert(m_wrappers.size() > 0); m_wrappers.back().inc_Downcast(); } + + Ordering ord(const LValue& x) const; LValue monomorphise(const MonomorphState& ms, unsigned local_offset=0); //LValue monomorphise(const TransParams& ms, unsigned local_offset=0); - LValue clone() const; + LValue clone() const { + return LValue(m_root.clone(), m_wrappers); + } + LValue clone_wrapped(::std::vector<Wrapper> wrappers) const { + if( this->m_wrappers.empty() ) { + return LValue(m_root.clone(), ::std::move(wrappers)); + } + else { + return clone_wrapped(wrappers.begin(), wrappers.end()); + } + } + template<typename It> + LValue clone_wrapped(It begin_it, It end_it) const { + ::std::vector<Wrapper> wrappers; + wrappers.reserve(m_wrappers.size() + ::std::distance(begin_it, end_it)); + wrappers.insert(wrappers.end(), m_wrappers.begin(), m_wrappers.end()); + wrappers.insert(wrappers.end(), begin_it, end_it); + return LValue(m_root.clone(), ::std::move(wrappers)); + } + + LValue clone_unwrapped(unsigned count=1) const { + assert(count > 0); + assert(count <= m_wrappers.size()); + return LValue(m_root.clone(), ::std::vector<Wrapper>(m_wrappers.begin(), m_wrappers.end() - count)); + } + + /// Helper class that represents a LValue unwrapped to a certain degree + class RefCommon + { + protected: + const LValue* m_lv; + size_t m_wrapper_count; + + RefCommon(const LValue& lv, size_t wrapper_count) + :m_lv(&lv) + ,m_wrapper_count(wrapper_count) + { + assert(wrapper_count <= lv.m_wrappers.size()); + } + + public: + LValue clone() const { + return ::MIR::LValue( m_lv->m_root.clone(), ::std::vector<Wrapper>(m_lv->m_wrappers.begin(), m_lv->m_wrappers.begin() + m_wrapper_count) ); + } + + const LValue& lv() const { return *m_lv; } + size_t wrapper_count() const { return m_wrapper_count; } + + /// Unwrap one level, returning false if already at the root + bool try_unwrap() { + if( m_wrapper_count == 0 ) { + return false; + } + else { + m_wrapper_count --; + return true; + } + } + + enum Tag { + TAGDEAD, + TAG_Return, + TAG_Argument, + TAG_Local, + TAG_Static, + TAG_Deref, + TAG_Field, + TAG_Downcast, + TAG_Index, + }; + Tag tag() const { + if( m_wrapper_count == 0 ) + { + switch(m_lv->m_root.tag()) + { + case Storage::TAGDEAD: return TAGDEAD; + case Storage::TAG_Return: return TAG_Return; + case Storage::TAG_Argument: return TAG_Argument; + case Storage::TAG_Local: return TAG_Local; + case Storage::TAG_Static: return TAG_Static; + } + } + else + { + switch(m_lv->m_wrappers[m_wrapper_count-1].tag()) + { + case Wrapper::TAGDEAD: return TAGDEAD; + case Wrapper::TAG_Deref: return TAG_Deref; + case Wrapper::TAG_Field: return TAG_Field; + case Wrapper::TAG_Downcast: return TAG_Downcast; + case Wrapper::TAG_Index: return TAG_Index; + } + } + return TAGDEAD; + } + + bool is_Local () const { return m_wrapper_count == 0 && m_lv->m_root.is_Local (); } + bool is_Return () const { return m_wrapper_count == 0 && m_lv->m_root.is_Return (); } + bool is_Argument() const { return m_wrapper_count == 0 && m_lv->m_root.is_Argument(); } + bool is_Static () const { return m_wrapper_count == 0 && m_lv->m_root.is_Static (); } + bool is_Deref () const { return m_wrapper_count >= 1 && m_lv->m_wrappers[m_wrapper_count-1].is_Deref (); } + bool is_Field () const { return m_wrapper_count >= 1 && m_lv->m_wrappers[m_wrapper_count-1].is_Field (); } + bool is_Downcast() const { return m_wrapper_count >= 1 && m_lv->m_wrappers[m_wrapper_count-1].is_Downcast(); } + bool is_Index () const { return m_wrapper_count >= 1 && m_lv->m_wrappers[m_wrapper_count-1].is_Index (); } + + const unsigned as_Local () const { assert(is_Local ()); return m_lv->m_root.as_Local (); } + const char as_Return () const { assert(is_Return ()); return m_lv->m_root.as_Return (); } + const unsigned as_Argument() const { assert(is_Argument()); return m_lv->m_root.as_Argument(); } + const HIR::Path& as_Static () const { assert(is_Static ()); return m_lv->m_root.as_Static (); } + const char as_Deref () const { assert(is_Deref ()); return m_lv->m_wrappers[m_wrapper_count-1].as_Deref (); } + const unsigned as_Field () const { assert(is_Field ()); return m_lv->m_wrappers[m_wrapper_count-1].as_Field (); } + const unsigned as_Downcast() const { assert(is_Downcast()); return m_lv->m_wrappers[m_wrapper_count-1].as_Downcast(); } + const unsigned as_Index () const { assert(is_Index ()); return m_lv->m_wrappers[m_wrapper_count-1].as_Index (); } - class Ref + void fmt(::std::ostream& os) const; + Ordering ord(const RefCommon& b) const; + }; + + class CRef: public RefCommon { - LValue& r; - size_t wrapper_idx; - public:: - LValue clone() const; - void replace(LValue x) const; + public: + CRef(const LValue& lv) + :RefCommon(lv, lv.m_wrappers.size()) + { + } + CRef(const LValue& lv, size_t wc) + :RefCommon(lv, wc) + { + } + + /// Unwrap one level + const CRef inner_ref() const { + assert(m_wrapper_count > 0); + auto rv = *this; + rv.m_wrapper_count--; + return rv; + } + + friend ::std::ostream& operator<<(::std::ostream& os, const CRef& x) { + x.fmt(os); + return os; + } + + bool operator<(const CRef& b) const { + return this->ord(b) == OrdLess; + } + bool operator==(const CRef& b) const { + return this->ord(b) == OrdEqual; + } }; + class MRef: public RefCommon + { + public: + MRef(LValue& lv) + :RefCommon(lv, lv.m_wrappers.size()) + { + } + + operator CRef() const { + return CRef(*m_lv, m_wrapper_count); + } + + MRef inner_ref() { + assert(m_wrapper_count > 0); + auto rv = *this; + rv.m_wrapper_count--; + return rv; + } + void replace(LValue x) { + auto& mut_lv = const_cast<LValue&>(*m_lv); + // Shortcut: No wrappers on source/destination (just assign the slot/root) + if( m_wrapper_count == 0 && x.m_wrappers.empty() ) { + mut_lv.m_root = ::std::move(x.m_root); + return ; + } + // If there's wrappers on this value (assigning over inner portion) + if( m_wrapper_count < m_lv->m_wrappers.size() ) { + // Add those wrappers to the end of the new value + x.m_wrappers.insert(x.m_wrappers.end(), m_lv->m_wrappers.begin() + m_wrapper_count, m_lv->m_wrappers.end()); + } + // Overwrite + mut_lv = ::std::move(x); + } + + friend ::std::ostream& operator<<(::std::ostream& os, const MRef& x) { + x.fmt(os); + return os; + } + }; + + Ordering ord(const LValue::CRef& x) const; + Ordering ord(const LValue::MRef& x) const; }; -#endif - -// "LVALUE" - Assignable values -TAGGED_UNION_EX(LValue, (), Return, ( - // Function return - (Return, struct{}), - // Function argument (input) - (Argument, struct { unsigned int idx; }), - // Variable/Temporary - (Local, unsigned int), - // `static` or `static mut` - (Static, ::std::unique_ptr<::HIR::Path>), - // Field access (tuple, struct, tuple struct, enum field, ...) - // NOTE: Also used to index an array/slice by a compile-time known index (e.g. in destructuring) - (Field, struct { - ::std::unique_ptr<LValue> val; - unsigned int field_index; - }), - // Dereference a value - (Deref, struct { - ::std::unique_ptr<LValue> val; - }), - // Index an array or slice (typeof(val) == [T; n] or [T]) - // NOTE: This is not bounds checked! - (Index, struct { - ::std::unique_ptr<LValue> val; - ::std::unique_ptr<LValue> idx; - }), - // Interpret an enum as a particular variant - (Downcast, struct { - ::std::unique_ptr<LValue> val; - unsigned int variant_index; - }) - ), (),(), ( - LValue clone() const; - ) - ); extern ::std::ostream& operator<<(::std::ostream& os, const LValue& x); -extern bool operator<(const LValue& a, const LValue& b); -extern bool operator==(const LValue& a, const LValue& b); +static inline bool operator<(const LValue& a, const LValue::CRef& b) { + return a.ord(b) == OrdLess; +} +static inline bool operator<(const LValue& a, const LValue::MRef& b) { + return a.ord(b) == OrdLess; +} +static inline bool operator<(const LValue::CRef& a, const LValue& b) { + return b.ord(a) == OrdGreater; +} +static inline bool operator<(const LValue::MRef& a, const LValue& b) { + return b.ord(a) == OrdGreater; +} +static inline bool operator<(const LValue& a, const LValue& b) { + return a.ord(b) == OrdLess; +} +static inline bool operator==(const LValue& a, const LValue& b) { + return a.ord(b) == OrdEqual; +} static inline bool operator!=(const LValue& a, const LValue& b) { return !(a == b); } @@ -224,7 +468,7 @@ TAGGED_UNION_EX(Constant, (), Int, ( /// Parameter - A value used when a rvalue just reads (doesn't require a lvalue) /// Can be either a lvalue (memory address), or a constant -TAGGED_UNION_EX(Param, (), LValue, ( +TAGGED_UNION_EX(Param, (), Constant, ( (LValue, LValue), (Constant, Constant) ), (), (), ( @@ -237,7 +481,7 @@ TAGGED_UNION_EX(Param, (), LValue, ( ) ); -TAGGED_UNION_EX(RValue, (), Use, ( +TAGGED_UNION_EX(RValue, (), Tuple, ( (Use, LValue), (Constant, Constant), (SizedArray, struct { @@ -359,7 +603,7 @@ enum class eDropKind { SHALLOW, DEEP, }; -TAGGED_UNION(Statement, Assign, +TAGGED_UNION(Statement, Asm, // Value assigment (Assign, struct { LValue dst; diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 8d534314..a064b67d 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -68,7 +68,7 @@ MirBuilder::~MirBuilder() { if( has_result() ) { - push_stmt_assign( sp, ::MIR::LValue::make_Return({}), get_result(sp) ); + push_stmt_assign( sp, ::MIR::LValue::new_Return(), get_result(sp) ); } terminate_scope_early(sp, fcn_scope()); @@ -176,7 +176,7 @@ void MirBuilder::define_variable(unsigned int idx) auto& tmp_scope = top_scope->data.as_Owning(); assert(tmp_scope.is_temporary); tmp_scope.slots.push_back( rv ); - return ::MIR::LValue::make_Local(rv); + return ::MIR::LValue::new_Local(rv); } ::MIR::LValue MirBuilder::lvalue_or_temp(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::RValue val) { @@ -258,12 +258,10 @@ void MirBuilder::set_result(const Span& sp, ::MIR::RValue val) m_result_valid = true; } -void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val) +void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RValue val, bool drop_destination/*=true*/) { DEBUG(dst << " = " << val); ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - ASSERT_BUG(sp, dst.tag() != ::MIR::LValue::TAGDEAD, ""); - ASSERT_BUG(sp, val.tag() != ::MIR::RValue::TAGDEAD, ""); auto moved_param = [&](const ::MIR::Param& p) { if(const auto* e = p.opt_LValue()) { @@ -344,13 +342,15 @@ void MirBuilder::push_stmt_assign(const Span& sp, ::MIR::LValue dst, ::MIR::RVal ) // Drop target if populated - mark_value_assigned(sp, dst); + if( drop_destination ) + { + mark_value_assigned(sp, dst); + } this->push_stmt( sp, ::MIR::Statement::make_Assign({ mv$(dst), mv$(val) }) ); } void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int flag/*=~0u*/) { ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - ASSERT_BUG(sp, val.tag() != ::MIR::LValue::TAGDEAD, ""); if( lvalue_is_copy(sp, val) ) { // Don't emit a drop for Copy values @@ -362,7 +362,6 @@ void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsigned int flag/*=~0u*/) { ASSERT_BUG(sp, m_block_active, "Pushing statement with no active block"); - ASSERT_BUG(sp, val.tag() != ::MIR::LValue::TAGDEAD, ""); // TODO: Ensure that the type is a Box? @@ -400,22 +399,12 @@ void MirBuilder::push_stmt(const Span& sp, ::MIR::Statement stmt) void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) { - VarState* state_p = nullptr; - // TODO: Tracking of complex asignment states (e.g. assignment of a field) - TU_MATCH_DEF(::MIR::LValue, (dst), (e), - ( - ), - (Return, - // Don't drop. - // No state tracking for the return value - ), - (Argument, - state_p = &get_slot_state_mut(sp, e.idx, SlotType::Argument); - ), - (Local, - state_p = &get_slot_state_mut(sp, e, SlotType::Local); - ) - ) + if( dst.m_root.is_Return() ) + { + ASSERT_BUG(sp, dst.m_wrappers.empty(), "Assignment to a component of the return value should be impossible."); + return ; + } + VarState* state_p = get_val_state_mut_p(sp, dst);; if( state_p ) { @@ -425,42 +414,28 @@ void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) drop_value_from_state(sp, *state_p, dst.clone()); *state_p = VarState::make_Valid({}); } - // TODO: What about assigning into non-tracked locations? Should still cause a drop + else + { + // Assigning into non-tracked locations still causes a drop + drop_value_from_state(sp, VarState::make_Valid({}), dst.clone()); + } } void MirBuilder::raise_temporaries(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above/*=false*/) { TRACE_FUNCTION_F(val); - TU_MATCH_DEF(::MIR::LValue, (val), (e), - ( + for(const auto& w : val.m_wrappers) + { + if( w.is_Index() ) { + // Raise index temporary + raise_temporaries(sp, ::MIR::LValue::new_Local(w.as_Index()), scope, to_above); + } + } + if( !val.m_root.is_Local() ) { // No raising of these source values? return ; - ), - // TODO: This may not be correct, because it can change the drop points and ordering - // HACK: Working around cases where values are dropped while the result is not yet used. - (Index, - raise_temporaries(sp, *e.val, scope, to_above); - raise_temporaries(sp, *e.idx, scope, to_above); - return ; - ), - (Deref, - raise_temporaries(sp, *e.val, scope, to_above); - return ; - ), - (Field, - raise_temporaries(sp, *e.val, scope, to_above); - return ; - ), - (Downcast, - raise_temporaries(sp, *e.val, scope, to_above); - return ; - ), - // Actual value types - (Local, - ) - ) - ASSERT_BUG(sp, val.is_Local(), "Hit value raising code with non-variable value - " << val); - const auto idx = val.as_Local(); + } + const auto idx = val.m_root.as_Local(); bool is_temp = (idx >= m_first_temp_idx); /* if( !is_temp ) { @@ -833,13 +808,12 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle } auto& src_scope_def = m_scopes.at(source.idx); -#if 1 ASSERT_BUG(sp, src_scope_def.data.is_Owning(), "Rasising scopes can only be done on temporaries (source)"); ASSERT_BUG(sp, src_scope_def.data.as_Owning().is_temporary, "Rasising scopes can only be done on temporaries (source)"); auto& src_list = src_scope_def.data.as_Owning().slots; for(auto idx : src_list) { - DEBUG("> Raising " << ::MIR::LValue::make_Local(idx)); + DEBUG("> Raising " << ::MIR::LValue::new_Local(idx)); assert(idx >= m_first_temp_idx); } @@ -910,13 +884,6 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle // Move all defined variables from one to the other auto& tgt_list = tgt_scope_def.data.as_Owning().slots; tgt_list.insert( tgt_list.end(), src_list.begin(), src_list.end() ); -#else - auto list = src_scope_def.data.as_Temporaries().temporaries; - for(auto idx : list) - { - this->raise_temporaries(sp, ::MIR::LValue::make_Temporary({ idx }), target); - } -#endif // Scope completed m_scope_stack.pop_back(); @@ -979,7 +946,7 @@ void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope, for(size_t i = 0; i < m_arg_states.size(); i ++) { const auto& state = get_slot_state(sp, i, SlotType::Argument); - this->drop_value_from_state(sp, state, ::MIR::LValue::make_Argument({ static_cast<unsigned>(i) })); + this->drop_value_from_state(sp, state, ::MIR::LValue::new_Argument(static_cast<unsigned>(i))); } } } @@ -1060,7 +1027,7 @@ namespace }); if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); + merge_state(sp, builder, ::MIR::LValue::new_Deref(lv.clone()), *ose.inner_state, *nse.inner_state); } else { @@ -1086,13 +1053,13 @@ namespace if( is_enum ) { for(size_t i = 0; i < ose.inner_states.size(); i ++) { - merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], nse.inner_states[i]); + merge_state(sp, builder, ::MIR::LValue::new_Downcast(lv.clone(), static_cast<unsigned int>(i)), ose.inner_states[i], nse.inner_states[i]); } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); + merge_state(sp, builder, ::MIR::LValue::new_Field(lv.clone(), i), ose.inner_states[i], nse.inner_states[i]); } } } return; @@ -1168,7 +1135,7 @@ namespace }); if( is_box ) { - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); + merge_state(sp, builder, ::MIR::LValue::new_Deref(lv.clone()), *ose.inner_state, *nse.inner_state); } else { BUG(sp, "MovedOut on non-Box"); @@ -1192,19 +1159,19 @@ namespace } auto& ose = old_state.as_Partial(); if( is_enum ) { - auto ilv = ::MIR::LValue::make_Downcast({ box$(lv.clone()), 0 }); + auto ilv = ::MIR::LValue::new_Downcast(lv.clone(), 0); for(size_t i = 0; i < ose.inner_states.size(); i ++) { merge_state(sp, builder, ilv, ose.inner_states[i], nse.inner_states[i]); - ilv.as_Downcast().variant_index ++; + ilv.inc_Downcast(); } } else { - auto ilv = ::MIR::LValue::make_Field({ box$(lv.clone()), 0 }); + auto ilv = ::MIR::LValue::new_Field(lv.clone(), 0); for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { merge_state(sp, builder, ilv, ose.inner_states[i], nse.inner_states[i]); - ilv.as_Field().field_index ++; + ilv.inc_Field(); } } } return; @@ -1268,13 +1235,13 @@ namespace if( is_enum ) { for(size_t i = 0; i < ose.inner_states.size(); i ++) { - merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], nse.inner_states[i]); + merge_state(sp, builder, ::MIR::LValue::new_Downcast(lv.clone(), static_cast<unsigned int>(i)), ose.inner_states[i], nse.inner_states[i]); } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); + merge_state(sp, builder, ::MIR::LValue::new_Field(lv.clone(), i), ose.inner_states[i], nse.inner_states[i]); } } return; } @@ -1309,7 +1276,7 @@ namespace builder.push_stmt_set_dropflag_val(sp, ose.outer_flag, is_valid); } - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, new_state); + merge_state(sp, builder, ::MIR::LValue::new_Deref(lv.clone()), *ose.inner_state, new_state); return ; } case VarState::TAG_Optional: { const auto& nse = new_state.as_Optional(); @@ -1334,7 +1301,7 @@ namespace builder.push_stmt_set_dropflag_other(sp, ose.outer_flag, nse); builder.push_stmt_set_dropflag_default(sp, nse); } - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, new_state); + merge_state(sp, builder, ::MIR::LValue::new_Deref(lv.clone()), *ose.inner_state, new_state); return; } case VarState::TAG_MovedOut: { const auto& nse = new_state.as_MovedOut(); @@ -1342,7 +1309,7 @@ namespace { TODO(sp, "Handle mismatched flags in MovedOut"); } - merge_state(sp, builder, ::MIR::LValue::make_Deref({ box$(lv.clone()) }), *ose.inner_state, *nse.inner_state); + merge_state(sp, builder, ::MIR::LValue::new_Deref(lv.clone()), *ose.inner_state, *nse.inner_state); return; } case VarState::TAG_Partial: BUG(sp, "MovedOut->Partial not valid"); @@ -1366,13 +1333,13 @@ namespace if( is_enum ) { for(size_t i = 0; i < ose.inner_states.size(); i ++) { - merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], new_state); + merge_state(sp, builder, ::MIR::LValue::new_Downcast(lv.clone(), static_cast<unsigned int>(i)), ose.inner_states[i], new_state); } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], new_state); + merge_state(sp, builder, ::MIR::LValue::new_Field(lv.clone(), i), ose.inner_states[i], new_state); } } return ; @@ -1384,13 +1351,13 @@ namespace if( is_enum ) { for(size_t i = 0; i < ose.inner_states.size(); i ++) { - merge_state(sp, builder, ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) }), ose.inner_states[i], nse.inner_states[i]); + merge_state(sp, builder, ::MIR::LValue::new_Downcast(lv.clone(), static_cast<unsigned int>(i)), ose.inner_states[i], nse.inner_states[i]); } } else { for(unsigned int i = 0; i < ose.inner_states.size(); i ++ ) { - merge_state(sp, builder, ::MIR::LValue::make_Field({ box$(lv.clone()), i }), ose.inner_states[i], nse.inner_states[i]); + merge_state(sp, builder, ::MIR::LValue::new_Field(lv.clone(), i), ose.inner_states[i], nse.inner_states[i]); } } } return ; @@ -1419,8 +1386,8 @@ void MirBuilder::terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_l merge_state(sp, *this, val_cb(idx), old_state, get_slot_state(sp, idx, type)); } }; - merge_list(sd_loop.changed_slots, sd_loop.exit_state.states, ::MIR::LValue::make_Local, SlotType::Local); - merge_list(sd_loop.changed_args, sd_loop.exit_state.arg_states, [](auto v){ return ::MIR::LValue::make_Argument({v}); }, SlotType::Argument); + merge_list(sd_loop.changed_slots, sd_loop.exit_state.states, ::MIR::LValue::new_Local, SlotType::Local); + merge_list(sd_loop.changed_args, sd_loop.exit_state.arg_states, [](auto v){ return ::MIR::LValue::new_Argument(v); }, SlotType::Argument); } else { @@ -1475,7 +1442,7 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r auto it = states.find(idx); const auto& src_state = (it != states.end() ? it->second : get_slot_state(sp, idx, type, 1)); - auto lv = (type == SlotType::Local ? ::MIR::LValue::make_Local(idx) : ::MIR::LValue::make_Argument({idx})); + auto lv = (type == SlotType::Local ? ::MIR::LValue::new_Local(idx) : ::MIR::LValue::new_Argument(idx)); merge_state(sp, *this, mv$(lv), out_state, src_state); } }; @@ -1565,7 +1532,7 @@ void MirBuilder::complete_scope(ScopeDef& sd) auto& vs = builder.get_slot_state_mut(sp, ent.first, SlotType::Local); if( vs != ent.second ) { - DEBUG(::MIR::LValue::make_Local(ent.first) << " " << vs << " => " << ent.second); + DEBUG(::MIR::LValue::new_Local(ent.first) << " " << vs << " => " << ent.second); vs = ::std::move(ent.second); } } @@ -1574,7 +1541,7 @@ void MirBuilder::complete_scope(ScopeDef& sd) auto& vs = builder.get_slot_state_mut(sp, ent.first, SlotType::Argument); if( vs != ent.second ) { - DEBUG(::MIR::LValue::make_Argument({ent.first}) << " " << vs << " => " << ent.second); + DEBUG(::MIR::LValue::new_Argument(ent.first) << " " << vs << " => " << ent.second); vs = ::std::move(ent.second); } } @@ -1612,7 +1579,7 @@ void MirBuilder::complete_scope(ScopeDef& sd) for(auto& ent : e->changed_slots) { auto& vs = this->get_slot_state_mut(sd.span, ent.first, SlotType::Local); - auto lv = ::MIR::LValue::make_Local(ent.first); + auto lv = ::MIR::LValue::new_Local(ent.first); DEBUG(lv << " " << vs << " => " << ent.second); if( vs != ent.second ) { @@ -1630,97 +1597,96 @@ void MirBuilder::complete_scope(ScopeDef& sd) } } -void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std::function<void(const ::HIR::TypeRef&)> cb) const +void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std::function<void(const ::HIR::TypeRef&)> cb, const ::MIR::LValue::Wrapper* stop_wrapper/*=nullptr*/) const { - TU_MATCH(::MIR::LValue, (val), (e), + ::HIR::TypeRef tmp; + const ::HIR::TypeRef* ty_p = nullptr; + TU_MATCHA( (val.m_root), (e), (Return, TODO(sp, "Return"); ), (Argument, - cb( m_args.at(e.idx).second ); + ty_p = &m_args.at(e).second; ), (Local, - cb( m_output.locals.at(e) ); + ty_p = &m_output.locals.at(e); ), (Static, - TU_MATCHA( (e->m_data), (pe), + TU_MATCHA( (e.m_data), (pe), (Generic, ASSERT_BUG(sp, pe.m_params.m_types.empty(), "Path params on static"); const auto& s = m_resolve.m_crate.get_static_by_path(sp, pe.m_path); - cb( s.m_type ); + ty_p = &s.m_type; ), (UfcsKnown, - TODO(sp, "Static - UfcsKnown - " << *e); + TODO(sp, "Static - UfcsKnown - " << e); ), (UfcsUnknown, - BUG(sp, "Encountered UfcsUnknown in Static - " << *e); + BUG(sp, "Encountered UfcsUnknown in Static - " << e); ), (UfcsInherent, - TODO(sp, "Static - UfcsInherent - " << *e); + TODO(sp, "Static - UfcsInherent - " << e); ) ) - ), - (Field, - with_val_type(sp, *e.val, [&](const auto& ty){ + ) + ) + assert(ty_p); + for(const auto& w : val.m_wrappers) + { + if( &w == stop_wrapper ) + { + stop_wrapper = nullptr; // Reset so the below bugcheck can work + break; + } + const auto& ty = *ty_p; + ty_p = nullptr; + auto maybe_monomorph = [&](const ::HIR::GenericParams& params_def, const ::HIR::Path& p, const ::HIR::TypeRef& t)->const ::HIR::TypeRef& { + if( monomorphise_type_needed(t) ) { + tmp = monomorphise_type(sp, params_def, p.m_data.as_Generic().m_params, t); + m_resolve.expand_associated_types(sp, tmp); + return tmp; + } + else { + return t; + } + }; + TU_MATCH_HDRA( (w), {) + TU_ARMA(Field, field_index) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( BUG(sp, "Field access on unexpected type - " << ty); ), (Array, - cb( *te.inner ); + ty_p = &*te.inner; ), (Slice, - cb( *te.inner ); + ty_p = &*te.inner; ), (Path, - ::HIR::TypeRef tmp; if( const auto* tep = te.binding.opt_Struct() ) { const auto& str = **tep; - auto maybe_monomorph = [&](const ::HIR::TypeRef& t)->const ::HIR::TypeRef& { - if( monomorphise_type_needed(t) ) { - tmp = monomorphise_type(sp, str.m_params, te.path.m_data.as_Generic().m_params, t); - m_resolve.expand_associated_types(sp, tmp); - return tmp; - } - else { - return t; - } - }; TU_MATCHA( (str.m_data), (se), (Unit, BUG(sp, "Field on unit-like struct - " << ty); ), (Tuple, - ASSERT_BUG(sp, e.field_index < se.size(), - "Field index out of range in tuple-struct " << ty << " - " << e.field_index << " > " << se.size()); - const auto& fld = se[e.field_index]; - cb( maybe_monomorph(fld.ent) ); + ASSERT_BUG(sp, field_index < se.size(), + "Field index out of range in tuple-struct " << ty << " - " << field_index << " > " << se.size()); + const auto& fld = se[field_index]; + ty_p = &maybe_monomorph(str.m_params, te.path, fld.ent); ), (Named, - ASSERT_BUG(sp, e.field_index < se.size(), - "Field index out of range in struct " << ty << " - " << e.field_index << " > " << se.size()); - const auto& fld = se[e.field_index].second; - cb( maybe_monomorph(fld.ent) ); + ASSERT_BUG(sp, field_index < se.size(), + "Field index out of range in struct " << ty << " - " << field_index << " > " << se.size()); + const auto& fld = se[field_index].second; + ty_p = &maybe_monomorph(str.m_params, te.path, fld.ent); ) ) } - else if( const auto* tep = te.binding.opt_Union() ) + else if( /*const auto* tep =*/ te.binding.opt_Union() ) { BUG(sp, "Field access on a union isn't valid, use Downcast instead - " << ty); - const auto& unm = **tep; - auto maybe_monomorph = [&](const ::HIR::TypeRef& t)->const ::HIR::TypeRef& { - if( monomorphise_type_needed(t) ) { - tmp = monomorphise_type(sp, unm.m_params, te.path.m_data.as_Generic().m_params, t); - m_resolve.expand_associated_types(sp, tmp); - return tmp; - } - else { - return t; - } - }; - ASSERT_BUG(sp, e.field_index < unm.m_variants.size(), "Field index out of range for union"); - cb( maybe_monomorph(unm.m_variants.at(e.field_index).second.ent) ); } else { @@ -1728,14 +1694,12 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: } ), (Tuple, - ASSERT_BUG(sp, e.field_index < te.size(), "Field index out of range in tuple " << e.field_index << " >= " << te.size()); - cb( te[e.field_index] ); + ASSERT_BUG(sp, field_index < te.size(), "Field index out of range in tuple " << field_index << " >= " << te.size()); + ty_p = &te[field_index]; ) ) - }); - ), - (Deref, - with_val_type(sp, *e.val, [&](const auto& ty){ + } + TU_ARMA(Deref, _e) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( BUG(sp, "Deref on unexpected type - " << ty); @@ -1743,38 +1707,34 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: (Path, if( const auto* inner_ptr = this->is_type_owned_box(ty) ) { - cb( *inner_ptr ); + ty_p = &*inner_ptr; } else { BUG(sp, "Deref on unexpected type - " << ty); } ), (Pointer, - cb(*te.inner); + ty_p = &*te.inner; ), (Borrow, - cb(*te.inner); + ty_p = &*te.inner; ) ) - }); - ), - (Index, - with_val_type(sp, *e.val, [&](const auto& ty){ + } + TU_ARMA(Index, _index_val) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( BUG(sp, "Index on unexpected type - " << ty); ), (Slice, - cb(*te.inner); + ty_p = &*te.inner; ), (Array, - cb(*te.inner); + ty_p = &*te.inner; ) ) - }); - ), - (Downcast, - with_val_type(sp, *e.val, [&](const auto& ty){ + } + TU_ARMA(Downcast, variant_index) { TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), ( BUG(sp, "Downcast on unexpected type - " << ty); @@ -1785,33 +1745,19 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: const auto& enm = **pbe; ASSERT_BUG(sp, enm.m_data.is_Data(), "Downcast on non-data enum"); const auto& variants = enm.m_data.as_Data(); - ASSERT_BUG(sp, e.variant_index < variants.size(), "Variant index out of range"); - const auto& variant = variants[e.variant_index]; + ASSERT_BUG(sp, variant_index < variants.size(), "Variant index out of range"); + const auto& variant = variants[variant_index]; - if( monomorphise_type_needed(variant.type) ) { - auto tmp = monomorphise_type(sp, enm.m_params, te.path.m_data.as_Generic().m_params, variant.type); - m_resolve.expand_associated_types(sp, tmp); - cb(tmp); - } - else { - cb(variant.type); - } + ty_p = &maybe_monomorph(enm.m_params, te.path, variant.type); } else if( const auto* pbe = te.binding.opt_Union() ) { const auto& unm = **pbe; - ASSERT_BUG(sp, e.variant_index < unm.m_variants.size(), "Variant index out of range"); - const auto& variant = unm.m_variants.at(e.variant_index); + ASSERT_BUG(sp, variant_index < unm.m_variants.size(), "Variant index out of range"); + const auto& variant = unm.m_variants.at(variant_index); const auto& fld = variant.second; - if( monomorphise_type_needed(fld.ent) ) { - auto sty = monomorphise_type(sp, unm.m_params, te.path.m_data.as_Generic().m_params, fld.ent); - m_resolve.expand_associated_types(sp, sty); - cb(sty); - } - else { - cb(fld.ent); - } + ty_p = &maybe_monomorph(unm.m_params, te.path, fld.ent); } else { @@ -1819,9 +1765,12 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: } ) ) - }); - ) - ) + } + } + assert(ty_p); + } + ASSERT_BUG(sp, !stop_wrapper, "A stop wrapper was passed, but not found"); + cb(*ty_p); } bool MirBuilder::lvalue_is_copy(const Span& sp, const ::MIR::LValue& val) const @@ -1998,165 +1947,177 @@ VarState& MirBuilder::get_slot_state_mut(const Span& sp, unsigned int idx, SlotT } } -const VarState& MirBuilder::get_val_state(const Span& sp, const ::MIR::LValue& lv, unsigned int skip_count) -{ - TODO(sp, ""); -} -VarState& MirBuilder::get_val_state_mut(const Span& sp, const ::MIR::LValue& lv) +VarState* MirBuilder::get_val_state_mut_p(const Span& sp, const ::MIR::LValue& lv) { TRACE_FUNCTION_F(lv); - TU_MATCHA( (lv), (e), + VarState* vs; + TU_MATCHA( (lv.m_root), (e), (Return, BUG(sp, "Move of return value"); - return get_slot_state_mut(sp, ~0u, SlotType::Local); + vs = &get_slot_state_mut(sp, ~0u, SlotType::Local); ), (Argument, - return get_slot_state_mut(sp, e.idx, SlotType::Argument); + vs = &get_slot_state_mut(sp, e, SlotType::Argument); ), (Local, - return get_slot_state_mut(sp, e, SlotType::Local); + vs = &get_slot_state_mut(sp, e, SlotType::Local); ), (Static, - BUG(sp, "Attempting to mutate state of a static"); - ), - (Field, - auto& ivs = get_val_state_mut(sp, *e.val); - VarState tpl; - TU_MATCHA( (ivs), (ivse), - (Invalid, - //BUG(sp, "Mutating inner state of an invalidated composite - " << lv); - tpl = VarState::make_Valid({}); - ), - (MovedOut, - BUG(sp, "Field on value with MovedOut state - " << lv); - ), - (Partial, - ), - (Optional, - tpl = ivs.clone(); - ), - (Valid, - tpl = VarState::make_Valid({}); - ) + return nullptr; + //BUG(sp, "Attempting to mutate state of a static"); ) - if( !ivs.is_Partial() ) - { - size_t n_flds = 0; - with_val_type(sp, *e.val, [&](const auto& ty) { - DEBUG("ty = " << ty); - if(const auto* e = ty.m_data.opt_Path()) { - ASSERT_BUG(sp, e->binding.is_Struct(), ""); - const auto& str = *e->binding.as_Struct(); - TU_MATCHA( (str.m_data), (se), - (Unit, - BUG(sp, "Field access of unit-like struct"); - ), - (Tuple, - n_flds = se.size(); - ), - (Named, - n_flds = se.size(); - ) - ) - } - else if(const auto* e = ty.m_data.opt_Tuple()) { - n_flds = e->size(); - } - else if(const auto* e = ty.m_data.opt_Array()) { - n_flds = e->size_val; - } - else { - TODO(sp, "Determine field count for " << ty); - } - }); - ::std::vector<VarState> inner_vs; inner_vs.reserve(n_flds); - for(size_t i = 0; i < n_flds; i++) - inner_vs.push_back( tpl.clone() ); - ivs = VarState::make_Partial({ mv$(inner_vs) }); - } - return ivs.as_Partial().inner_states.at(e.field_index); - ), - (Deref, - // HACK: If the dereferenced type is a Box ("owned_box") then hack in move and shallow drop - bool is_box = false; - if( this->m_lang_Box ) - { - with_val_type(sp, *e.val, [&](const auto& ty){ - DEBUG("ty = " << ty); - is_box = this->is_type_owned_box(ty); - }); - } - + ) - if( is_box ) - { - auto& ivs = get_val_state_mut(sp, *e.val); - if( ! ivs.is_MovedOut() ) + for(const auto& w : lv.m_wrappers) + { + auto& ivs = *vs; + vs = nullptr; + TU_MATCH_HDRA( (w), { ) + TU_ARMA(Field, field_index) { + VarState tpl; + TU_MATCHA( (ivs), (ivse), + (Invalid, + //BUG(sp, "Mutating inner state of an invalidated composite - " << lv); + tpl = VarState::make_Valid({}); + ), + (MovedOut, + BUG(sp, "Field on value with MovedOut state - " << lv); + ), + (Partial, + ), + (Optional, + tpl = ivs.clone(); + ), + (Valid, + tpl = VarState::make_Valid({}); + ) + ) + if( !ivs.is_Partial() ) { - ::std::vector<VarState> inner; - inner.push_back(VarState::make_Valid({})); - unsigned int drop_flag = (ivs.is_Optional() ? ivs.as_Optional() : ~0u); - ivs = VarState::make_MovedOut({ box$(VarState::make_Valid({})), drop_flag }); + size_t n_flds = 0; + with_val_type(sp, lv, [&](const auto& ty) { + DEBUG("ty = " << ty); + if(const auto* e = ty.m_data.opt_Path()) { + ASSERT_BUG(sp, e->binding.is_Struct(), ""); + const auto& str = *e->binding.as_Struct(); + TU_MATCHA( (str.m_data), (se), + (Unit, + BUG(sp, "Field access of unit-like struct"); + ), + (Tuple, + n_flds = se.size(); + ), + (Named, + n_flds = se.size(); + ) + ) + } + else if(const auto* e = ty.m_data.opt_Tuple()) { + n_flds = e->size(); + } + else if(const auto* e = ty.m_data.opt_Array()) { + n_flds = e->size_val; + } + else { + TODO(sp, "Determine field count for " << ty); + } + }, &w); + ::std::vector<VarState> inner_vs; inner_vs.reserve(n_flds); + for(size_t i = 0; i < n_flds; i++) + inner_vs.push_back( tpl.clone() ); + ivs = VarState::make_Partial({ mv$(inner_vs) }); + } + vs = &ivs.as_Partial().inner_states.at(field_index); + } + TU_ARMA(Deref, _e) { + // HACK: If the dereferenced type is a Box ("owned_box") then hack in move and shallow drop + bool is_box = false; + if( this->m_lang_Box ) + { + with_val_type(sp, lv, [&](const auto& ty){ + DEBUG("ty = " << ty); + is_box = this->is_type_owned_box(ty); + }, &w); } - return *ivs.as_MovedOut().inner_state; - } - else - { - BUG(sp, "Move out of deref with non-Copy values - &move? - " << lv << " : " << FMT_CB(ss, this->with_val_type(sp, lv, [&](const auto& ty){ss<<ty;});) ); - } - ), - (Index, - BUG(sp, "Move out of index with non-Copy values - Partial move?"); - ), - (Downcast, - // TODO: What if the inner is Copy? What if the inner is a hidden pointer? - auto& ivs = get_val_state_mut(sp, *e.val); - //static VarState ivs; ivs = VarState::make_Valid({}); - if( !ivs.is_Partial() ) - { - ASSERT_BUG(sp, !ivs.is_MovedOut(), "Downcast of a MovedOut value"); - - size_t var_count = 0; - with_val_type(sp, *e.val, [&](const auto& ty){ - DEBUG("ty = " << ty); - ASSERT_BUG(sp, ty.m_data.is_Path(), "Downcast on non-Path type - " << ty); - const auto& pb = ty.m_data.as_Path().binding; - // TODO: What about unions? - // - Iirc, you can't move out of them so they will never have state mutated - if( pb.is_Enum() ) - { - const auto& enm = *pb.as_Enum(); - var_count = enm.num_variants(); - } - else if( const auto* pbe = pb.opt_Union() ) + + if( is_box ) + { + if( ! ivs.is_MovedOut() ) { - const auto& unm = **pbe; - var_count = unm.m_variants.size(); + ::std::vector<VarState> inner; + inner.push_back(VarState::make_Valid({})); + unsigned int drop_flag = (ivs.is_Optional() ? ivs.as_Optional() : ~0u); + ivs = VarState::make_MovedOut({ box$(VarState::make_Valid({})), drop_flag }); } - else + vs = &*ivs.as_MovedOut().inner_state; + } + else + { + return nullptr; + } + } + TU_ARMA(Index, e) { + return nullptr; + } + TU_ARMA(Downcast, variant_index) { + if( !ivs.is_Partial() ) + { + ASSERT_BUG(sp, !ivs.is_MovedOut(), "Downcast of a MovedOut value"); + + size_t var_count = 0; + with_val_type(sp, lv, [&](const auto& ty){ + DEBUG("ty = " << ty); + ASSERT_BUG(sp, ty.m_data.is_Path(), "Downcast on non-Path type - " << ty); + const auto& pb = ty.m_data.as_Path().binding; + // TODO: What about unions? + // - Iirc, you can't move out of them so they will never have state mutated + if( pb.is_Enum() ) + { + const auto& enm = *pb.as_Enum(); + var_count = enm.num_variants(); + } + else if( const auto* pbe = pb.opt_Union() ) + { + const auto& unm = **pbe; + var_count = unm.m_variants.size(); + } + else + { + BUG(sp, "Downcast on non-Enum/Union - " << ty); + } + }, &w); + + ::std::vector<VarState> inner; + for(size_t i = 0; i < var_count; i ++) { - BUG(sp, "Downcast on non-Enum/Union - " << ty); + inner.push_back( VarState::make_Invalid(InvalidType::Uninit) ); } - }); + inner[variant_index] = mv$(ivs); + ivs = VarState::make_Partial({ mv$(inner) }); + } - ::std::vector<VarState> inner; - for(size_t i = 0; i < var_count; i ++) - { - inner.push_back( VarState::make_Invalid(InvalidType::Uninit) ); + vs = &ivs.as_Partial().inner_states.at(variant_index); } - inner[e.variant_index] = mv$(ivs); - ivs = VarState::make_Partial({ mv$(inner) }); } - - return ivs.as_Partial().inner_states.at(e.variant_index); - ) - ) - BUG(sp, "Fell off send of get_val_state_mut"); + assert(vs); + } + return vs; +} +VarState& MirBuilder::get_val_state_mut(const Span& sp, const ::MIR::LValue& lv) +{ + auto rv_p = this->get_val_state_mut_p(sp, lv); + if( !rv_p ) + { + //BUG(sp, "Move out of index with non-Copy values - Partial move?"); + BUG(sp, "Move out of deref with non-Copy values - &move? - " << lv << " : " << FMT_CB(ss, this->with_val_type(sp, lv, [&](const auto& ty){ss<<ty;});) ); + } + return *rv_p; } void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR::LValue lv) { + TRACE_FUNCTION_F(lv << " " << vs); TU_MATCHA( (vs), (vse), (Invalid, ), @@ -2170,7 +2131,7 @@ void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR }); if( is_box ) { - drop_value_from_state(sp, *vse.inner_state, ::MIR::LValue::make_Deref({ box$(lv.clone()) })); + drop_value_from_state(sp, *vse.inner_state, ::MIR::LValue::new_Deref(lv.clone())); push_stmt_drop_shallow(sp, mv$(lv), vse.outer_flag); } else @@ -2190,7 +2151,7 @@ void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR DEBUG("TODO: Switch based on enum value"); //for(size_t i = 0; i < vse.inner_states.size(); i ++) //{ - // drop_value_from_state(sp, vse.inner_states[i], ::MIR::LValue::make_Downcast({ box$(lv.clone()), static_cast<unsigned int>(i) })); + // drop_value_from_state(sp, vse.inner_states[i], ::MIR::LValue::new_Downcast(lv.clone(), static_cast<unsigned int>(i))); //} } else if( is_union ) @@ -2201,7 +2162,7 @@ void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR { for(size_t i = 0; i < vse.inner_states.size(); i ++) { - drop_value_from_state(sp, vse.inner_states[i], ::MIR::LValue::make_Field({ box$(lv.clone()), static_cast<unsigned int>(i) })); + drop_value_from_state(sp, vse.inner_states[i], ::MIR::LValue::new_Field(lv.clone(), static_cast<unsigned int>(i))); } } ), @@ -2219,7 +2180,7 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) { const auto& vs = get_slot_state(sd.span, idx, SlotType::Local); DEBUG("slot" << idx << " - " << vs); - drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Local(idx) ); + drop_value_from_state( sd.span, vs, ::MIR::LValue::new_Local(idx) ); } ), (Split, @@ -2243,18 +2204,18 @@ void MirBuilder::moved_lvalue(const Span& sp, const ::MIR::LValue& lv) } } -const ::MIR::LValue& MirBuilder::get_ptr_to_dst(const Span& sp, const ::MIR::LValue& lv) const +::MIR::LValue MirBuilder::get_ptr_to_dst(const Span& sp, const ::MIR::LValue& lv) const { // Undo field accesses - const auto* lvp = &lv; - while(lvp->is_Field()) - lvp = &*lvp->as_Field().val; + size_t count = 0; + while( count < lv.m_wrappers.size() && lv.m_wrappers[ lv.m_wrappers.size()-1 - count ].is_Field() ) + count ++; // TODO: Enum variants? - ASSERT_BUG(sp, lvp->is_Deref(), "Access of an unsized field without a dereference - " << lv); + ASSERT_BUG(sp, count < lv.m_wrappers.size() && lv.m_wrappers[ lv.m_wrappers.size()-1 - count ].is_Deref(), "Access of an unsized field without a dereference - " << lv); - return *lvp->as_Deref().val; + return lv.clone_unwrapped(count+1); } // -------------------------------------------------------------------- diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 440f65db..a08aa055 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -305,49 +305,72 @@ namespace { Borrow, // Any borrow }; - bool visit_mir_lvalue_mut(::MIR::LValue& lv, ValUsage u, ::std::function<bool(::MIR::LValue& , ValUsage)> cb) + bool visit_mir_lvalues_inner(const ::MIR::LValue& lv, ValUsage u, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb) { - //TRACE_FUNCTION_F(lv); - if( cb(lv, u) ) - return true; - TU_MATCHA( (lv), (e), - (Return, - ), - (Argument, - ), - (Local, - ), - (Static, - ), - (Field, - // HACK: If "moving", use a "Read" value usage (covers some quirks) - return visit_mir_lvalue_mut(*e.val, u == ValUsage::Move ? ValUsage::Read : u, cb); - ), - (Deref, - return visit_mir_lvalue_mut(*e.val, u == ValUsage::Borrow ? u : ValUsage::Read, cb); - ), - (Index, - bool rv = false; - rv |= visit_mir_lvalue_mut(*e.val, u, cb); - rv |= visit_mir_lvalue_mut(*e.idx, ValUsage::Read, cb); - return rv; - ), - (Downcast, - return visit_mir_lvalue_mut(*e.val, u, cb); - ) - ) + for(const auto& w : lv.m_wrappers) + { + if(w.is_Index()) + if( cb(::MIR::LValue::new_Local(w.as_Index()), ValUsage::Read) ) + return true; + } + return cb(lv, u); + } + bool visit_mir_lvalue_mut(::MIR::LValue& lv, ValUsage u, ::std::function<bool(::MIR::LValue::MRef& , ValUsage)> cb) + { + auto lvr = ::MIR::LValue::MRef(lv); + do + { + if( cb(lvr, u) ) + return true; + // TODO: Use a TU_MATCH? + if( lvr.is_Index() ) + { + auto ilv = ::MIR::LValue::new_Local(lvr.as_Index()); + auto ilv_r = ::MIR::LValue::MRef(ilv); + bool rv = cb(ilv_r, ValUsage::Read); + assert(ilv.is_Local() && ilv.as_Local() == lvr.as_Index()); + if( rv ) + return true; + } + else if( lvr.is_Field() ) + { + // HACK: If "moving", use a "Read" value usage (covers some quirks) + if( u == ValUsage::Move ) { + u = ValUsage::Read; + } + } + else if( lvr.is_Deref() ) + { + // TODO: Is this right? + if( u == ValUsage::Borrow ) { + u = ValUsage::Read; + } + } + else + { + // No change + } + } while( lvr.try_unwrap() ); return false; } - bool visit_mir_lvalue(const ::MIR::LValue& lv, ValUsage u, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb) + bool visit_mir_lvalue(const ::MIR::LValue& lv, ValUsage u, ::std::function<bool(const ::MIR::LValue::CRef& , ValUsage)> cb) { return visit_mir_lvalue_mut( const_cast<::MIR::LValue&>(lv), u, [&](auto& v, auto u) { return cb(v,u); } ); } + bool visit_mir_lvalue_raw_mut(::MIR::LValue& lv, ValUsage u, ::std::function<bool(::MIR::LValue& , ValUsage)> cb) + { + return cb(lv, u); + } + bool visit_mir_lvalue_raw(const ::MIR::LValue& lv, ValUsage u, ::std::function<bool(const ::MIR::LValue& , ValUsage)> cb) + { + return cb(lv, u); + } bool visit_mir_lvalue_mut(::MIR::Param& p, ValUsage u, ::std::function<bool(::MIR::LValue& , ValUsage)> cb) { if( auto* e = p.opt_LValue() ) { - return visit_mir_lvalue_mut(*e, u, cb); + return visit_mir_lvalue_raw_mut(*e, u, cb); } else { @@ -358,7 +381,7 @@ namespace { { if( const auto* e = p.opt_LValue() ) { - return visit_mir_lvalue(*e, u, cb); + return visit_mir_lvalue_raw(*e, u, cb); } else { @@ -371,7 +394,7 @@ namespace { bool rv = false; TU_MATCHA( (rval), (se), (Use, - rv |= visit_mir_lvalue_mut(se, ValUsage::Move, cb); // Can move + rv |= visit_mir_lvalue_raw_mut(se, ValUsage::Move, cb); // Can move ), (Constant, ), @@ -379,23 +402,23 @@ namespace { rv |= visit_mir_lvalue_mut(se.val, ValUsage::Read, cb); // Has to be Read ), (Borrow, - rv |= visit_mir_lvalue_mut(se.val, ValUsage::Borrow, cb); + rv |= visit_mir_lvalue_raw_mut(se.val, ValUsage::Borrow, cb); ), (Cast, - rv |= visit_mir_lvalue_mut(se.val, ValUsage::Read, cb); // Also has to be read + rv |= visit_mir_lvalue_raw_mut(se.val, ValUsage::Read, cb); // Also has to be read ), (BinOp, rv |= visit_mir_lvalue_mut(se.val_l, ValUsage::Read, cb); // Same rv |= visit_mir_lvalue_mut(se.val_r, ValUsage::Read, cb); ), (UniOp, - rv |= visit_mir_lvalue_mut(se.val, ValUsage::Read, cb); + rv |= visit_mir_lvalue_raw_mut(se.val, ValUsage::Read, cb); ), (DstMeta, - rv |= visit_mir_lvalue_mut(se.val, ValUsage::Read, cb); // Reads + rv |= visit_mir_lvalue_raw_mut(se.val, ValUsage::Read, cb); // Reads ), (DstPtr, - rv |= visit_mir_lvalue_mut(se.val, ValUsage::Read, cb); + rv |= visit_mir_lvalue_raw_mut(se.val, ValUsage::Read, cb); ), (MakeDst, rv |= visit_mir_lvalue_mut(se.ptr_val, ValUsage::Move, cb); @@ -430,19 +453,19 @@ namespace { TU_MATCHA( (stmt), (e), (Assign, rv |= visit_mir_lvalues_mut(e.src, cb); - rv |= visit_mir_lvalue_mut(e.dst, ValUsage::Write, cb); + rv |= visit_mir_lvalue_raw_mut(e.dst, ValUsage::Write, cb); ), (Asm, for(auto& v : e.inputs) - rv |= visit_mir_lvalue_mut(v.second, ValUsage::Read, cb); + rv |= visit_mir_lvalue_raw_mut(v.second, ValUsage::Read, cb); for(auto& v : e.outputs) - rv |= visit_mir_lvalue_mut(v.second, ValUsage::Write, cb); + rv |= visit_mir_lvalue_raw_mut(v.second, ValUsage::Write, cb); ), (SetDropFlag, ), (Drop, // Well, it mutates... - rv |= visit_mir_lvalue_mut(e.slot, ValUsage::Write, cb); + rv |= visit_mir_lvalue_raw_mut(e.slot, ValUsage::Write, cb); ), (ScopeEnd, ) @@ -468,21 +491,21 @@ namespace { (Panic, ), (If, - visit_mir_lvalue_mut(e.cond, ValUsage::Read, cb); + visit_mir_lvalue_raw_mut(e.cond, ValUsage::Read, cb); ), (Switch, - visit_mir_lvalue_mut(e.val, ValUsage::Read, cb); + visit_mir_lvalue_raw_mut(e.val, ValUsage::Read, cb); ), (SwitchValue, - visit_mir_lvalue_mut(e.val, ValUsage::Read, cb); + visit_mir_lvalue_raw_mut(e.val, ValUsage::Read, cb); ), (Call, if( e.fcn.is_Value() ) { - visit_mir_lvalue_mut(e.fcn.as_Value(), ValUsage::Read, cb); + visit_mir_lvalue_raw_mut(e.fcn.as_Value(), ValUsage::Read, cb); } for(auto& v : e.args) visit_mir_lvalue_mut(v, ValUsage::Move, cb); - visit_mir_lvalue_mut(e.ret_val, ValUsage::Write, cb); + visit_mir_lvalue_raw_mut(e.ret_val, ValUsage::Write, cb); ) ) } @@ -747,12 +770,7 @@ namespace { { if( const auto* e = term.opt_Call() ) { - return visit_mir_lvalue(e->ret_val, ValUsage::Write, [&](const auto& v, auto vu) { - if( v == lv ) { - return vu != ValUsage::Read; - } - return false; - }); + return (e->ret_val.m_root == lv.m_root); } else { @@ -1163,42 +1181,34 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool ::MIR::LValue clone_lval(const ::MIR::LValue& src) const { - TU_MATCHA( (src), (se), + auto wrappers = src.m_wrappers; + for(auto& w : wrappers) + { + if( w.is_Index() ) { + w = ::MIR::LValue::Wrapper::new_Index( this->var_base + w.as_Index() ); + } + } + TU_MATCHA( (src.m_root), (se), (Return, - return this->retval.clone(); + return this->retval.clone_wrapped( mv$(wrappers) ); ), (Argument, - const auto& arg = this->te.args.at(se.idx); - if( this->copy_args[se.idx] != ~0u ) + const auto& arg = this->te.args.at(se); + if( this->copy_args[se] != ~0u ) { - return ::MIR::LValue::make_Local(this->copy_args[se.idx]); + return ::MIR::LValue( ::MIR::LValue::Storage::new_Local(this->copy_args[se]), mv$(wrappers) ); } else { assert( !arg.is_Constant() ); // Should have been handled in the above - return arg.as_LValue().clone(); + return arg.as_LValue().clone_wrapped( mv$(wrappers) ); } ), (Local, - return ::MIR::LValue::make_Local(this->var_base + se); + return ::MIR::LValue( ::MIR::LValue::Storage::new_Local(this->var_base + se), mv$(wrappers) ); ), (Static, - return box$(this->monomorph( *se )); - ), - (Deref, - return ::MIR::LValue::make_Deref({ box$(this->clone_lval(*se.val)) }); - ), - (Field, - return ::MIR::LValue::make_Field({ box$(this->clone_lval(*se.val)), se.field_index }); - ), - (Index, - return ::MIR::LValue::make_Index({ - box$(this->clone_lval(*se.val)), - box$(this->clone_lval(*se.idx)) - }); - ), - (Downcast, - return ::MIR::LValue::make_Downcast({ box$(this->clone_lval(*se.val)), se.variant_index }); + return ::MIR::LValue( ::MIR::LValue::Storage::new_Static(this->monomorph(se)), mv$(wrappers) ); ) ) throw ""; @@ -1329,7 +1339,7 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool // Allocate a temporary for the return value { - cloner.retval = ::MIR::LValue::make_Local( fcn.locals.size() ); + cloner.retval = ::MIR::LValue::new_Local( fcn.locals.size() ); DEBUG("- Storing return value in " << cloner.retval); ::HIR::TypeRef tmp_ty; fcn.locals.push_back( state.get_lvalue_type(tmp_ty, te->ret_val).clone() ); @@ -1373,7 +1383,7 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool { ::HIR::TypeRef tmp; auto ty = val.is_Constant() ? state.get_const_type(val.as_Constant()) : state.get_lvalue_type(tmp, val.as_LValue()).clone(); - auto lv = ::MIR::LValue::make_Local( static_cast<unsigned>(fcn.locals.size()) ); + auto lv = ::MIR::LValue::new_Local( static_cast<unsigned>(fcn.locals.size()) ); fcn.locals.push_back( mv$(ty) ); auto rval = val.is_Constant() ? ::MIR::RValue(mv$(val.as_Constant())) : ::MIR::RValue( mv$(val.as_LValue()) ); auto stmt = ::MIR::Statement::make_Assign({ mv$(lv), mv$(rval) }); @@ -1442,7 +1452,7 @@ bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn) const auto& src_rvalue = bb.statements[it->second].as_Assign().src; // Destination invalidated? - if( lv.is_Local() && it->first == lv.as_Local() ) + if( lv.m_root.is_Local() && it->first == lv.m_root.as_Local() ) { switch(vu) { @@ -1463,9 +1473,9 @@ bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn) case ValUsage::Borrow: // Borrows are annoying, assume they invalidate anything used case ValUsage::Write: // Mutated? It's invalidated case ValUsage::Move: // Moved? Now invalid - visit_mir_lvalues(src_rvalue, [&](const auto& s_lv, auto /*s_vu*/) { + visit_mir_lvalues(src_rvalue, [&](const ::MIR::LValue& s_lv, auto s_vu) { //DEBUG(" " << s_lv << " ?= " << lv); - if( s_lv == lv ) + if( s_lv.m_root == lv.m_root ) { DEBUG(state << "> Invalidates source of Local(" << it->first << ") - " << src_rvalue); invalidated = true; @@ -1499,39 +1509,37 @@ bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn) // > For now, don't do the replacement if it would delete the assignment UNLESS it's directly being used) // 2. Search for replacements - bool top_level = true; - visit_mir_lvalue_mut(top_lv, top_usage, [&](auto& ilv, auto /*i_usage*/) { - if( ilv.is_Local() ) + if( top_lv.m_root.is_Local() ) + { + bool top_level = top_lv.m_wrappers.empty(); + auto ilv = ::MIR::LValue::new_Local(top_lv.m_root.as_Local()); + auto it = local_assignments.find(top_lv.m_root.as_Local()); + if( it != local_assignments.end() ) { - auto it = local_assignments.find(ilv.as_Local()); - if( it != local_assignments.end() ) + const auto& new_val = bb.statements[it->second].as_Assign().src.as_Use(); + // - Copy? All is good. + if( state.lvalue_is_copy(ilv) ) { - // - Copy? All is good. - if( state.lvalue_is_copy(ilv) ) - { - ilv = bb.statements[it->second].as_Assign().src.as_Use().clone(); - DEBUG(state << "> Replace (and keep) Local(" << it->first << ") with " << ilv); - } - // - Top-level (directly used) also good. - else if( top_level && top_usage == ValUsage::Move ) - { - // TODO: DstMeta/DstPtr _doesn't_ move, so shouldn't trigger this. - ilv = bb.statements[it->second].as_Assign().src.as_Use().clone(); - DEBUG(state << "> Replace (and remove) Local(" << it->first << ") with " << ilv); - statements_to_remove.push_back( it->second ); - local_assignments.erase(it); - } - // - Otherwise, remove the record. - else - { - DEBUG(state << "> Non-copy value used within a LValue, remove record of Local(" << it->first << ")"); - local_assignments.erase(it); - } + top_lv = new_val.clone_wrapped(top_lv.m_wrappers.begin(), top_lv.m_wrappers.end()); + DEBUG(state << "> Replace (and keep) Local(" << it->first << ") with " << new_val); + } + // - Top-level (directly used) also good. + else if( top_level && top_usage == ValUsage::Move ) + { + // TODO: DstMeta/DstPtr _doesn't_ move, so shouldn't trigger this. + top_lv = new_val.clone(); + DEBUG(state << "> Replace (and remove) Local(" << it->first << ") with " << new_val); + statements_to_remove.push_back( it->second ); + local_assignments.erase(it); + } + // - Otherwise, remove the record. + else + { + DEBUG(state << "> Non-copy value used within a LValue, remove record of Local(" << it->first << ")"); + local_assignments.erase(it); } } - top_level = false; - return false; - }); + } // Return true to prevent recursion return true; }; @@ -1558,15 +1566,13 @@ bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn) // - Check if this is a new assignment if( stmt.is_Assign() && stmt.as_Assign().dst.is_Local() && stmt.as_Assign().src.is_Use() ) { - if( visit_mir_lvalue(stmt.as_Assign().src.as_Use(), ValUsage::Read, [&](const auto& lv, auto /*vu*/) { - return lv == stmt.as_Assign().dst; - }) ) + const auto& dst_lv = stmt.as_Assign().dst; + const auto& src_lv = stmt.as_Assign().src.as_Use(); + if( visit_mir_lvalues_inner(src_lv, ValUsage::Read, [&](const auto& lv, auto) { return lv.m_root == dst_lv.m_root; }) ) { DEBUG(state << "> Don't record, self-referrential"); } - else if( visit_mir_lvalue(stmt.as_Assign().src.as_Use(), ValUsage::Read, [&](const auto& lv, auto /*vu*/) { - return lv.is_Deref(); - }) ) + else if( ::std::any_of(src_lv.m_wrappers.begin(), src_lv.m_wrappers.end(), [](const auto& w){ return w.is_Deref(); }) ) { DEBUG(state << "> Don't record, dereference"); } @@ -1741,12 +1747,13 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f { DEBUG("Replacing temporaries using {" << replacements << "}"); visit_mir_lvalues_mut(state, fcn, [&](auto& lv, auto ) { - if( auto* ve = lv.opt_Local() ) { - auto it = replacements.find(*ve); + if( lv.m_root.is_Local() ) + { + auto it = replacements.find(lv.m_root.as_Local()); if( it != replacements.end() ) { MIR_DEBUG(state, lv << " => Local(" << it->second << ")"); - *ve = it->second; + lv.m_root = ::MIR::LValue::Storage::new_Local(it->second); return true; } } @@ -2082,33 +2089,34 @@ bool MIR_Optimise_PropagateKnownValues(::MIR::TypeResolve& state, ::MIR::Functio state.set_cur_stmt(bb_idx, i); DEBUG(state << block.statements[i]); visit_mir_lvalues_mut(block.statements[i], [&](::MIR::LValue& lv, auto vu) { - if(const auto* e = lv.opt_Field()) + if(vu == ValUsage::Read && lv.m_wrappers.size() > 1 && lv.m_wrappers.front().is_Field() && lv.m_root.is_Local()) { - if(vu == ValUsage::Read && e->val->is_Local() ) { - // TODO: This value _must_ be Copy for this optimisation to work. - // - OR, it has to somehow invalidate the original tuple - DEBUG(state << "Locating origin of " << lv); - ::HIR::TypeRef tmp; - if( !state.m_resolve.type_is_copy(state.sp, state.get_lvalue_type(tmp, *e->val)) ) + auto field_index = lv.m_wrappers.front().as_Field(); + auto inner_lv = ::MIR::LValue::new_Local(lv.m_root.as_Local()); + auto outer_lv = ::MIR::LValue::new_Field(inner_lv.clone(), field_index); + // TODO: This value _must_ be Copy for this optimisation to work. + // - OR, it has to somehow invalidate the original tuple + DEBUG(state << "Locating origin of " << lv); + ::HIR::TypeRef tmp; + if( !state.m_resolve.type_is_copy(state.sp, state.get_lvalue_type(tmp, inner_lv)) ) + { + DEBUG(state << "- not Copy, can't optimise"); + return false; + } + const auto* source_lvalue = get_field(inner_lv, field_index, bb_idx, i); + if( source_lvalue ) + { + if( outer_lv != *source_lvalue ) { - DEBUG(state << "- not Copy, can't optimise"); - return false; + DEBUG(state << "Source is " << *source_lvalue); + lv = source_lvalue->clone_wrapped( lv.m_wrappers.begin() + 1, lv.m_wrappers.end() ); + change_happend = true; } - const auto* source_lvalue = get_field(*e->val, e->field_index, bb_idx, i); - if( source_lvalue ) + else { - if( lv != *source_lvalue ) - { - DEBUG(state << "Source is " << *source_lvalue); - lv = source_lvalue->clone(); - change_happend = true; - } - else - { - DEBUG(state << "No change"); - } - return false; + DEBUG(state << "No change"); } + return false; } } return false; @@ -2760,15 +2768,17 @@ bool MIR_Optimise_SplitAggregates(::MIR::TypeResolve& state, ::MIR::Function& fc for(auto& blk : fcn.blocks) { auto cb = [&](auto& lv, auto _vu) { - if(lv.is_Field() && lv.as_Field().val->is_Local()) + if( !lv.m_wrappers.empty() && lv.m_wrappers.front().is_Field() && lv.m_root.is_Local() ) { - auto fld_idx = lv.as_Field().field_index; - auto it = replacements.find( lv.as_Field().val->as_Local() ); + auto fld_idx = lv.m_wrappers.front().as_Field(); + auto it = replacements.find( lv.m_root.as_Local() ); if( it != replacements.end() ) { MIR_ASSERT(state, fld_idx < it->second.size(), "Tuple field index out of range"); DEBUG(state << "Replace " << lv << " with Local(" << it->second.at(fld_idx) << ")"); - lv = ::MIR::LValue::make_Local(it->second.at(fld_idx)); + + lv.m_wrappers.erase( lv.m_wrappers.begin() ); + lv.m_root = ::MIR::LValue::Storage::new_Local(it->second.at(fld_idx)); } } return false; @@ -2790,7 +2800,7 @@ bool MIR_Optimise_SplitAggregates(::MIR::TypeResolve& state, ::MIR::Function& fc for(size_t i = 0; i < vals.size(); i ++) { - auto lv = ::MIR::LValue::make_Local(rit->second[i]); + auto lv = ::MIR::LValue::new_Local(rit->second[i]); auto rv = vals[i].is_LValue() ? ::MIR::RValue(::std::move( vals[i].as_LValue() )) : ::MIR::RValue(::std::move( vals[i].as_Constant() )) @@ -2835,13 +2845,16 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F ::std::vector<ValUse> local_uses; void use_lvalue(const ::MIR::LValue& lv, ValUsage ut) { - TU_MATCHA( (lv), (e), - (Return, - ), - (Argument, - ), - (Local, - auto& vu = local_uses[e]; + for(const auto& w : lv.m_wrappers) + { + if( w.is_Index() ){ + //local_uses[w.as_Index()].read += 1; + local_uses[w.as_Index()].borrow += 1; + } + } + if( lv.m_root.is_Local() ) + { + auto& vu = local_uses[lv.m_root.as_Local()]; switch(ut) { case ValUsage::Move: @@ -2849,23 +2862,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F case ValUsage::Write: vu.write += 1; break; case ValUsage::Borrow: vu.borrow += 1; break; } - ), - (Static, - ), - (Field, - use_lvalue(*e.val, ut); - ), - (Deref, - use_lvalue(*e.val, ut); - ), - (Index, - use_lvalue(*e.val, ut); - use_lvalue(*e.idx, ValUsage::Read); - ), - (Downcast, - use_lvalue(*e.val, ut); - ) - ) + } } } val_uses = { ::std::vector<ValUse>(fcn.locals.size()) @@ -2881,7 +2878,11 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F // > Replace usage with the inner of the original `Use` { // 1. Assignments (forward propagate) - ::std::map< ::MIR::LValue, ::MIR::RValue> replacements; + //::std::map< ::MIR::LValue::CRef, ::MIR::RValue> replacements; + ::std::vector< ::std::pair<::MIR::LValue, ::MIR::RValue> > replacements; + auto replacements_find = [&replacements](const ::MIR::LValue::CRef& lv) { + return ::std::find_if(replacements.begin(), replacements.end(), [&](const auto& e) { return lv == e.first; }); + }; for(const auto& block : fcn.blocks) { if( block.terminator.tag() == ::MIR::Terminator::TAGDEAD ) @@ -2889,16 +2890,18 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F for(unsigned int stmt_idx = 0; stmt_idx < block.statements.size(); stmt_idx ++) { + state.set_cur_stmt(&block - &fcn.blocks.front(), stmt_idx); const auto& stmt = block.statements[stmt_idx]; + DEBUG(state << stmt); // > Assignment if( ! stmt.is_Assign() ) continue ; const auto& e = stmt.as_Assign(); // > Of a temporary from with a RValue::Use - if( const auto* de = e.dst.opt_Local() ) + if( e.dst.is_Local() ) { - const auto& vu = val_uses.local_uses[*de]; - DEBUG(e.dst << " - VU " << e.dst << " R:" << vu.read << " W:" << vu.write << " B:" << vu.borrow); + const auto& vu = val_uses.local_uses[e.dst.as_Local()]; + DEBUG(" - VU " << e.dst << " R:" << vu.read << " W:" << vu.write << " B:" << vu.borrow); // TODO: Allow write many? // > Where the variable is written once and read once if( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) @@ -2908,17 +2911,16 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F { continue ; } - DEBUG(e.dst << " = " << e.src); if( e.src.is_Use() ) { // Keep the complexity down const auto* srcp = &e.src.as_Use(); - while( srcp->is_Field() ) - srcp = &*srcp->as_Field().val; - if( !srcp->is_Local() ) + if( ::std::any_of(srcp->m_wrappers.begin(), srcp->m_wrappers.end(), [](auto& w) { return !w.is_Field(); }) ) + continue ; + if( !srcp->m_root.is_Local() ) continue ; - if( replacements.find(*srcp) != replacements.end() ) + if( replacements_find(*srcp) != replacements.end() ) { DEBUG("> Can't replace, source has pending replacement"); continue; @@ -2933,12 +2935,19 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F continue ; } bool src_is_lvalue = e.src.is_Use(); + DEBUG("- Locate usage"); - auto is_lvalue_usage = [&](const auto& lv, auto ){ return lv == e.dst; }; + auto is_lvalue_usage = [&](const auto& lv, auto ){ + return lv.m_root == e.dst.m_root; + //return lv == e.dst; + }; // Returns `true` if the passed lvalue is used as a part of the source auto is_lvalue_in_val = [&](const auto& lv) { - return visit_mir_lvalues(e.src, [&](const auto& slv, auto ) { return lv == slv; }); + return visit_mir_lvalues(e.src, [&](const auto& slv, auto ) { + return lv.m_root == slv.m_root; + //return lv == slv; + }); }; // Eligable for replacement // Find where this value is used @@ -2948,8 +2957,9 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F bool found = false; for(unsigned int si2 = stmt_idx+1; si2 < block.statements.size(); si2 ++) { + state.set_cur_stmt(&block - &fcn.blocks.front(), si2); const auto& stmt2 = block.statements[si2]; - DEBUG("[find usage] " << stmt2); + DEBUG(state << "[find usage] " << stmt2); // Usage found. if( visit_mir_lvalues(stmt2, is_lvalue_usage) ) @@ -2981,7 +2991,15 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F } if( !stop ) { - DEBUG("[find usage] " << block.terminator); + state.set_cur_stmt_term(&block - &fcn.blocks.front()); + DEBUG(state << "[find usage] " << block.terminator); + if( src_is_lvalue ) + { + visit_mir_lvalues(block.terminator, [&](const auto& lv, auto vu) { + found |= is_lvalue_usage(lv, vu); + return found; + }); + } TU_MATCHA( (block.terminator), (e), (Incomplete, ), @@ -2995,29 +3013,15 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F (Panic, ), (If, - if( src_is_lvalue && visit_mir_lvalue(e.cond, ValUsage::Read, is_lvalue_usage) ) - found = true; stop = true; ), (Switch, - if( src_is_lvalue && visit_mir_lvalue(e.val, ValUsage::Read, is_lvalue_usage) ) - found = true; stop = true; ), (SwitchValue, - if( src_is_lvalue && visit_mir_lvalue(e.val, ValUsage::Read, is_lvalue_usage) ) - found = true; stop = true; ), (Call, - if( e.fcn.is_Value() ) - if( src_is_lvalue && visit_mir_lvalue(e.fcn.as_Value(), ValUsage::Read, is_lvalue_usage) ) - found = true; - for(const auto& v : e.args) - { - if( src_is_lvalue && visit_mir_lvalue(v, ValUsage::Read, is_lvalue_usage) ) - found = true; - } stop = true; ) ) @@ -3025,8 +3029,8 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F // Schedule a replacement in a future pass if( found ) { - DEBUG("> Replace " << e.dst << " with " << e.src.as_Use()); - replacements.insert( ::std::make_pair(e.dst.clone(), e.src.clone()) ); + DEBUG("> Schedule replace " << e.dst << " with " << e.src.as_Use()); + replacements.push_back( ::std::make_pair(e.dst.clone(), e.src.clone()) ); } else { @@ -3035,21 +3039,26 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F } // for(stmt : block.statements) } + DEBUG("replacements = " << replacements); + // Apply replacements within replacements for(;;) { unsigned int inner_replaced_count = 0; for(auto& r : replacements) { - visit_mir_lvalues_mut(r.second, [&](auto& lv, auto vu) { + visit_mir_lvalues_mut(r.second, [&](::MIR::LValue& lv, auto vu) { if( vu == ValUsage::Read || vu == ValUsage::Move ) { - auto it = replacements.find(lv); - if( it != replacements.end() && it->second.is_Use() ) - { - lv = it->second.as_Use().clone(); - inner_replaced_count ++; - } + visit_mir_lvalue_mut(lv, vu, [&](::MIR::LValue::MRef& lvr, auto vu) { + auto it = replacements_find(lvr); + if( it != replacements.end() && it->second.is_Use() ) + { + lvr.replace( it->second.as_Use().clone() ); + inner_replaced_count ++; + } + return false; + }); } return false; }); @@ -3057,26 +3066,30 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F if( inner_replaced_count == 0 ) break; } + DEBUG("replacements = " << replacements); // Apply replacements unsigned int replaced = 0; while( replaced < replacements.size() ) { auto old_replaced = replaced; - auto cb = [&](auto& lv, auto vu){ - if( vu == ValUsage::Read || vu == ValUsage::Move ) - { - auto it = replacements.find(lv); - if( it != replacements.end() ) + auto cb = [&](::MIR::LValue& lv, auto vu){ + return visit_mir_lvalue_mut(lv, vu, [&](::MIR::LValue::MRef& lv, auto vu) { + if( vu == ValUsage::Read || vu == ValUsage::Move ) { - MIR_ASSERT(state, it->second.tag() != ::MIR::RValue::TAGDEAD, "Replacement of " << lv << " fired twice"); - MIR_ASSERT(state, it->second.is_Use(), "Replacing a lvalue with a rvalue - " << lv << " with " << it->second); - auto rval = ::std::move(it->second); - lv = ::std::move(rval.as_Use()); - replaced += 1; + auto it = replacements_find(lv); + if( it != replacements.end() ) + { + MIR_ASSERT(state, it->second.tag() != ::MIR::RValue::TAGDEAD, "Replacement of " << lv << " fired twice"); + MIR_ASSERT(state, it->second.is_Use(), "Replacing a lvalue with a rvalue - " << lv << " with " << it->second); + auto rval = ::std::move(it->second); + DEBUG("> Do replace " << lv << " => " << rval); + lv.replace( ::std::move(rval.as_Use()) ); + replaced += 1; + } } - } - return false; + return false; + }); }; for(unsigned int block_idx = 0; block_idx < fcn.blocks.size(); block_idx ++) { @@ -3086,10 +3099,12 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F for(auto& stmt : block.statements) { state.set_cur_stmt(block_idx, (&stmt - &block.statements.front())); + DEBUG(state << stmt); +#if 0 if( stmt.is_Assign() && stmt.as_Assign().src.is_Use() ) { auto& e = stmt.as_Assign(); - auto it = replacements.find(e.src.as_Use()); + auto it = replacements_find(e.src.as_Use()); if( it != replacements.end() ) { MIR_ASSERT(state, it->second.tag() != ::MIR::RValue::TAGDEAD, "Replacement of " << it->first << " fired twice"); @@ -3098,6 +3113,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F } } else +#endif { visit_mir_lvalues_mut(stmt, cb); } @@ -3114,8 +3130,11 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F { state.set_cur_stmt(&block - &fcn.blocks.front(), (it - block.statements.begin())); // If the statement was an assign of a replaced temporary, remove it. - if( it->is_Assign() && replacements.count( it->as_Assign().dst ) > 0 ) + auto it2 = replacements.end(); + if( it->is_Assign() && (it2 = replacements_find(it->as_Assign().dst)) != replacements.end() ) { + DEBUG(state << "Delete " << *it); it = block.statements.erase(it); + } else { MIR_ASSERT(state, !( it->is_Assign() && it->as_Assign().src.tag() == ::MIR::RValue::TAGDEAD ), ""); ++it; @@ -3137,8 +3156,8 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F if( it->as_Assign().src.tag() == ::MIR::RValue::TAGDEAD ) continue ; auto& to_replace_lval = it->as_Assign().dst; - if( const auto* e = to_replace_lval.opt_Local() ) { - const auto& vu = val_uses.local_uses[*e]; + if( to_replace_lval.is_Local() ){ + const auto& vu = val_uses.local_uses[to_replace_lval.as_Local()]; if( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) continue ; } @@ -3162,8 +3181,8 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F // `... = Use(to_replace_lval)` // TODO: Ensure that the target isn't borrowed. - if( const auto* e = new_dst_lval.opt_Local() ) { - const auto& vu = val_uses.local_uses[*e]; + if( new_dst_lval.is_Local() ) { + const auto& vu = val_uses.local_uses[new_dst_lval.as_Local()]; if( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) break ; } @@ -3180,7 +3199,8 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F { // Closure returns `true` if the passed lvalue is a component of `new_dst_lval` auto is_lvalue_in_val = [&](const auto& lv) { - return visit_mir_lvalue(new_dst_lval, ValUsage::Write, [&](const auto& slv, auto ) { return lv == slv; }); + // Don't care about indexing? + return lv.m_root == new_dst_lval.m_root; }; if( visit_mir_lvalues(*it3, [&](const auto& lv, auto ){ return is_lvalue_in_val(lv); }) ) { @@ -3241,8 +3261,11 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F // Ensure that the new destination value isn't used before assignment if( new_dst ) { - auto lvalue_impacts_dst = [&](const ::MIR::LValue& lv) { - return visit_mir_lvalue(*new_dst, ValUsage::Write, [&](const auto& slv, auto ) { return lv == slv; }); + auto lvalue_impacts_dst = [&](const ::MIR::LValue& lv)->bool { + // Returns true if the two lvalues share a common root + // TODO: Could restrict based on the presence of + // deref/field accesses? + return lv.m_root == new_dst->m_root; }; for(auto it = blk2.statements.begin(); it != blk2.statements.end(); ++ it) { @@ -3251,11 +3274,12 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F { DEBUG("- Replace function return " << e.ret_val << " with " << *new_dst); e.ret_val = new_dst->clone(); + // TODO: Invalidate the entry, instead of deleting? it = blk2.statements.erase(it); replacement_happend = true; break; } - if( visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu){ return lv == *new_dst || (vu == ValUsage::Write && lvalue_impacts_dst(lv)); }) ) + if( visit_mir_lvalues(stmt, [&](const MIR::LValue& lv, ValUsage vu){ return lv == *new_dst || (vu == ValUsage::Write && lvalue_impacts_dst(lv)); }) ) { break; } @@ -3289,17 +3313,14 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F } // Remove assignments of locals that are never read - TU_MATCH_DEF( ::MIR::LValue, (se->dst), (de), - ( - ), - (Local, - const auto& vu = val_uses.local_uses[de]; + if( se->dst.is_Local() ) + { + const auto& vu = val_uses.local_uses[se->dst.as_Local()]; if( vu.write == 1 && vu.read == 0 && vu.borrow == 0 ) { DEBUG(state << se->dst << " only written, removing write"); it = block.statements.erase(it)-1; } - ) - ) + } } } // NOTE: Calls can write values, but they also have side-effects @@ -3427,9 +3448,12 @@ bool MIR_Optimise_DeadAssignments(::MIR::TypeResolve& state, ::MIR::Function& fc for(const auto& bb : fcn.blocks) { auto cb = [&](const ::MIR::LValue& lv, ValUsage vu) { - if( lv.is_Local() ) { - read_locals[lv.as_Local()] = true; + if( lv.m_root.is_Local() ) { + read_locals[lv.m_root.as_Local()] = true; } + for(const auto& w : lv.m_wrappers) + if(w.is_Index()) + read_locals[w.as_Index()] = true; return false; }; for(const auto& stmt : bb.statements) @@ -3496,7 +3520,7 @@ bool MIR_Optimise_NoopRemoval(::MIR::TypeResolve& state, ::MIR::Function& fcn) if( it->is_Assign() && it->as_Assign().src.is_Borrow() && it->as_Assign().src.as_Borrow().val.is_Deref() - && *it->as_Assign().src.as_Borrow().val.as_Deref().val == it->as_Assign().dst + && it->as_Assign().src.as_Borrow().val.clone_unwrapped() == it->as_Assign().dst ) { DEBUG(state << "Useless assignment (v = &*v), remove - " << *it); @@ -3520,7 +3544,7 @@ bool MIR_Optimise_NoopRemoval(::MIR::TypeResolve& state, ::MIR::Function& fcn) bool MIR_Optimise_GarbageCollect_Partial(::MIR::TypeResolve& state, ::MIR::Function& fcn) { ::std::vector<bool> visited( fcn.blocks.size() ); - visit_blocks(state, fcn, [&visited](auto bb, const auto& _blokc) { + visit_blocks(state, fcn, [&visited](auto bb, const auto& /*block*/) { assert( !visited[bb] ); visited[bb] = true; }); @@ -3550,12 +3574,19 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn visited[bb] = true; auto assigned_lval = [&](const ::MIR::LValue& lv) { - const auto* lvp = &lv; // TODO: Consume through indexing/field accesses - while(lvp->is_Field()) - lvp = &*lvp->as_Field().val; - if(const auto* le = lvp->opt_Local() ) - used_locals[*le] = true; + for(const auto& w : lv.m_wrappers) + { + if( w.is_Field() ) { + } + else { + return ; + } + } + if( lv.m_root.is_Local() ) + { + used_locals[lv.m_root.as_Local()] = true; + } }; for(const auto& stmt : block.statements) @@ -3629,20 +3660,23 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn auto it = fcn.blocks.begin(); for(unsigned int i = 0; i < visited.size(); i ++) { - if( !visited[i] ) - { - // Delete - DEBUG("GC bb" << i); - it = fcn.blocks.erase(it); - } - else + if( visited[i] ) { - auto lvalue_cb = [&](auto& lv, auto ) { - if(auto* e = lv.opt_Local() ) { - MIR_ASSERT(state, *e < local_rewrite_table.size(), "Variable out of range - " << lv); + auto lvalue_cb = [&](::MIR::LValue& lv, auto ) { + if( lv.m_root.is_Local() ) + { + auto e = lv.m_root.as_Local(); + MIR_ASSERT(state, e < local_rewrite_table.size(), "Variable out of range - " << lv); // If the table entry for this variable is !0, it wasn't marked as used - MIR_ASSERT(state, local_rewrite_table.at(*e) != ~0u, "LValue " << lv << " incorrectly marked as unused"); - *e = local_rewrite_table.at(*e); + MIR_ASSERT(state, local_rewrite_table.at(e) != ~0u, "LValue " << lv << " incorrectly marked as unused"); + lv.m_root = ::MIR::LValue::Storage::new_Local(local_rewrite_table.at(e) ); + } + for(auto& w : lv.m_wrappers) + { + if( w.is_Index()) + { + w = ::MIR::LValue::Wrapper::new_Index(local_rewrite_table.at( w.as_Index() )); + } } return false; }; @@ -3716,6 +3750,7 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn } state.set_cur_stmt_term(i); // Rewrite and advance + visit_mir_lvalues_mut(it->terminator, lvalue_cb); TU_MATCHA( (it->terminator), (e), (Incomplete, ), @@ -3729,49 +3764,45 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn (Panic, ), (If, - visit_mir_lvalue_mut(e.cond, ValUsage::Read, lvalue_cb); e.bb0 = block_rewrite_table[e.bb0]; e.bb1 = block_rewrite_table[e.bb1]; ), (Switch, - visit_mir_lvalue_mut(e.val, ValUsage::Read, lvalue_cb); for(auto& target : e.targets) target = block_rewrite_table[target]; ), (SwitchValue, - visit_mir_lvalue_mut(e.val, ValUsage::Read, lvalue_cb); for(auto& target : e.targets) target = block_rewrite_table[target]; e.def_target = block_rewrite_table[e.def_target]; ), (Call, - if( e.fcn.is_Value() ) { - visit_mir_lvalue_mut(e.fcn.as_Value(), ValUsage::Read, lvalue_cb); - } - for(auto& v : e.args) - visit_mir_lvalue_mut(v, ValUsage::Read, lvalue_cb); - visit_mir_lvalue_mut(e.ret_val, ValUsage::Write, lvalue_cb); e.ret_block = block_rewrite_table[e.ret_block]; e.panic_block = block_rewrite_table[e.panic_block]; ) ) // Delete all statements flagged in a bitmap for deletion - auto stmt_it = it->statements.begin(); - for(auto flag : to_remove_statements) - { - if(flag) { - stmt_it = it->statements.erase(stmt_it); - } - else { - ++ stmt_it; - } - } - - ++it; + assert(it->statements.size() == to_remove_statements.size()); + auto new_end = ::std::remove_if(it->statements.begin(), it->statements.end(), [&](const auto& s){ + size_t stmt_idx = (&s - &it->statements.front()); + return to_remove_statements[stmt_idx]; + }); + it->statements.erase(new_end, it->statements.end()); } + ++it; } + auto new_blocks_end = ::std::remove_if(fcn.blocks.begin(), fcn.blocks.end(), [&](const auto& bb) { + size_t i = &bb - &fcn.blocks.front(); + if( !visited[i] ) { + DEBUG("GC bb" << i); + } + return !visited[i]; + }); + fcn.blocks.erase(new_blocks_end, fcn.blocks.end()); + + // NOTE: Drop flags are bool, so can't use the above hack for(unsigned int i = 0, j = 0; i < n_df; i ++) { if( !used_dfs[i] ) diff --git a/src/trans/auto_impls.cpp b/src/trans/auto_impls.cpp index 07a52857..ea10c8e2 100644 --- a/src/trans/auto_impls.cpp +++ b/src/trans/auto_impls.cpp @@ -55,9 +55,9 @@ namespace { { const auto& lang_Clone = state.resolve.m_crate.get_lang_item_path(sp, "clone"); // Allocate to locals (one for the `&T`, the other for the cloned `T`) - auto borrow_lv = ::MIR::LValue::make_Local( mir_fcn.locals.size() ); + auto borrow_lv = ::MIR::LValue::new_Local( mir_fcn.locals.size() ); mir_fcn.locals.push_back(::HIR::TypeRef::new_borrow(::HIR::BorrowType::Shared, subty.clone())); - auto res_lv = ::MIR::LValue::make_Local( mir_fcn.locals.size() ); + auto res_lv = ::MIR::LValue::new_Local( mir_fcn.locals.size() ); mir_fcn.locals.push_back(subty.clone()); // Call `<T as Clone>::clone`, passing a borrow of the field @@ -97,8 +97,8 @@ void Trans_AutoImpl_Clone(State& state, ::HIR::TypeRef ty) { ::MIR::BasicBlock bb; bb.statements.push_back(::MIR::Statement::make_Assign({ - ::MIR::LValue::make_Return({}), - ::MIR::RValue::make_Use( ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({ 0 })) }) ) + ::MIR::LValue::new_Return(), + ::MIR::RValue::make_Use( ::MIR::LValue::new_Deref( ::MIR::LValue::new_Argument(0) ) ) })); bb.terminator = ::MIR::Terminator::make_Return({}); mir_fcn.blocks.push_back(::std::move( bb )); @@ -121,13 +121,13 @@ void Trans_AutoImpl_Clone(State& state, ::HIR::TypeRef ty) { ::HIR::TypeRef tmp; const auto& ty_m = monomorphise_type_needed(fld.ent) ? (tmp = p.monomorph(state.resolve, fld.ent)) : fld.ent; - auto fld_lvalue = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({ 0 })) })), static_cast<unsigned>(values.size()) }); + auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) ); values.push_back( clone_field(state, sp, mir_fcn, ty_m, mv$(fld_lvalue)) ); } // Construct the result value ::MIR::BasicBlock bb; bb.statements.push_back(::MIR::Statement::make_Assign({ - ::MIR::LValue::make_Return({}), + ::MIR::LValue::new_Return(), ::MIR::RValue::make_Struct({ gp.clone(), mv$(values) }) })); bb.terminator = ::MIR::Terminator::make_Return({}); @@ -142,13 +142,13 @@ void Trans_AutoImpl_Clone(State& state, ::HIR::TypeRef ty) ::std::vector< ::MIR::Param> values; values.reserve(te.size_val); for(size_t i = 0; i < te.size_val; i ++) { - auto fld_lvalue = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({ 0 })) })), static_cast<unsigned>(i) }); + auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) ); values.push_back( clone_field(state, sp, mir_fcn, *te.inner, mv$(fld_lvalue)) ); } // Construct the result ::MIR::BasicBlock bb; bb.statements.push_back(::MIR::Statement::make_Assign({ - ::MIR::LValue::make_Return({}), + ::MIR::LValue::new_Return(), ::MIR::RValue::make_Array({ mv$(values) }) })); bb.terminator = ::MIR::Terminator::make_Return({}); @@ -161,14 +161,14 @@ void Trans_AutoImpl_Clone(State& state, ::HIR::TypeRef ty) // For each field of the tuple, create a clone (either using Copy if posible, or calling Clone::clone) for(const auto& subty : te) { - auto fld_lvalue = ::MIR::LValue::make_Field({ box$(::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({ 0 })) })), static_cast<unsigned>(values.size()) }); + auto fld_lvalue = ::MIR::LValue::new_Field( ::MIR::LValue::new_Deref(::MIR::LValue::new_Argument(0)), static_cast<unsigned>(values.size()) ); values.push_back( clone_field(state, sp, mir_fcn, subty, mv$(fld_lvalue)) ); } // Construct the result tuple ::MIR::BasicBlock bb; bb.statements.push_back(::MIR::Statement::make_Assign({ - ::MIR::LValue::make_Return({}), + ::MIR::LValue::new_Return(), ::MIR::RValue::make_Tuple({ mv$(values) }) })); bb.terminator = ::MIR::Terminator::make_Return({}); diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 09b63f44..bd447191 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -945,7 +945,7 @@ namespace { { auto inner_ptr = ::HIR::TypeRef::new_pointer( ::HIR::BorrowType::Unique, inner_type.clone() ); m_of << indent; emit_ctype(inner_ptr, FMT_CB(ss, ss << "i"; )); m_of << " = "; emit_lvalue(slot); m_of << "._0._0._0;\n"; - emit_destructor_call( ::MIR::LValue::make_Local(~0u), inner_type, true, indent_level ); + emit_destructor_call( ::MIR::LValue::new_Local(::MIR::LValue::Storage::MAX_ARG), inner_type, true, indent_level ); } // TODO: This is specific to the official liballoc's owned_box ::HIR::GenericPath box_free { m_crate.get_lang_item_path(sp, "box_free"), { inner_type.clone() } }; @@ -976,7 +976,7 @@ namespace { m_mir_res = &mir_res; m_of << "static void " << Trans_Mangle(drop_glue_path) << "(struct s_" << Trans_Mangle(p) << "* rv) {\n"; - emit_box_drop(1, *ity, ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }), /*run_destructor=*/true); + emit_box_drop(1, *ity, ::MIR::LValue::new_Deref(::MIR::LValue::new_Return()), /*run_destructor=*/true); m_of << "}\n"; m_mir_res = nullptr; @@ -1122,13 +1122,13 @@ namespace { m_of << "static void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(ty); m_of << "* rv) {\n"; if( m_resolve.type_needs_drop_glue(sp, ty) ) { - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); - auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); + auto self = ::MIR::LValue::new_Deref(::MIR::LValue::new_Return()); + auto fld_lv = ::MIR::LValue::new_Field(mv$(self), 0); for(const auto& ity : te) { // TODO: What if it's a ZST? emit_destructor_call(fld_lv, ity, /*unsized_valid=*/false, 1); - fld_lv.as_Field().field_index ++; + fld_lv.inc_Field(); } } m_of << "}\n"; @@ -1329,13 +1329,12 @@ namespace { m_of << "\t" << Trans_Mangle( ::HIR::Path(struct_ty.clone(), m_resolve.m_lang_Drop, "drop") ) << "(rv);\n"; } - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); - auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); + auto self = ::MIR::LValue::new_Deref(::MIR::LValue::new_Return()); + auto fld_lv = ::MIR::LValue::new_Field(mv$(self), 0); for(size_t i = 0; i < repr->fields.size(); i++) { - fld_lv.as_Field().field_index = i; - emit_destructor_call(fld_lv, repr->fields[i].ty, /*unsized_valid=*/true, /*indent=*/1); + fld_lv.inc_Field(); } } m_of << "}\n"; @@ -1573,14 +1572,14 @@ namespace { { m_of << "\t" << Trans_Mangle(drop_impl_path) << "(rv);\n"; } - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); + auto self = ::MIR::LValue::new_Deref(::MIR::LValue::new_Return()); if( const auto* e = repr->variants.opt_NonZero() ) { unsigned idx = 1 - e->zero_variant; // TODO: Fat pointers? m_of << "\tif( (*rv)"; emit_enum_path(repr, e->field); m_of << " != 0 ) {\n"; - emit_destructor_call( ::MIR::LValue::make_Downcast({ box$(self), idx }), repr->fields[idx].ty, false, 2 ); + emit_destructor_call( ::MIR::LValue::new_Downcast(mv$(self), idx), repr->fields[idx].ty, false, 2 ); m_of << "\t}\n"; } else if( repr->fields.size() <= 1 ) @@ -1590,15 +1589,15 @@ namespace { } else if( const auto* e = repr->variants.opt_Values() ) { - auto var_lv =::MIR::LValue::make_Downcast({ box$(self), 0 }); + auto var_lv =::MIR::LValue::new_Downcast(mv$(self), 0); m_of << "\tswitch(rv->TAG) {\n"; for(unsigned int var_idx = 0; var_idx < e->values.size(); var_idx ++) { - var_lv.as_Downcast().variant_index = var_idx; m_of << "\tcase " << e->values[var_idx] << ":\n"; emit_destructor_call(var_lv, repr->fields[var_idx].ty, /*unsized_valid=*/false, /*indent=*/2); m_of << "\t\tbreak;\n"; + var_lv.inc_Downcast(); } m_of << "\t}\n"; } @@ -2874,8 +2873,8 @@ namespace { break; } - TU_MATCHA( (e.src), (ve), - (Use, + TU_MATCH_HDRA( (e.src), {) + TU_ARMA(Use, ve) { ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, ve); if( ty == ::HIR::TypeRef::new_diverge() ) { @@ -2892,13 +2891,13 @@ namespace { emit_lvalue(e.dst); m_of << " = "; emit_lvalue(ve); - ), - (Constant, + } + TU_ARMA(Constant, ve) { emit_lvalue(e.dst); m_of << " = "; emit_constant(ve, &e.dst); - ), - (SizedArray, + } + TU_ARMA(SizedArray, ve) { if( ve.count == 0 ) { } else if( ve.count == 1 ) { @@ -2917,34 +2916,35 @@ namespace { m_of << "for(unsigned int i = 0; i < " << ve.count << "; i ++)\n"; m_of << indent << "\t"; emit_lvalue(e.dst); m_of << ".DATA[i] = "; emit_param(ve.val); } - ), - (Borrow, + } + TU_ARMA(Borrow, ve) { ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); bool special = false; // If the inner value was a deref, just copy the pointer verbatim - TU_IFLET(::MIR::LValue, ve.val, Deref, le, + if( ve.val.is_Deref() ) + { emit_lvalue(e.dst); m_of << " = "; - emit_lvalue(*le.val); + emit_lvalue( ::MIR::LValue::CRef(ve.val).inner_ref() ); special = true; - ) + } // Magic for taking a &-ptr to unsized field of a struct. // - Needs to get metadata from bottom-level pointer. - else TU_IFLET(::MIR::LValue, ve.val, Field, le, + else if( ve.val.is_Field() ) { if( metadata_type(ty) != MetadataType::None ) { - const ::MIR::LValue* base_val = &*le.val; - while(base_val->is_Field()) - base_val = &*base_val->as_Field().val; - MIR_ASSERT(mir_res, base_val->is_Deref(), "DST access must be via a deref"); - const ::MIR::LValue& base_ptr = *base_val->as_Deref().val; + auto base_val = ::MIR::LValue::CRef(ve.val).inner_ref(); + while(base_val.is_Field()) + base_val.try_unwrap(); + MIR_ASSERT(mir_res, base_val.is_Deref(), "DST access must be via a deref"); + const auto base_ptr = base_val.inner_ref(); // Construct the new DST emit_lvalue(e.dst); m_of << ".META = "; emit_lvalue(base_ptr); m_of << ".META;\n" << indent; emit_lvalue(e.dst); m_of << ".PTR = &"; emit_lvalue(ve.val); special = true; } - ) + } else { } @@ -2953,50 +2953,55 @@ namespace { if( !special && m_options.disallow_empty_structs && ve.val.is_Field() && this->type_is_bad_zst(ty) ) { // Work backwards to the first non-ZST field - const auto* val_fp = &ve.val.as_Field(); - while( val_fp->val->is_Field() ) + auto val_fp = ::MIR::LValue::CRef(ve.val); + assert(val_fp.is_Field()); + while( val_fp.inner_ref().is_Field() ) { ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, *val_fp->val); + const auto& ty = mir_res.get_lvalue_type(tmp, val_fp.inner_ref()); if( !this->type_is_bad_zst(ty) ) break; + val_fp.try_unwrap(); } + assert(val_fp.is_Field()); // Here, we have `val_fp` be a LValue::Field that refers to a ZST, but the inner of the field points to a non-ZST or a local emit_lvalue(e.dst); m_of << " = "; // If the index is zero, then the best option is to borrow the source - if( val_fp->val->is_Downcast() ) + auto field_inner = val_fp.inner_ref(); + if( field_inner.is_Downcast() ) { - m_of << "(void*)& "; emit_lvalue(*val_fp->val->as_Downcast().val); + m_of << "(void*)& "; emit_lvalue(field_inner.inner_ref()); } - else if( val_fp->field_index == 0 ) + else if( val_fp.as_Field() == 0 ) { - m_of << "(void*)& "; emit_lvalue(*val_fp->val); + m_of << "(void*)& "; emit_lvalue(field_inner); } else { ::HIR::TypeRef tmp; - auto tmp_lv = ::MIR::LValue::make_Field({ box$(val_fp->val->clone()), val_fp->field_index - 1 }); + auto tmp_lv = ::MIR::LValue::new_Field( field_inner.clone(), val_fp.as_Field() - 1 ); bool use_parent = false; for(;;) { const auto& ty = mir_res.get_lvalue_type(tmp, tmp_lv); if( !this->type_is_bad_zst(ty) ) break; - if( tmp_lv.as_Field().field_index == 0 ) + auto idx = tmp_lv.as_Field(); + if( idx == 0 ) { use_parent = true; break; } - tmp_lv.as_Field().field_index -= 1; + tmp_lv.m_wrappers.back() = ::MIR::LValue::Wrapper::new_Field(idx - 1); } // Reached index zero, with still ZST if( use_parent ) { - m_of << "(void*)& "; emit_lvalue(*val_fp->val); + m_of << "(void*)& "; emit_lvalue(field_inner); } // Use the address after the previous item else @@ -3013,11 +3018,11 @@ namespace { m_of << " = "; m_of << "& "; emit_lvalue(ve.val); } - ), - (Cast, + } + TU_ARMA(Cast, ve) { emit_rvalue_cast(mir_res, e.dst, ve); - ), - (BinOp, + } + TU_ARMA(BinOp, ve) { emit_lvalue(e.dst); m_of << " = "; ::HIR::TypeRef tmp, tmp_r; @@ -3168,8 +3173,8 @@ namespace { { m_of << ".lo"; } - ), - (UniOp, + } + TU_ARMA(UniOp, ve) { ::HIR::TypeRef tmp; const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); @@ -3204,24 +3209,24 @@ namespace { break; } emit_lvalue(ve.val); - ), - (DstMeta, + } + TU_ARMA(DstMeta, ve) { emit_lvalue(e.dst); m_of << " = "; emit_lvalue(ve.val); m_of << ".META"; - ), - (DstPtr, + } + TU_ARMA(DstPtr, ve) { emit_lvalue(e.dst); m_of << " = "; emit_lvalue(ve.val); m_of << ".PTR"; - ), - (MakeDst, + } + TU_ARMA(MakeDst, ve) { emit_lvalue(e.dst); m_of << ".PTR = "; emit_param(ve.ptr_val); m_of << ";\n" << indent; emit_lvalue(e.dst); m_of << ".META = "; emit_param(ve.meta_val); - ), - (Tuple, + } + TU_ARMA(Tuple, ve) { bool has_emitted = false; for(unsigned int j = 0; j < ve.vals.size(); j ++) { @@ -3245,15 +3250,15 @@ namespace { m_of << "._" << j << " = "; emit_param(ve.vals[j]); } - ), - (Array, + } + TU_ARMA(Array, ve) { for(unsigned int j = 0; j < ve.vals.size(); j ++) { if( j != 0 ) m_of << ";\n" << indent; emit_lvalue(e.dst); m_of << ".DATA[" << j << "] = "; emit_param(ve.vals[j]); } - ), - (Variant, + } + TU_ARMA(Variant, ve) { const auto& tyi = m_crate.get_typeitem_by_path(sp, ve.path.m_path); if( tyi.is_Union() ) { @@ -3310,8 +3315,8 @@ namespace { { BUG(mir_res.sp, "Unexpected type in Variant"); } - ), - (Struct, + } + TU_ARMA(Struct, ve) { if( ve.vals.empty() ) { if( m_options.disallow_empty_structs ) @@ -3349,8 +3354,8 @@ namespace { emit_param(ve.vals[j]); } } - ) - ) + } + } m_of << ";"; m_of << "\t// " << e.dst << " = " << e.src; m_of << "\n"; @@ -4432,7 +4437,7 @@ namespace { // Nothing needs to be done, this just stops the destructor from running. } else if( name == "drop_in_place" ) { - emit_destructor_call( ::MIR::LValue::make_Deref({ box$(e.args.at(0).as_LValue().clone()) }), params.m_types.at(0), true, 1 /* TODO: get from caller */ ); + emit_destructor_call( ::MIR::LValue::new_Deref(e.args.at(0).as_LValue().clone()), params.m_types.at(0), true, /*indent_level=*/1 /* TODO: get from caller */ ); } else if( name == "needs_drop" ) { // Returns `true` if the actual type given as `T` requires drop glue; @@ -5186,7 +5191,7 @@ namespace { if( te.type == ::HIR::BorrowType::Owned ) { // Call drop glue on inner. - emit_destructor_call( ::MIR::LValue::make_Deref({ box$(slot.clone()) }), *te.inner, true, indent_level ); + emit_destructor_call( ::MIR::LValue::new_Deref(slot.clone()), *te.inner, true, indent_level ); } ), (Path, @@ -5201,10 +5206,7 @@ namespace { if( this->type_is_bad_zst(ty) && (slot.is_Field() || slot.is_Downcast()) ) { m_of << indent << Trans_Mangle(p) << "((void*)&"; - if( slot.is_Field() ) - emit_lvalue(*slot.as_Field().val); - else - emit_lvalue(*slot.as_Downcast().val); + emit_lvalue(::MIR::LValue::CRef(slot).inner_ref()); m_of << ");\n"; } else @@ -5219,7 +5221,7 @@ namespace { m_of << indent << Trans_Mangle(p) << "( " << make_fcn << "("; if( slot.is_Deref() ) { - emit_lvalue(*slot.as_Deref().val); + emit_lvalue( ::MIR::LValue::CRef(slot).inner_ref() ); m_of << ".PTR"; } else @@ -5227,10 +5229,10 @@ namespace { m_of << "&"; emit_lvalue(slot); } m_of << ", "; - const auto* lvp = &slot; - while(const auto* le = lvp->opt_Field()) lvp = &*le->val; - MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); - emit_lvalue(*lvp->as_Deref().val); m_of << ".META"; + auto lvr = ::MIR::LValue::CRef(slot); + while(lvr.is_Field()) lvr.try_unwrap(); + MIR_ASSERT(*m_mir_res, lvr.is_Deref(), "Access to unized type without a deref - " << lvr << " (part of " << slot << ")"); + emit_lvalue(lvr.inner_ref()); m_of << ".META"; m_of << ") );\n"; break; } @@ -5240,7 +5242,7 @@ namespace { if( te.size_val > 0 ) { m_of << indent << "for(unsigned i = 0; i < " << te.size_val << "; i++) {\n"; - emit_destructor_call(::MIR::LValue::make_Index({ box$(slot.clone()), box$(::MIR::LValue::make_Local(~0u)) }), *te.inner, false, indent_level+1); + emit_destructor_call(::MIR::LValue::new_Index(slot.clone(), ::MIR::LValue::Storage::MAX_ARG), *te.inner, false, indent_level+1); m_of << "\n" << indent << "}"; } ), @@ -5248,24 +5250,24 @@ namespace { // Emit destructors for all entries if( te.size() > 0 ) { - ::MIR::LValue lv = ::MIR::LValue::make_Field({ box$(slot.clone()), 0 }); + ::MIR::LValue lv = ::MIR::LValue::new_Field(slot.clone(), 0); for(unsigned int i = 0; i < te.size(); i ++) { - lv.as_Field().field_index = i; emit_destructor_call(lv, te[i], unsized_valid && (i == te.size()-1), indent_level); + lv.inc_Field(); } } ), (TraitObject, MIR_ASSERT(*m_mir_res, unsized_valid, "Dropping TraitObject without a pointer"); // Call destructor in vtable - const auto* lvp = &slot; - while(const auto* le = lvp->opt_Field()) lvp = &*le->val; - MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); - m_of << indent << "((VTABLE_HDR*)"; emit_lvalue(*lvp->as_Deref().val); m_of << ".META)->drop("; - if( const auto* ve = slot.opt_Deref() ) + auto lvr = ::MIR::LValue::CRef(slot); + while(lvr.is_Field()) lvr.try_unwrap(); + MIR_ASSERT(*m_mir_res, lvr.is_Deref(), "Access to unized type without a deref - " << lvr << " (part of " << slot << ")"); + m_of << indent << "((VTABLE_HDR*)"; emit_lvalue(lvr.inner_ref()); m_of << ".META)->drop("; + if( slot.is_Deref() ) { - emit_lvalue(*ve->val); m_of << ".PTR"; + emit_lvalue(::MIR::LValue::CRef(slot).inner_ref()); m_of << ".PTR"; } else { @@ -5275,12 +5277,12 @@ namespace { ), (Slice, MIR_ASSERT(*m_mir_res, unsized_valid, "Dropping Slice without a pointer"); - const auto* lvp = &slot; - while(const auto* le = lvp->opt_Field()) lvp = &*le->val; - MIR_ASSERT(*m_mir_res, lvp->is_Deref(), "Access to unized type without a deref - " << *lvp << " (part of " << slot << ")"); + auto lvr = ::MIR::LValue::CRef(slot); + while(lvr.is_Field()) lvr.try_unwrap(); + MIR_ASSERT(*m_mir_res, lvr.is_Deref(), "Access to unized type without a deref - " << lvr << " (part of " << slot << ")"); // Call destructor on all entries - m_of << indent << "for(unsigned i = 0; i < "; emit_lvalue(*lvp->as_Deref().val); m_of << ".META; i++) {\n"; - emit_destructor_call(::MIR::LValue::make_Index({ box$(slot.clone()), box$(::MIR::LValue::make_Local(~0u)) }), *te.inner, false, indent_level+1); + m_of << indent << "for(unsigned i = 0; i < "; emit_lvalue(lvr.inner_ref()); m_of << ".META; i++) {\n"; + emit_destructor_call(::MIR::LValue::new_Index(slot.clone(), ::MIR::LValue::Storage::MAX_ARG), *te.inner, false, indent_level+1); m_of << "\n" << indent << "}"; ) ) @@ -5525,116 +5527,125 @@ namespace { ) } - void emit_lvalue(const ::MIR::LValue& val) { - TU_MATCHA( (val), (e), - (Return, + void emit_lvalue(const ::MIR::LValue::CRef& val) + { + TU_MATCH_HDRA( (val), {) + TU_ARMA(Return, _e) { m_of << "rv"; - ), - (Argument, - m_of << "arg" << e.idx; - ), - (Local, - if( e == ~0u ) + } + TU_ARMA(Argument, e) { + m_of << "arg" << e; + } + TU_ARMA(Local, e) { + if( e == ::MIR::LValue::Storage::MAX_ARG ) m_of << "i"; else m_of << "var" << e; - ), - (Static, - m_of << Trans_Mangle(*e); - ), - (Field, + } + TU_ARMA(Static, e) { + m_of << Trans_Mangle(e); + } + TU_ARMA(Field, field_index) { ::HIR::TypeRef tmp; - const auto& ty = m_mir_res->get_lvalue_type(tmp, *e.val); - if( ty.m_data.is_Slice() ) { - if( e.val->is_Deref() ) + auto inner = val.inner_ref(); + const auto& ty = m_mir_res->get_lvalue_type(tmp, inner); + if( ty.m_data.is_Slice() ) + { + if( inner.is_Deref() ) { m_of << "(("; emit_ctype(*ty.m_data.as_Slice().inner); m_of << "*)"; - emit_lvalue(*e.val->as_Deref().val); + emit_lvalue(inner.inner_ref()); m_of << ".PTR)"; } else { - emit_lvalue(*e.val); + emit_lvalue(inner); } - m_of << "[" << e.field_index << "]"; + m_of << "[" << field_index << "]"; } else if( ty.m_data.is_Array() ) { - emit_lvalue(*e.val); - m_of << ".DATA[" << e.field_index << "]"; + emit_lvalue(inner); + m_of << ".DATA[" << field_index << "]"; } - else if( e.val->is_Deref() ) { + else if( inner.is_Deref() ) { auto dst_type = metadata_type(ty); if( dst_type != MetadataType::None ) { - m_of << "(("; emit_ctype(ty); m_of << "*)"; emit_lvalue(*e.val->as_Deref().val); m_of << ".PTR)->_" << e.field_index; + m_of << "(("; emit_ctype(ty); m_of << "*)"; emit_lvalue(inner.inner_ref()); m_of << ".PTR)->_" << field_index; } else { - emit_lvalue(*e.val->as_Deref().val); - m_of << "->_" << e.field_index; + emit_lvalue(inner.inner_ref()); + m_of << "->_" << field_index; } } else { - emit_lvalue(*e.val); - m_of << "._" << e.field_index; + emit_lvalue(inner); + m_of << "._" << field_index; } - ), - (Deref, - // TODO: If the type is unsized, then this pointer is a fat pointer, so we need to cast the data pointer. + } + TU_ARMA(Deref, _e) { + auto inner = val.inner_ref(); ::HIR::TypeRef tmp; const auto& ty = m_mir_res->get_lvalue_type(tmp, val); auto dst_type = metadata_type(ty); + // If the type is unsized, then this pointer is a fat pointer, so we need to cast the data pointer. if( dst_type != MetadataType::None ) { m_of << "(*("; emit_ctype(ty); m_of << "*)"; - emit_lvalue(*e.val); + emit_lvalue(inner); m_of << ".PTR)"; } else { m_of << "(*"; - emit_lvalue(*e.val); + emit_lvalue(inner); m_of << ")"; } - ), - (Index, + } + TU_ARMA(Index, index_local) { + auto inner = val.inner_ref(); ::HIR::TypeRef tmp; - const auto& ty = m_mir_res->get_lvalue_type(tmp, *e.val); + const auto& ty = m_mir_res->get_lvalue_type(tmp, inner); m_of << "("; if( ty.m_data.is_Slice() ) { - if( e.val->is_Deref() ) + if( inner.is_Deref() ) { m_of << "("; emit_ctype(*ty.m_data.as_Slice().inner); m_of << "*)"; - emit_lvalue(*e.val->as_Deref().val); + emit_lvalue(inner.inner_ref()); m_of << ".PTR"; } else { - emit_lvalue(*e.val); + emit_lvalue(inner); } } else if( ty.m_data.is_Array() ) { - emit_lvalue(*e.val); + emit_lvalue(inner); m_of << ".DATA"; } else { - emit_lvalue(*e.val); + emit_lvalue(inner); } m_of << ")["; - emit_lvalue(*e.idx); + emit_lvalue(::MIR::LValue::new_Local(index_local)); m_of << "]"; - ), - (Downcast, + } + TU_ARMA(Downcast, variant_index) { + auto inner = val.inner_ref(); ::HIR::TypeRef tmp; - const auto& ty = m_mir_res->get_lvalue_type(tmp, *e.val); - emit_lvalue(*e.val); + const auto& ty = m_mir_res->get_lvalue_type(tmp, inner); + emit_lvalue(inner); MIR_ASSERT(*m_mir_res, ty.m_data.is_Path(), "Downcast on non-Path type - " << ty); if( ty.m_data.as_Path().binding.is_Enum() ) { m_of << ".DATA"; } - m_of << ".var_" << e.variant_index; - ) - ) + m_of << ".var_" << variant_index; + } + } + } + void emit_lvalue(const ::MIR::LValue& val) { + emit_lvalue( ::MIR::LValue::CRef(val) ); } void emit_constant(const ::MIR::Constant& ve, const ::MIR::LValue* dst_ptr=nullptr) { diff --git a/src/trans/codegen_mmir.cpp b/src/trans/codegen_mmir.cpp index 50d36a8a..23f333fd 100644 --- a/src/trans/codegen_mmir.cpp +++ b/src/trans/codegen_mmir.cpp @@ -31,47 +31,44 @@ namespace ::std::ostream& operator<<(::std::ostream& os, const Fmt<::MIR::LValue>& x) { - auto fmt_lhs = [](::std::ostream& os, const ::MIR::LValue& lv) { - if( lv.is_Deref() ) { - os << "(" << fmt(lv) << ")"; - } - else { - os << fmt(lv); - } - }; - switch(x.e.tag()) - { - case ::MIR::LValue::TAGDEAD: throw ""; - TU_ARM(x.e, Return, _e) (void)_e; + TU_MATCHA( (x.e.m_root), (e), + (Return, os << "RETURN"; - break; - TU_ARM(x.e, Local, e) + ), + (Local, os << "var" << e; - break; - TU_ARM(x.e, Argument, e) - os << "arg" << e.idx; - break; - TU_ARM(x.e, Static, e) - os << *e; - break; - TU_ARM(x.e, Deref, e) - os << "*" << fmt(*e.val); - break; - TU_ARM(x.e, Field, e) { - fmt_lhs(os, *e.val); - // Avoid `0.` existing in the output - if( e.val->is_Field() || e.val->is_Downcast() ) - os << " "; - os << "." << e.field_index; - } break; - TU_ARM(x.e, Index, e) { - fmt_lhs(os, *e.val); - os << "[" << fmt(*e.idx) << "]"; - } break; - TU_ARM(x.e, Downcast, e) { - fmt_lhs(os, *e.val); - os << "@" << e.variant_index; - } break; + ), + (Argument, + os << "arg" << e; + ), + (Static, + os << e; + ) + ) + bool was_num = false; + for(const auto& w : x.e.m_wrappers) + { + bool prev_was_num = was_num; was_num = false; + switch(w.tag()) + { + TU_ARM(w, Deref, e) + os << "*"; + break; + TU_ARM(w, Field, field_index) { + // Add a space to prevent accidental float literals + if( prev_was_num ) + os << " "; + os << "." << field_index; + was_num = true; + } break; + TU_ARM(w, Index, e) { + os << "[var" << fmt(::MIR::LValue::new_Local(e)) << "]"; + } break; + TU_ARM(w, Downcast, variant_index) { + os << "@" << variant_index; + was_num = true; + } break; + } } return os; } @@ -225,14 +222,14 @@ namespace m_of << "\t0: {\n"; - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({0})) }); - auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); + auto self = ::MIR::LValue::new_Deref( ::MIR::LValue::new_Argument(0) ); + auto fld_lv = ::MIR::LValue::new_Field(mv$(self), 0); for(const auto& e : repr->fields) { if( m_resolve.type_needs_drop_glue(sp, e.ty) ) { m_of << "\t\t""DROP " << fmt(fld_lv) << ";\n"; } - fld_lv.as_Field().field_index += 1; + fld_lv.inc_Field(); } m_of << "\t\t""RETURN\n"; m_of << "\t}\n"; @@ -388,13 +385,13 @@ namespace //ASSERT_BUG(sp, !item.m_markings.has_drop_impl, "Box shouldn't have a Drop impl"); // TODO: This is very specific to the structure of the official liballoc's Box. - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({0})) }); - auto fld_p_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); - fld_p_lv = ::MIR::LValue::make_Field({ box$(fld_p_lv), 0 }); - fld_p_lv = ::MIR::LValue::make_Field({ box$(fld_p_lv), 0 }); + auto self = ::MIR::LValue::new_Deref( ::MIR::LValue::new_Argument(0) ); + auto fld_p_lv = ::MIR::LValue::new_Field( mv$(self), 0 ); + fld_p_lv = ::MIR::LValue::new_Field( mv$(fld_p_lv), 0 ); + fld_p_lv = ::MIR::LValue::new_Field( mv$(fld_p_lv), 0 ); if( m_resolve.type_needs_drop_glue(sp, *ity) ) { - auto fld_lv = ::MIR::LValue::make_Deref({ box$(fld_p_lv.clone()) }); + auto fld_lv = ::MIR::LValue::new_Deref( fld_p_lv.clone() ); m_of << "\t\t""DROP " << fmt(fld_lv) << ";\n"; } @@ -424,14 +421,14 @@ namespace m_of << "\t2: {\n"; } - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({0})) }); - auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); + auto self = ::MIR::LValue::new_Deref( ::MIR::LValue::new_Argument(0) ); + auto fld_lv = ::MIR::LValue::new_Field( mv$(self), 0 ); for(const auto& e : repr->fields) { if( m_resolve.type_needs_drop_glue(sp, e.ty) ) { m_of << "\t\t""DROP " << fmt(fld_lv) << ";\n"; } - fld_lv.as_Field().field_index += 1; + fld_lv.inc_Field(); } } m_of << "\t\t""RETURN\n"; @@ -606,18 +603,18 @@ namespace { m_of << "\t" << Trans_Mangle(drop_impl_path) << "(rv);\n"; } - auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); + auto self = ::MIR::LValue::new_Deref( ::MIR::LValue::new_Return() ); if( nonzero_path.size() > 0 ) { // TODO: Fat pointers? m_of << "\tif( (*rv)._1"; emit_nonzero_path(nonzero_path); m_of << " ) {\n"; - emit_destructor_call( ::MIR::LValue::make_Field({ box$(self), 1 }), monomorph(item.m_data.as_Data()[1].type), false, 2 ); + emit_destructor_call( ::MIR::LValue::new_Field( box$(self), 1 ), monomorph(item.m_data.as_Data()[1].type), false, 2 ); m_of << "\t}\n"; } else if( const auto* e = item.m_data.opt_Data() ) { - auto var_lv =::MIR::LValue::make_Downcast({ box$(self), 0 }); + auto var_lv =::MIR::LValue::new_Downcast( mv$(self), 0 ); m_of << "\tswitch(rv->TAG) {\n"; for(unsigned int var_idx = 0; var_idx < e->size(); var_idx ++) diff --git a/src/trans/enumerate.cpp b/src/trans/enumerate.cpp index a2ecb408..dfc566c0 100644 --- a/src/trans/enumerate.cpp +++ b/src/trans/enumerate.cpp @@ -11,6 +11,7 @@ #include "trans_list.hpp" #include <hir/hir.hpp> #include <mir/mir.hpp> +#include <mir/helpers.hpp> #include <hir_typeck/common.hpp> // monomorph #include <hir_typeck/static.hpp> // StaticTraitResolve #include <hir/item_path.hpp> @@ -659,8 +660,9 @@ void Trans_Enumerate_Types(EnumState& state) // Visit all functions that haven't been type-visited yet for(unsigned int i = 0; i < state.fcns_to_type_visit.size(); i++) { - auto p = state.fcns_to_type_visit[i]; - TRACE_FUNCTION_F("Function " << ::std::find_if(state.rv.m_functions.begin(), state.rv.m_functions.end(), [&](const auto&x){ return x.second.get() == p; })->first); + auto* p = state.fcns_to_type_visit[i]; + auto& fcn_path = ::std::find_if(state.rv.m_functions.begin(), state.rv.m_functions.end(), [&](const auto&x){ return x.second.get() == p; })->first; + TRACE_FUNCTION_F("Function " << fcn_path); assert(p->ptr); const auto& fcn = *p->ptr; const auto& pp = p->pp; @@ -701,290 +703,170 @@ void Trans_Enumerate_Types(EnumState& state) for(const auto& ty : mir.locals) tv.visit_type(monomorph(ty)); - // TODO: Find all LValue::Deref instances and get the result type + // Find all LValue::Deref instances and get the result type + ::MIR::TypeResolve::args_t empty_args; + ::HIR::TypeRef empty_ty; + ::MIR::TypeResolve mir_res(sp, tv.m_resolve, FMT_CB(fcn_path), /*ret_ty=*/empty_ty, empty_args, mir); for(const auto& block : mir.blocks) { - struct H { - static const ::HIR::TypeRef& visit_lvalue(TypeVisitor& tv, const Trans_Params& pp, const ::HIR::Function& fcn, const ::MIR::LValue& lv, ::HIR::TypeRef* tmp_ty_ptr = nullptr) { - static ::HIR::TypeRef blank; - TRACE_FUNCTION_F(lv << (tmp_ty_ptr ? " [type]" : "")); + struct MirVisitor + //:public ::MIR::Visitor + { + TypeVisitor& tv; + const Trans_Params& pp; + const ::HIR::Function& fcn; + const ::MIR::TypeResolve& mir_res; + + MirVisitor(TypeVisitor& tv, const Trans_Params& pp, const ::HIR::Function& fcn, const ::MIR::TypeResolve& mir_res) + :tv(tv) + ,pp(pp) + ,fcn(fcn) + ,mir_res(mir_res) + { + } + + void visit_lvalue(const ::MIR::LValue& lv) //override + { + TRACE_FUNCTION_F(lv); + if( ::std::none_of(lv.m_wrappers.begin(), lv.m_wrappers.end(), [](const auto& w){ return w.is_Deref(); }) ) + { + return ; + } + ::HIR::TypeRef tmp; auto monomorph_outer = [&](const auto& tpl)->const auto& { - assert(tmp_ty_ptr); if( monomorphise_type_needed(tpl) ) { - return *tmp_ty_ptr = pp.monomorph(tv.m_resolve, tpl); + return tmp = pp.monomorph(tv.m_resolve, tpl); } else { return tpl; } }; + const ::HIR::TypeRef* ty_p = nullptr;; // Recurse, if Deref get the type and add it to the visitor - TU_MATCHA( (lv), (e), + TU_MATCHA( (lv.m_root), (e), (Return, - if( tmp_ty_ptr ) { - TODO(Span(), "Get return type for MIR type enumeration"); - } + MIR_TODO(mir_res, "Get return type for MIR type enumeration"); ), (Argument, - if( tmp_ty_ptr ) { - return monomorph_outer(fcn.m_args[e.idx].second); - } + ty_p = &monomorph_outer(fcn.m_args[e].second); ), (Local, - if( tmp_ty_ptr ) { - return monomorph_outer(fcn.m_code.m_mir->locals[e]); - } + ty_p = &monomorph_outer(fcn.m_code.m_mir->locals[e]); ), (Static, - if( tmp_ty_ptr ) { - const auto& path = *e; - TU_MATCHA( (path.m_data), (pe), - (Generic, - ASSERT_BUG(Span(), pe.m_params.m_types.empty(), "Path params on static - " << path); - const auto& s = tv.m_resolve.m_crate.get_static_by_path(Span(), pe.m_path); - return s.m_type; - ), - (UfcsKnown, - TODO(Span(), "LValue::Static - UfcsKnown - " << path); - ), - (UfcsUnknown, - BUG(Span(), "Encountered UfcsUnknown in LValue::Static - " << path); - ), - (UfcsInherent, - TODO(Span(), "LValue::Static - UfcsInherent - " << path); - ) - ) - } - ), - (Field, - const auto& ity = visit_lvalue(tv,pp,fcn, *e.val, tmp_ty_ptr); - if( tmp_ty_ptr ) - { - TU_MATCH_DEF(::HIR::TypeRef::Data, (ity.m_data), (te), - ( - BUG(Span(), "Field access of unexpected type - " << ity); - ), - (Tuple, - return te[e.field_index]; - ), - (Array, - return *te.inner; - ), - (Slice, - return *te.inner; - ), - (Path, - ASSERT_BUG(Span(), te.binding.is_Struct(), "Field on non-Struct - " << ity); - const auto& str = *te.binding.as_Struct(); - auto monomorph = [&](const auto& ty)->const auto& { - if( monomorphise_type_needed(ty) ) { - *tmp_ty_ptr = monomorphise_type(sp, str.m_params, te.path.m_data.as_Generic().m_params, ty); - tv.m_resolve.expand_associated_types(sp, *tmp_ty_ptr); - return *tmp_ty_ptr; - } - else { - return ty; - } - }; - TU_MATCHA( (str.m_data), (se), - (Unit, - BUG(Span(), "Field on unit-like struct - " << ity); - ), - (Tuple, - ASSERT_BUG(Span(), e.field_index < se.size(), "Field index out of range in struct " << te.path); - return monomorph(se.at(e.field_index).ent); - ), - (Named, - ASSERT_BUG(Span(), e.field_index < se.size(), "Field index out of range in struct " << te.path); - return monomorph(se.at(e.field_index).second.ent); - ) - ) - ) - ) - } - ), - (Deref, - ::HIR::TypeRef tmp; - if( !tmp_ty_ptr ) tmp_ty_ptr = &tmp; - - const auto& ity = visit_lvalue(tv,pp,fcn, *e.val, tmp_ty_ptr); - TU_MATCH_DEF(::HIR::TypeRef::Data, (ity.m_data), (te), - ( - BUG(Span(), "Deref of unexpected type - " << ity); + // TODO: Monomorphise the path then hand to MIR::TypeResolve? + const auto& path = e; + TU_MATCHA( (path.m_data), (pe), + (Generic, + MIR_ASSERT(mir_res, pe.m_params.m_types.empty(), "Path params on static - " << path); + const auto& s = tv.m_resolve.m_crate.get_static_by_path(mir_res.sp, pe.m_path); + ty_p = &s.m_type; ), - (Path, - if( const auto* inner_ptr = tv.m_resolve.is_type_owned_box(ity) ) - { - DEBUG("- Add type " << ity); - tv.visit_type(*inner_ptr); - return *inner_ptr; - } - else { - BUG(Span(), "Deref on unexpected type - " << ity); - } + (UfcsKnown, + MIR_TODO(mir_res, "LValue::Static - UfcsKnown - " << path); ), - (Borrow, - DEBUG("- Add type " << ity); - tv.visit_type(*te.inner); - return *te.inner; + (UfcsUnknown, + MIR_BUG(mir_res, "Encountered UfcsUnknown in LValue::Static - " << path); ), - (Pointer, - DEBUG("- Add type " << ity); - tv.visit_type(*te.inner); - return *te.inner; + (UfcsInherent, + MIR_TODO(mir_res, "LValue::Static - UfcsInherent - " << path); ) ) - ), - (Index, - visit_lvalue(tv,pp,fcn, *e.idx, tmp_ty_ptr); - const auto& ity = visit_lvalue(tv,pp,fcn, *e.val, tmp_ty_ptr); - if( tmp_ty_ptr ) - { - TU_MATCH_DEF(::HIR::TypeRef::Data, (ity.m_data), (te), - ( - BUG(Span(), "Index of unexpected type - " << ity); - ), - (Array, - return *te.inner; - ), - (Slice, - return *te.inner; - ) - ) - } - ), - (Downcast, - const auto& ity = visit_lvalue(tv,pp,fcn, *e.val, tmp_ty_ptr); - if( tmp_ty_ptr ) - { - TU_MATCH_DEF( ::HIR::TypeRef::Data, (ity.m_data), (te), - ( - BUG(Span(), "Downcast on unexpected type - " << ity); - ), - (Path, - if( te.binding.is_Enum() ) - { - const auto& enm = *te.binding.as_Enum(); - auto monomorph = [&](const auto& ty)->auto { - ::HIR::TypeRef rv = monomorphise_type(pp.sp, enm.m_params, te.path.m_data.as_Generic().m_params, ty); - tv.m_resolve.expand_associated_types(sp, rv); - return rv; - }; - ASSERT_BUG(Span(), enm.m_data.is_Data(), ""); - const auto& variants = enm.m_data.as_Data(); - ASSERT_BUG(Span(), e.variant_index < variants.size(), "Variant index out of range"); - const auto& raw_ty = variants[e.variant_index].type; - if( monomorphise_type_needed(raw_ty) ) { - return *tmp_ty_ptr = monomorph(raw_ty); - } - else { - return raw_ty; - } - } - else - { - const auto& unm = *te.binding.as_Union(); - ASSERT_BUG(Span(), e.variant_index < unm.m_variants.size(), "Variant index out of range"); - const auto& variant = unm.m_variants[e.variant_index]; - const auto& var_ty = variant.second.ent; - - if( monomorphise_type_needed(var_ty) ) { - *tmp_ty_ptr = monomorphise_type(pp.sp, unm.m_params, te.path.m_data.as_Generic().m_params, variant.second.ent); - tv.m_resolve.expand_associated_types(pp.sp, *tmp_ty_ptr); - return *tmp_ty_ptr; - } - else { - return var_ty; - } - } - ) - ) - } ) ) - return blank; + assert(ty_p); + for(const auto& w : lv.m_wrappers) + { + ty_p = &mir_res.get_unwrapped_type(tmp, w, *ty_p); + if( w.is_Deref() ) + { + tv.visit_type(*ty_p); + } + } } - static void visit_const(TypeVisitor& tv, const Trans_Params& pp, const ::HIR::Function& fcn, const ::MIR::Constant& p) + void visit_const(const ::MIR::Constant& p) { } - static void visit_param(TypeVisitor& tv, const Trans_Params& pp, const ::HIR::Function& fcn, const ::MIR::Param& p) + void visit_param(const ::MIR::Param& p) { TU_MATCHA( (p), (e), (LValue, - H::visit_lvalue(tv, pp, fcn, e); + this->visit_lvalue(e); ), (Constant, - H::visit_const(tv, pp, fcn, e); + this->visit_const(e); ) ) } }; + MirVisitor mir_visit(tv, pp, fcn, mir_res); for(const auto& stmt : block.statements) { TU_MATCHA( (stmt), (se), (Drop, - H::visit_lvalue(tv,pp,fcn, se.slot); + mir_visit.visit_lvalue(se.slot); ), (SetDropFlag, ), (Asm, for(const auto& v : se.outputs) - H::visit_lvalue(tv,pp,fcn, v.second); + mir_visit.visit_lvalue(v.second); for(const auto& v : se.inputs) - H::visit_lvalue(tv,pp,fcn, v.second); + mir_visit.visit_lvalue(v.second); ), (ScopeEnd, ), (Assign, - H::visit_lvalue(tv,pp,fcn, se.dst); + mir_visit.visit_lvalue(se.dst); TU_MATCHA( (se.src), (re), (Use, - H::visit_lvalue(tv,pp,fcn, re); + mir_visit.visit_lvalue(re); ), (Constant, - H::visit_const(tv,pp,fcn, re); + mir_visit.visit_const(re); ), (SizedArray, - H::visit_param(tv,pp,fcn, re.val); + mir_visit.visit_param(re.val); ), (Borrow, - H::visit_lvalue(tv,pp,fcn, re.val); + mir_visit.visit_lvalue(re.val); ), (Cast, - H::visit_lvalue(tv,pp,fcn, re.val); + mir_visit.visit_lvalue(re.val); ), (BinOp, - H::visit_param(tv,pp,fcn, re.val_l); - H::visit_param(tv,pp,fcn, re.val_l); + mir_visit.visit_param(re.val_l); + mir_visit.visit_param(re.val_r); ), (UniOp, - H::visit_lvalue(tv,pp,fcn, re.val); + mir_visit.visit_lvalue(re.val); ), (DstMeta, - H::visit_lvalue(tv,pp,fcn, re.val); + mir_visit.visit_lvalue(re.val); ), (DstPtr, - H::visit_lvalue(tv,pp,fcn, re.val); + mir_visit.visit_lvalue(re.val); ), (MakeDst, - H::visit_param(tv,pp,fcn, re.ptr_val); - H::visit_param(tv,pp,fcn, re.meta_val); + mir_visit.visit_param(re.ptr_val); + mir_visit.visit_param(re.meta_val); ), (Tuple, for(const auto& v : re.vals) - H::visit_param(tv,pp,fcn, v); + mir_visit.visit_param(v); ), (Array, for(const auto& v : re.vals) - H::visit_param(tv,pp,fcn, v); + mir_visit.visit_param(v); ), (Variant, - H::visit_param(tv,pp,fcn, re.val); + mir_visit.visit_param(re.val); ), (Struct, for(const auto& v : re.vals) - H::visit_param(tv,pp,fcn, v); + mir_visit.visit_param(v); ) ) ) @@ -997,25 +879,31 @@ void Trans_Enumerate_Types(EnumState& state) (Goto, ), (Panic, ), (If, - H::visit_lvalue(tv,pp,fcn, te.cond); + mir_visit.visit_lvalue(te.cond); ), (Switch, - H::visit_lvalue(tv,pp,fcn, te.val); + mir_visit.visit_lvalue(te.val); ), (SwitchValue, - H::visit_lvalue(tv,pp,fcn, te.val); + mir_visit.visit_lvalue(te.val); ), (Call, - if( te.fcn.is_Value() ) - H::visit_lvalue(tv,pp,fcn, te.fcn.as_Value()); - else if( te.fcn.is_Intrinsic() ) + if(const auto* e = te.fcn.opt_Value() ) { - for(const auto& ty : te.fcn.as_Intrinsic().params.m_types) + mir_visit.visit_lvalue(*e); + } + else if(const auto* e = te.fcn.opt_Intrinsic()) + { + for(const auto& ty : e->params.m_types) tv.visit_type(monomorph(ty)); } - H::visit_lvalue(tv,pp,fcn, te.ret_val); + else + { + // Paths don't need visiting? + } + mir_visit.visit_lvalue(te.ret_val); for(const auto& arg : te.args) - H::visit_param(tv,pp,fcn, arg); + mir_visit.visit_param(arg); ) ) } @@ -1550,30 +1438,10 @@ void Trans_Enumerate_FillFrom_Path(EnumState& state, const ::HIR::Path& path, co void Trans_Enumerate_FillFrom_MIR_LValue(MIR::EnumCache& state, const ::MIR::LValue& lv) { - TU_MATCHA( (lv), (e), - (Return, - ), - (Argument, - ), - (Local, - ), - (Static, - state.insert_path(*e); - ), - (Field, - Trans_Enumerate_FillFrom_MIR_LValue(state, *e.val); - ), - (Deref, - Trans_Enumerate_FillFrom_MIR_LValue(state, *e.val); - ), - (Index, - Trans_Enumerate_FillFrom_MIR_LValue(state, *e.val); - Trans_Enumerate_FillFrom_MIR_LValue(state, *e.idx); - ), - (Downcast, - Trans_Enumerate_FillFrom_MIR_LValue(state, *e.val); - ) - ) + if( lv.m_root.is_Static() ) + { + state.insert_path(lv.m_root.as_Static()); + } } void Trans_Enumerate_FillFrom_MIR_Constant(MIR::EnumCache& state, const ::MIR::Constant& c) { diff --git a/src/trans/monomorphise.cpp b/src/trans/monomorphise.cpp index 6c331d9c..b5099918 100644 --- a/src/trans/monomorphise.cpp +++ b/src/trans/monomorphise.cpp @@ -15,38 +15,14 @@ namespace { ::MIR::LValue monomorph_LValue(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::LValue& tpl) { - TU_MATCHA( (tpl), (e), - (Return, return e; ), - (Argument, return e; ), - (Local, return e; ), - (Static, - return box$(params.monomorph(resolve, *e)); - ), - (Field, - return ::MIR::LValue::make_Field({ - box$(monomorph_LValue(resolve, params, *e.val)), - e.field_index - }); - ), - (Deref, - return ::MIR::LValue::make_Deref({ - box$(monomorph_LValue(resolve, params, *e.val)) - }); - ), - (Index, - return ::MIR::LValue::make_Index({ - box$(monomorph_LValue(resolve, params, *e.val)), - box$(monomorph_LValue(resolve, params, *e.idx)) - }); - ), - (Downcast, - return ::MIR::LValue::make_Downcast({ - box$(monomorph_LValue(resolve, params, *e.val)), - e.variant_index - }); - ) - ) - throw ""; + if( tpl.m_root.is_Static() ) + { + return ::MIR::LValue( ::MIR::LValue::Storage::new_Static(params.monomorph(resolve, tpl.m_root.as_Static())), tpl.m_wrappers ); + } + else + { + return tpl.clone(); + } } ::MIR::Constant monomorph_Constant(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::Constant& tpl) { |