diff options
author | John Hodge (bugs) <tpg@mutabah.net> | 2017-07-06 17:41:10 +0800 |
---|---|---|
committer | John Hodge (bugs) <tpg@mutabah.net> | 2017-07-06 17:41:10 +0800 |
commit | 48e2c4973d5401c9f7d0ee2bac22ac6015744961 (patch) | |
tree | b93fb21b85343633e2d0c00c42ea7a388bb6f883 | |
parent | ce3d36a90b9efd11c94f9084b5e727d65b6509a7 (diff) | |
parent | c4e88b3c49736e71534c918a83956885c052beb8 (diff) | |
download | mrust-48e2c4973d5401c9f7d0ee2bac22ac6015744961.tar.gz |
Merge branch 'master' of https://github.com/thepowersgang/mrustc
# Conflicts:
# src/trans/codegen_c.cpp
69 files changed, 3390 insertions, 2273 deletions
@@ -49,6 +49,7 @@ CXXFLAGS += -Wno-misleading-indentation RUST_FLAGS := --cfg debug_assertions RUST_FLAGS += -g RUST_FLAGS += -O +RUST_FLAGS += $(RUST_FLAGS_EXTRA) SHELL = bash @@ -110,7 +111,8 @@ OBJ += mir/check.o mir/cleanup.o mir/optimise.o OBJ += mir/check_full.o OBJ += hir/serialise.o hir/deserialise.o hir/serialise_lowlevel.o OBJ += trans/trans_list.o trans/mangling.o -OBJ += trans/enumerate.o trans/monomorphise.o trans/codegen.o trans/codegen_c.o +OBJ += trans/enumerate.o trans/monomorphise.o trans/codegen.o +OBJ += trans/codegen_c.o trans/codegen_c_structured.o OBJ += trans/target.o PCHS := ast/ast.hpp @@ -372,6 +374,8 @@ DISABLED_TESTS += run-pass/out-of-stack # - Requires jemalloc DISABLED_TESTS += run-pass/allocator-default DISABLED_TESTS += run-pass/allocator-override +# - Really odd behavior: +DISABLED_TESTS += run-pass/move-guard-const # Moves are allowed in match arm guards (should error in mrustc) # - Lazy. DISABLED_TESTS += run-pass/associated-types-projection-in-where-clause # Not normalizing bounds DISABLED_TESTS += run-pass/cast # Disallows cast from char to i32 @@ -715,7 +719,7 @@ DISABLED_TESTS += run-pass/issue-13027 # Infinite loop (match?) DISABLED_TESTS += run-pass/issue-36936 # assert_eq failing on equal values # - BUG: signed ctpop/cttz/ctlz DISABLED_TESTS += run-pass/intrinsics-integer # todo - bswap<i8> -# - BUG: Incorrect drop order of ? +# - BUG: Incorrect drop ordering DISABLED_TESTS += run-pass/issue-23338-ensure-param-drop-order # - BUG: Incorrect consteval DISABLED_TESTS += run-pass/issue-23968-const-not-overflow # !0 / 2 incorrect value @@ -750,15 +754,14 @@ DISABLED_TESTS += run-pass/type-sizes DISABLED_TESTS += run-pass/discrim-explicit-23030 DISABLED_TESTS += run-pass/issue-13902 # - BUG: Bad floats -DISABLED_TESTS += run-pass/float-nan -DISABLED_TESTS += run-pass/float_math -DISABLED_TESTS += run-pass/floatlits +DISABLED_TESTS += run-pass/float_math # Missing intrinsic DISABLED_TESTS += run-pass/intrinsics-math +DISABLED_TESTS += run-pass/issue-32805 # Possible f32 literal rounding isue # - BUG: MIR Generation DISABLED_TESTS += run-pass/union/union-drop-assign # No drop when assiging to union field DISABLED_TESTS += run-pass/issue-4734 # Destructor on unused rvalue DISABLED_TESTS += run-pass/issue-8860 # No drop of un-moved arguments -DISABLED_TESTS += run-pass/issue-15080 # Inifinte loop from incorrect match generation +DISABLED_TESTS += run-pass/issue-15080 # Infinte loop from incorrect match generation # - BUG: Codegen DISABLED_TESTS += run-pass/union/union-transmute # Incorrect union behavior, likey backend UB DISABLED_TESTS += run-pass/mir_overflow_off # out-of-range shift behavior diff --git a/Notes/todo.txt b/Notes/todo.txt index 54956718..8146bfd6 100644 --- a/Notes/todo.txt +++ b/Notes/todo.txt @@ -23,3 +23,12 @@ TODO: - This should reduce the amount of code needed for validation, but will be a BIG change. + + +## Optimisations +- Argument propagation: replace assignments from Argument(_) if target is only + written once +- Dead assignment removal (Delete `<val> = Use(<val>)` +- Tuple destructure removal + - Spot `#1 = (...,)`, `#2 = (#1).n` where #1 is Write-once, borrow-none + diff --git a/run_rustc/Makefile b/run_rustc/Makefile index 385bfd9c..71c124a3 100644 --- a/run_rustc/Makefile +++ b/run_rustc/Makefile @@ -4,8 +4,8 @@ RUSTC := ../output/rustc RUST_SRC := ../rustc-nightly/src/ PREFIX := prefix/ -BINDIR := $(PREFIX)bin -LIBDIR := $(PREFIX)lib +BINDIR := $(PREFIX)bin/ +LIBDIR := $(PREFIX)lib/ V ?= @ @@ -24,7 +24,7 @@ $(BINDIR)rustc: $(RUSTC) $(LIBDIR)%.rlib: $(RUST_SRC)%/lib.rs $(RUSTC) @mkdir -p $(dir $@) @echo "[RUSTC] -o $@" - time $(RUSTC) $(RUSTFLAGS_$@) -L output/ -L ../output/libs $< -o $@ + $Vtime $(RUSTC) $(RUSTFLAGS_$@) -L output/ -L ../output/libs $< -o $@ $(LIBDIR)%.rlib: $(RUST_SRC)%/src/lib.rs $(RUSTC) @mkdir -p $(dir $@) @echo "[RUSTC] -o $@" diff --git a/src/ast/expr.cpp b/src/ast/expr.cpp index dd586683..c1baf9b4 100644 --- a/src/ast/expr.cpp +++ b/src/ast/expr.cpp @@ -69,11 +69,11 @@ ExprNode::~ExprNode() { #define OPT_CLONE(node) (node.get() ? node->clone() : ::AST::ExprNodeP()) namespace { - static inline ExprNodeP mk_exprnodep(const Position& pos, AST::ExprNode* en) { - en->set_pos(pos); + static inline ExprNodeP mk_exprnodep(const Span& pos, AST::ExprNode* en) { + en->set_span(pos); return ExprNodeP(en); } - #define NEWNODE(type, ...) mk_exprnodep(get_pos(), new type(__VA_ARGS__)) + #define NEWNODE(type, ...) mk_exprnodep(span(), new type(__VA_ARGS__)) } NODE(ExprNode_Block, { @@ -453,7 +453,7 @@ NV(ExprNode_Block, { }) NV(ExprNode_Macro, { - BUG(node.get_pos(), "Hit unexpanded macro in expression - " << node); + BUG(node.span(), "Hit unexpanded macro in expression - " << node); }) NV(ExprNode_Asm, { diff --git a/src/ast/expr.hpp b/src/ast/expr.hpp index ee1da656..a6b58e03 100644 --- a/src/ast/expr.hpp +++ b/src/ast/expr.hpp @@ -26,7 +26,7 @@ class NodeVisitor; class ExprNode { MetaItems m_attrs; - Position m_pos; + Span m_span; public: virtual ~ExprNode() = 0; @@ -34,9 +34,8 @@ public: virtual void print(::std::ostream& os) const = 0; virtual ::std::unique_ptr<ExprNode> clone() const = 0; - void set_pos(Position p) { m_pos = ::std::move(p); } - const Position& get_pos() const { return m_pos; } - Span span() const { return m_pos; } + void set_span(Span s) { m_span = ::std::move(s); } + const Span& span() const { return m_span; } void set_attrs(MetaItems&& mi) { m_attrs = mv$(mi); diff --git a/src/ast/path.cpp b/src/ast/path.cpp index ebec5d5a..37428b1a 100644 --- a/src/ast/path.cpp +++ b/src/ast/path.cpp @@ -190,18 +190,18 @@ void Path::bind_variable(unsigned int slot) } void Path::bind_enum_var(const Enum& ent, const ::std::string& name, const ::std::vector<TypeRef>& /*args*/) { - auto it = ::std::find_if(ent.variants().begin(), ent.variants().end(), [&](const auto& x) { return x.m_name == name; }); - if( it == ent.variants().end() ) - { - throw ParseError::Generic("Enum variant not found"); - } - unsigned int idx = it - ent.variants().begin(); + auto it = ::std::find_if(ent.variants().begin(), ent.variants().end(), [&](const auto& x) { return x.m_name == name; }); + if( it == ent.variants().end() ) + { + throw ParseError::Generic("Enum variant not found"); + } + unsigned int idx = it - ent.variants().begin(); DEBUG("Bound to enum variant '" << name << "' (#" << idx << ")"); - ::AST::PathBinding::Data_EnumVar tmp; - tmp.enum_ = &ent; - tmp.idx = idx; - m_binding = PathBinding::make_EnumVar(tmp); + ::AST::PathBinding::Data_EnumVar tmp = {}; + tmp.enum_ = &ent; + tmp.idx = idx; + m_binding = PathBinding::make_EnumVar( mv$(tmp) ); } Path& Path::operator+=(const Path& other) diff --git a/src/common.hpp b/src/common.hpp index 55e0f833..dec40191 100644 --- a/src/common.hpp +++ b/src/common.hpp @@ -12,7 +12,7 @@ #include <memory> #ifdef _MSC_VER -#define __attribute__(x) +#define __attribute__(x) /* no-op */ #endif #define FMT(ss) (dynamic_cast< ::std::stringstream&>(::std::stringstream() << ss).str()) @@ -22,7 +22,7 @@ #define rc_new$(...) ::make_shared_ptr(::std::move(__VA_ARGS__)) #include "include/debug.hpp" -#include "include/rustic.hpp" // slice and option +#include "include/rustic.hpp" // slice and option #include "include/compile_error.hpp" template<typename T> diff --git a/src/expand/cfg.hpp b/src/expand/cfg.hpp index 8e5fce42..edf17851 100644 --- a/src/expand/cfg.hpp +++ b/src/expand/cfg.hpp @@ -1,6 +1,8 @@ #pragma once +#include <ast/attrs.hpp> + extern void Cfg_SetFlag(::std::string name); extern void Cfg_SetValue(::std::string name, ::std::string val); extern void Cfg_SetValueCb(::std::string name, ::std::function<bool(const ::std::string&)> cb); diff --git a/src/expand/file_line.cpp b/src/expand/file_line.cpp index 8dfb7e6d..7e117993 100644 --- a/src/expand/file_line.cpp +++ b/src/expand/file_line.cpp @@ -9,12 +9,23 @@ #include "../parse/common.hpp" #include "../parse/ttstream.hpp" +namespace { + const Span& get_top_span(const Span& sp) { + if( sp.outer_span ) { + return get_top_span(*sp.outer_span); + } + else { + return sp; + } + } +} + class CExpanderFile: public ExpandProcMacro { ::std::unique_ptr<TokenStream> expand(const Span& sp, const AST::Crate& crate, const ::std::string& ident, const TokenTree& tt, AST::Module& mod) override { - return box$( TTStreamO(TokenTree(Token(TOK_STRING, sp.filename.c_str()))) ); + return box$( TTStreamO(TokenTree(Token(TOK_STRING, get_top_span(sp).filename.c_str()))) ); } }; @@ -23,7 +34,7 @@ class CExpanderLine: { ::std::unique_ptr<TokenStream> expand(const Span& sp, const AST::Crate& crate, const ::std::string& ident, const TokenTree& tt, AST::Module& mod) override { - return box$( TTStreamO(TokenTree(Token((uint64_t)sp.start_line, CORETYPE_U32))) ); + return box$( TTStreamO(TokenTree(Token((uint64_t)get_top_span(sp).start_line, CORETYPE_U32))) ); } }; @@ -32,7 +43,7 @@ class CExpanderColumn: { ::std::unique_ptr<TokenStream> expand(const Span& sp, const AST::Crate& crate, const ::std::string& ident, const TokenTree& tt, AST::Module& mod) override { - return box$( TTStreamO(TokenTree(Token((uint64_t)sp.start_ofs, CORETYPE_U32))) ); + return box$( TTStreamO(TokenTree(Token((uint64_t)get_top_span(sp).start_ofs, CORETYPE_U32))) ); } }; diff --git a/src/expand/macro_rules.cpp b/src/expand/macro_rules.cpp index 338edd12..0375f430 100644 --- a/src/expand/macro_rules.cpp +++ b/src/expand/macro_rules.cpp @@ -169,11 +169,6 @@ class CMacroReexportHandler: } }; -::std::unique_ptr<TokenStream> Macro_Invoke(const char* name, const MacroRules& rules, TokenTree tt, AST::Module& mod) -{ - return Macro_InvokeRules(name, rules, mv$(tt), mod); -} - STATIC_MACRO("macro_rules", CMacroRulesExpander); STATIC_DECORATOR("macro_use", CMacroUseHandler); diff --git a/src/expand/macro_rules.hpp b/src/expand/macro_rules.hpp index b5f09f1c..00332dd5 100644 --- a/src/expand/macro_rules.hpp +++ b/src/expand/macro_rules.hpp @@ -12,5 +12,3 @@ namespace AST { class TokenTree; class TokenStream; class MacroRules; - -extern ::std::unique_ptr<TokenStream> Macro_Invoke(const char* name, const MacroRules& rules, TokenTree tt, AST::Module& mod); diff --git a/src/expand/mod.cpp b/src/expand/mod.cpp index 01786284..5c3be722 100644 --- a/src/expand/mod.cpp +++ b/src/expand/mod.cpp @@ -11,12 +11,13 @@ #include <synext.hpp> #include <map> #include "macro_rules.hpp" +#include "../macro_rules/macro_rules.hpp" #include "../parse/common.hpp" // For reparse from macros #include <ast/expr.hpp> #include "cfg.hpp" -DecoratorDef* g_decorators_list = nullptr; -MacroDef* g_macros_list = nullptr; +DecoratorDef* g_decorators_list = nullptr; +MacroDef* g_macros_list = nullptr; ::std::map< ::std::string, ::std::unique_ptr<ExpandDecorator> > g_decorators; ::std::map< ::std::string, ::std::unique_ptr<ExpandProcMacro> > g_macros; @@ -26,18 +27,18 @@ void Expand_Expr(::AST::Crate& crate, LList<const AST::Module*> modstack, AST::E void Expand_Expr(::AST::Crate& crate, LList<const AST::Module*> modstack, ::std::shared_ptr<AST::ExprNode>& node); void Register_Synext_Decorator(::std::string name, ::std::unique_ptr<ExpandDecorator> handler) { - g_decorators.insert(::std::make_pair( mv$(name), mv$(handler) )); + g_decorators.insert(::std::make_pair( mv$(name), mv$(handler) )); } void Register_Synext_Macro(::std::string name, ::std::unique_ptr<ExpandProcMacro> handler) { - g_macros.insert(::std::make_pair( mv$(name), mv$(handler) )); + g_macros.insert(::std::make_pair( mv$(name), mv$(handler) )); } void Register_Synext_Decorator_Static(DecoratorDef* def) { - def->prev = g_decorators_list; - g_decorators_list = def; + def->prev = g_decorators_list; + g_decorators_list = def; } void Register_Synext_Macro_Static(MacroDef* def) { - def->prev = g_macros_list; - g_macros_list = def; + def->prev = g_macros_list; + g_macros_list = def; } @@ -115,7 +116,7 @@ void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate if( input_ident != "" ) ERROR(mi_span, E0000, "macro_rules! macros can't take an ident"); - auto e = Macro_Invoke(name.c_str(), *mr.data, mv$(input_tt), mod); + auto e = Macro_InvokeRules(name.c_str(), *mr.data, mi_span, mv$(input_tt), mod); return e; } } @@ -134,7 +135,7 @@ void Expand_Attrs(::AST::MetaItems& attrs, AttrStage stage, ::AST::Crate& crate } if( last_mac ) { - auto e = Macro_Invoke(name.c_str(), *last_mac, mv$(input_tt), mod); + auto e = Macro_InvokeRules(name.c_str(), *last_mac, mi_span, mv$(input_tt), mod); return e; } } @@ -317,7 +318,7 @@ struct CExpandExpr: { this->visit(cnode); if(cnode.get() == nullptr) - ERROR(parent.get_pos(), E0000, "#[cfg] not allowed in this position"); + ERROR(parent.span(), E0000, "#[cfg] not allowed in this position"); } assert( ! this->replacement ); } @@ -343,7 +344,7 @@ struct CExpandExpr: ::AST::ExprNodeP rv; auto& mod = this->cur_mod(); - auto ttl = Expand_Macro( crate, modstack, mod, Span(node.get_pos()), node.m_name, node.m_ident, node.m_tokens ); + auto ttl = Expand_Macro( crate, modstack, mod, node.span(), node.m_name, node.m_ident, node.m_tokens ); if( !ttl.get() ) { // No expansion @@ -362,7 +363,7 @@ struct CExpandExpr: auto newexpr = Parse_ExprBlockLine_WithItems(*ttl, local_mod_ptr, add_silence_if_end); if( tmp_local_mod ) - TODO(node.get_pos(), "Handle edge case where a macro expansion outside of a _Block creates an item"); + TODO(node.span(), "Handle edge case where a macro expansion outside of a _Block creates an item"); if( newexpr ) { @@ -382,7 +383,7 @@ struct CExpandExpr: if( ttl->lookahead(0) != TOK_EOF ) { if( !nodes_out ) { - ERROR(node.get_pos(), E0000, "Unused tokens at the end of macro expansion - " << ttl->getToken()); + ERROR(node.span(), E0000, "Unused tokens at the end of macro expansion - " << ttl->getToken()); } } } @@ -1074,17 +1075,17 @@ void Expand_Mod_IndexAnon(::AST::Crate& crate, ::AST::Module& mod) } void Expand(::AST::Crate& crate) { - // Fill macro/decorator map from init list - while(g_decorators_list) - { - g_decorators.insert(::std::make_pair( mv$(g_decorators_list->name), mv$(g_decorators_list->def) )); - g_decorators_list = g_decorators_list->prev; - } - while (g_macros_list) - { - g_macros.insert(::std::make_pair(mv$(g_macros_list->name), mv$(g_macros_list->def))); - g_macros_list = g_macros_list->prev; - } + // Fill macro/decorator map from init list + while(g_decorators_list) + { + g_decorators.insert(::std::make_pair( mv$(g_decorators_list->name), mv$(g_decorators_list->def) )); + g_decorators_list = g_decorators_list->prev; + } + while (g_macros_list) + { + g_macros.insert(::std::make_pair(mv$(g_macros_list->name), mv$(g_macros_list->def))); + g_macros_list = g_macros_list->prev; + } auto modstack = LList<const ::AST::Module*>(nullptr, &crate.m_root_module); diff --git a/src/hir/deserialise.cpp b/src/hir/deserialise.cpp index 0ff4d63b..3cb58a2e 100644 --- a/src/hir/deserialise.cpp +++ b/src/hir/deserialise.cpp @@ -338,11 +338,10 @@ namespace { switch(auto tag = m_in.read_tag()) { #define _(x, ...) case ::MIR::LValue::TAG_##x: return ::MIR::LValue::make_##x( __VA_ARGS__ ); - _(Variable, static_cast<unsigned int>(m_in.read_count()) ) - _(Temporary, { static_cast<unsigned int>(m_in.read_count()) } ) - _(Argument, { static_cast<unsigned int>(m_in.read_count()) } ) - _(Static, deserialise_path() ) _(Return, {}) + _(Argument, { static_cast<unsigned int>(m_in.read_count()) } ) + _(Local, static_cast<unsigned int>(m_in.read_count()) ) + _(Static, deserialise_path() ) _(Field, { box$( deserialise_mir_lvalue() ), static_cast<unsigned int>(m_in.read_count()) @@ -505,10 +504,10 @@ namespace { ::HIR::Linkage deserialise_linkage() { - ::HIR::Linkage l; - l.type = ::HIR::Linkage::Type::Auto; - l.name = m_in.read_string(); - return l; + ::HIR::Linkage l; + l.type = ::HIR::Linkage::Type::Auto; + l.name = m_in.read_string(); + return l; } // - Value items @@ -962,8 +961,8 @@ namespace { ::MIR::Function rv; - rv.named_variables = deserialise_vec< ::HIR::TypeRef>( ); - rv.temporaries = deserialise_vec< ::HIR::TypeRef>( ); + rv.locals = deserialise_vec< ::HIR::TypeRef>( ); + //rv.local_names = deserialise_vec< ::std::string>( ); rv.drop_flags = deserialise_vec<bool>(); rv.blocks = deserialise_vec< ::MIR::BasicBlock>( ); @@ -1004,15 +1003,14 @@ namespace { deserialise_vec< ::std::string>() }); case 3: { - ::MIR::Statement::Data_SetDropFlag sdf; - sdf.idx = static_cast<unsigned int>(m_in.read_count()); - sdf.new_val = m_in.read_bool(); - sdf.other = static_cast<unsigned int>(m_in.read_count()); - return ::MIR::Statement::make_SetDropFlag(sdf); - } + ::MIR::Statement::Data_SetDropFlag sdf; + sdf.idx = static_cast<unsigned int>(m_in.read_count()); + sdf.new_val = m_in.read_bool(); + sdf.other = static_cast<unsigned int>(m_in.read_count()); + return ::MIR::Statement::make_SetDropFlag(sdf); + } case 4: return ::MIR::Statement::make_ScopeEnd({ - deserialise_vec<unsigned int>(), deserialise_vec<unsigned int>() }); default: diff --git a/src/hir/dump.cpp b/src/hir/dump.cpp index 649116e0..75dde59f 100644 --- a/src/hir/dump.cpp +++ b/src/hir/dump.cpp @@ -262,13 +262,13 @@ namespace { void visit_constant(::HIR::ItemPath p, ::HIR::Constant& item) override { m_os << indent() << "const " << p.get_name() << ": " << item.m_type << " = " << item.m_value_res; - if( item.m_value ) - { - m_os << " /*= "; - item.m_value->visit(*this); - m_os << "*/"; - } - m_os << ";\n"; + if( item.m_value ) + { + m_os << " /*= "; + item.m_value->visit(*this); + m_os << "*/"; + } + m_os << ";\n"; } // - Misc @@ -530,7 +530,7 @@ namespace { m_os << ", "; } m_os << ")"; - m_os << "/* : " << node.m_res_type << " */"; + m_os << "/* : " << node.m_res_type << " */"; } void visit(::HIR::ExprNode_CallValue& node) override { diff --git a/src/hir/from_ast.cpp b/src/hir/from_ast.cpp index c0a3151e..e0429aca 100644 --- a/src/hir/from_ast.cpp +++ b/src/hir/from_ast.cpp @@ -702,7 +702,7 @@ { if( ptr->m_datatype == CORETYPE_UINT || ptr->m_datatype == CORETYPE_ANY ) { - // TODO: Limit check. + // TODO: Limit check. auto size_val = static_cast<unsigned int>( ptr->m_value ); return ::HIR::TypeRef::new_array( mv$(inner), size_val ); } diff --git a/src/hir/from_ast_expr.cpp b/src/hir/from_ast_expr.cpp index 9085bd3b..296bdce1 100644 --- a/src/hir/from_ast_expr.cpp +++ b/src/hir/from_ast_expr.cpp @@ -30,7 +30,7 @@ struct LowerHIR_ExprNode_Visitor: auto rv = new ::HIR::ExprNode_Block(v.span()); for(const auto& n : v.m_nodes) { - ASSERT_BUG(v.get_pos(), n, "NULL node encountered in block"); + ASSERT_BUG(v.span(), n, "NULL node encountered in block"); rv->m_nodes.push_back( LowerHIR_ExprNode_Inner( *n ) ); } if( v.m_yields_final_value && ! rv->m_nodes.empty() ) @@ -48,7 +48,7 @@ struct LowerHIR_ExprNode_Visitor: m_rv.reset( static_cast< ::HIR::ExprNode*>(rv) ); } virtual void visit(::AST::ExprNode_Macro& v) override { - BUG(v.get_pos(), "Hit ExprNode_Macro"); + BUG(v.span(), "Hit ExprNode_Macro"); } virtual void visit(::AST::ExprNode_Asm& v) override { ::std::vector< ::HIR::ExprNode_Asm::ValRef> outputs; @@ -72,7 +72,7 @@ struct LowerHIR_ExprNode_Visitor: case ::AST::ExprNode_Flow::CONTINUE: case ::AST::ExprNode_Flow::BREAK: if( v.m_value ) - TODO(v.get_pos(), "Handle break/continue values in HIR"); + TODO(v.span(), "Handle break/continue values in HIR"); m_rv.reset( new ::HIR::ExprNode_LoopControl( v.span(), v.m_target, (v.m_type == ::AST::ExprNode_Flow::CONTINUE) ) ); break; } @@ -167,7 +167,7 @@ struct LowerHIR_ExprNode_Visitor: } break; } case ::AST::ExprNode_BinOp::PLACE_IN: - TODO(v.get_pos(), "Desugar placement syntax"); + TODO(v.span(), "Desugar placement syntax"); break; case ::AST::ExprNode_BinOp::CMPEQU : op = ::HIR::ExprNode_BinOp::Op::CmpEqu ; if(0) @@ -213,7 +213,7 @@ struct LowerHIR_ExprNode_Visitor: )); } break; case ::AST::ExprNode_UniOp::QMARK: - BUG(v.get_pos(), "Encounterd question mark operator (should have been expanded in AST)"); + BUG(v.span(), "Encounterd question mark operator (should have been expanded in AST)"); break; case ::AST::ExprNode_UniOp::REF: @@ -262,7 +262,7 @@ struct LowerHIR_ExprNode_Visitor: TU_MATCH_DEF(::AST::PathBinding, (v.m_path.binding()), (e), ( m_rv.reset( new ::HIR::ExprNode_CallPath( v.span(), - LowerHIR_Path(Span(v.get_pos()), v.m_path), + LowerHIR_Path(v.span(), v.m_path), mv$( args ) ) ); ), @@ -361,7 +361,7 @@ struct LowerHIR_ExprNode_Visitor: break; } case ::AST::ExprNode_Loop::FOR: // NOTE: This should already be desugared (as a pass before resolve) - BUG(v.get_pos(), "Encountered still-sugared for loop"); + BUG(v.span(), "Encountered still-sugared for loop"); break; } @@ -507,7 +507,7 @@ struct LowerHIR_ExprNode_Visitor: } m_rv.reset( new ::HIR::ExprNode_Literal( v.span(), ::HIR::ExprNode_Literal::Data::make_Integer({ - H::get_type( Span(v.get_pos()), v.m_datatype ), + H::get_type( v.span(), v.m_datatype ), v.m_value }) ) ); @@ -520,7 +520,7 @@ struct LowerHIR_ExprNode_Visitor: case CORETYPE_F32: ct = ::HIR::CoreType::F32; break; case CORETYPE_F64: ct = ::HIR::CoreType::F64; break; default: - BUG(v.get_pos(), "Unknown type for float literal"); + BUG(v.span(), "Unknown type for float literal"); } m_rv.reset( new ::HIR::ExprNode_Literal( v.span(), ::HIR::ExprNode_Literal::Data::make_Float({ ct, v.m_value }) @@ -560,7 +560,7 @@ struct LowerHIR_ExprNode_Visitor: ERROR(v.span(), E0000, "Union constructors can't take a base value"); m_rv.reset( new ::HIR::ExprNode_UnionLiteral( v.span(), - LowerHIR_GenericPath(v.get_pos(), v.m_path), + LowerHIR_GenericPath(v.span(), v.m_path), v.m_values[0].first, LowerHIR_ExprNode_Inner(*v.m_values[0].second) ) ); @@ -571,7 +571,7 @@ struct LowerHIR_ExprNode_Visitor: for(const auto& val : v.m_values) values.push_back( ::std::make_pair(val.first, LowerHIR_ExprNode_Inner(*val.second)) ); m_rv.reset( new ::HIR::ExprNode_StructLiteral( v.span(), - LowerHIR_GenericPath(v.get_pos(), v.m_path), + LowerHIR_GenericPath(v.span(), v.m_path), ! v.m_path.binding().is_EnumVar(), LowerHIR_ExprNode_Inner_Opt(v.m_base_value.get()), mv$(values) @@ -604,7 +604,7 @@ struct LowerHIR_ExprNode_Visitor: virtual void visit(::AST::ExprNode_NamedValue& v) override { TU_IFLET(::AST::Path::Class, v.m_path.m_class, Local, e, if( !v.m_path.binding().is_Variable() ) { - BUG(v.get_pos(), "Named value was a local, but wasn't bound - " << v.m_path); + BUG(v.span(), "Named value was a local, but wasn't bound - " << v.m_path); } auto slot = v.m_path.binding().as_Variable().slot; m_rv.reset( new ::HIR::ExprNode_Variable( v.span(), e.name, slot ) ); @@ -612,7 +612,7 @@ struct LowerHIR_ExprNode_Visitor: else { TU_MATCH_DEF(::AST::PathBinding, (v.m_path.binding()), (e), ( - auto p = LowerHIR_Path(Span(v.get_pos()), v.m_path); + auto p = LowerHIR_Path(v.span(), v.m_path); if( p.m_data.is_Generic() ) { BUG(v.span(), "Unknown binding for PathValue but path is generic - " << v.m_path); } @@ -643,10 +643,10 @@ struct LowerHIR_ExprNode_Visitor: } } if( is_tuple_constructor ) { - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::STRUCT_CONSTR ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::STRUCT_CONSTR ) ); } else { - m_rv.reset( new ::HIR::ExprNode_UnitVariant( v.span(), LowerHIR_GenericPath(Span(v.get_pos()), v.m_path), true ) ); + m_rv.reset( new ::HIR::ExprNode_UnitVariant( v.span(), LowerHIR_GenericPath(v.span(), v.m_path), true ) ); } ), (EnumVar, @@ -680,33 +680,33 @@ struct LowerHIR_ExprNode_Visitor: } (void)var_idx; // TODO: Save time later by saving this. if( is_tuple_constructor ) { - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::ENUM_VAR_CONSTR ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::ENUM_VAR_CONSTR ) ); } else { - m_rv.reset( new ::HIR::ExprNode_UnitVariant( v.span(), LowerHIR_GenericPath(Span(v.get_pos()), v.m_path), false ) ); + m_rv.reset( new ::HIR::ExprNode_UnitVariant( v.span(), LowerHIR_GenericPath(v.span(), v.m_path), false ) ); } ), (Function, - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::FUNCTION ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::FUNCTION ) ); ), (Static, if( e.static_ ) { if( e.static_->s_class() != ::AST::Static::CONST ) { - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::STATIC ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::STATIC ) ); } else { - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::CONSTANT ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::CONSTANT ) ); } } else if( e.hir ) { - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::STATIC ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::STATIC ) ); } // HACK: If the HIR pointer is nullptr, then it refers to a `const else { - m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(Span(v.get_pos()), v.m_path), ::HIR::ExprNode_PathValue::CONSTANT ) ); + m_rv.reset( new ::HIR::ExprNode_PathValue( v.span(), LowerHIR_Path(v.span(), v.m_path), ::HIR::ExprNode_PathValue::CONSTANT ) ); } ) ) @@ -739,7 +739,7 @@ struct LowerHIR_ExprNode_Visitor: const_cast<::AST::ExprNode*>(&e)->visit( v ); if( ! v.m_rv ) { - BUG(e.get_pos(), typeid(e).name() << " - Yielded a nullptr HIR node"); + BUG(e.span(), typeid(e).name() << " - Yielded a nullptr HIR node"); } return mv$( v.m_rv ); } diff --git a/src/hir/serialise.cpp b/src/hir/serialise.cpp index 77e17dba..78efe261 100644 --- a/src/hir/serialise.cpp +++ b/src/hir/serialise.cpp @@ -77,6 +77,9 @@ namespace { m_out.write_count(e.first); serialise(e.second); } + //void serialise(::MIR::BasicBlockId val) { + // m_out.write_count(val); + //} void serialise_type(const ::HIR::TypeRef& ty) { @@ -457,8 +460,8 @@ namespace { void serialise(const ::MIR::Function& mir) { // Write out MIR. - serialise_vec( mir.named_variables ); - serialise_vec( mir.temporaries ); + serialise_vec( mir.locals ); + //serialise_vec( mir.slot_names ); serialise_vec( mir.drop_flags ); serialise_vec( mir.blocks ); } @@ -498,8 +501,7 @@ namespace { ), (ScopeEnd, m_out.write_tag(4); - serialise_vec(e.vars); - serialise_vec(e.tmps); + serialise_vec(e.slots); ) ) } @@ -532,6 +534,12 @@ namespace { for(auto t : e.targets) m_out.write_count(t); ), + (SwitchValue, + serialise(e.val); + m_out.write_count(e.def_target); + serialise_vec(e.targets); + serialise(e.values); + ), (Call, m_out.write_count(e.ret_block); m_out.write_count(e.panic_block); @@ -541,6 +549,21 @@ namespace { ) ) } + void serialise(const ::MIR::SwitchValues& sv) + { + m_out.write_tag( static_cast<int>(sv.tag()) ); + TU_MATCHA( (sv), (e), + (Unsigned, + serialise_vec(e); + ), + (Signed, + serialise_vec(e); + ), + (String, + serialise_vec(e); + ) + ) + } void serialise(const ::MIR::CallTarget& ct) { m_out.write_tag( static_cast<int>(ct.tag()) ); @@ -571,20 +594,17 @@ namespace { TRACE_FUNCTION_F("LValue = "<<lv); m_out.write_tag( static_cast<int>(lv.tag()) ); TU_MATCHA( (lv), (e), - (Variable, - m_out.write_count(e); - ), - (Temporary, - m_out.write_count(e.idx); + (Return, ), (Argument, m_out.write_count(e.idx); ), + (Local, + m_out.write_count(e); + ), (Static, serialise_path(e); ), - (Return, - ), (Field, serialise(e.val); m_out.write_count(e.field_index); diff --git a/src/hir/type.cpp b/src/hir/type.cpp index 7dd8dc80..c672b3bd 100644 --- a/src/hir/type.cpp +++ b/src/hir/type.cpp @@ -781,7 +781,7 @@ bool ::HIR::TypeRef::match_test_generics(const Span& sp, const ::HIR::TypeRef& x (Enum , return ::HIR::TypeRef::TypePathBinding(e); ) ) assert(!"Fell off end of clone_binding"); - throw ""; + throw ""; } diff --git a/src/hir/type.hpp b/src/hir/type.hpp index a832e8e9..cc0894d2 100644 --- a/src/hir/type.hpp +++ b/src/hir/type.hpp @@ -178,8 +178,8 @@ public: Data m_data; TypeRef(): - m_data(Data::make_Infer({ ~0u, InferClass::None })) - {} + m_data(Data::make_Infer({ ~0u, InferClass::None })) + {} TypeRef(TypeRef&& ) = default; TypeRef(const TypeRef& ) = delete; TypeRef& operator=(TypeRef&& ) = default; @@ -212,9 +212,9 @@ public: static TypeRef new_diverge() { return TypeRef(Data::make_Diverge({})); } - static TypeRef new_infer(unsigned int idx = ~0u, InferClass ty_class = InferClass::None) { - return TypeRef(Data::make_Infer({idx, ty_class})); - } + static TypeRef new_infer(unsigned int idx = ~0u, InferClass ty_class = InferClass::None) { + return TypeRef(Data::make_Infer({idx, ty_class})); + } static TypeRef new_borrow(BorrowType bt, TypeRef inner) { return TypeRef(Data::make_Borrow({bt, box$(mv$(inner))})); } diff --git a/src/hir_conv/bind.cpp b/src/hir_conv/bind.cpp index cf443eb6..beac3b84 100644 --- a/src/hir_conv/bind.cpp +++ b/src/hir_conv/bind.cpp @@ -480,14 +480,12 @@ namespace { static void visit_lvalue(Visitor& upper_visitor, ::MIR::LValue& lv) { TU_MATCHA( (lv), (e), - (Variable, + (Return, ), - (Temporary, + (Local, ), (Argument, ), - (Return, - ), (Static, upper_visitor.visit_path(e, ::HIR::Visitor::PathContext::VALUE); ), @@ -529,9 +527,7 @@ namespace { ) } }; - for(auto& ty : expr.m_mir->named_variables) - this->visit_type(ty); - for(auto& ty : expr.m_mir->temporaries) + for(auto& ty : expr.m_mir->locals) this->visit_type(ty); for(auto& block : expr.m_mir->blocks) { @@ -621,6 +617,9 @@ namespace { (Switch, H::visit_lvalue(*this, te.val); ), + (SwitchValue, + H::visit_lvalue(*this, te.val); + ), (Call, H::visit_lvalue(*this, te.ret_val); TU_MATCHA( (te.fcn), (e2), diff --git a/src/hir_conv/constant_evaluation.cpp b/src/hir_conv/constant_evaluation.cpp index a950a3dd..e8138169 100644 --- a/src/hir_conv/constant_evaluation.cpp +++ b/src/hir_conv/constant_evaluation.cpp @@ -1026,32 +1026,25 @@ namespace { ::MIR::TypeResolve state { sp, resolve, FMT_CB(,), exp, {}, fcn }; ::HIR::Literal retval; - ::std::vector< ::HIR::Literal> locals; - ::std::vector< ::HIR::Literal> temps; - locals.resize( fcn.named_variables.size() ); - temps.resize( fcn.temporaries.size() ); + ::std::vector< ::HIR::Literal> locals( fcn.locals.size() ); auto get_lval = [&](const ::MIR::LValue& lv) -> ::HIR::Literal& { TU_MATCHA( (lv), (e), - (Variable, - if( e >= locals.size() ) - BUG(sp, "Local index out of range - " << e << " >= " << locals.size()); - return locals[e]; - ), - (Temporary, - if( e.idx >= temps.size() ) - BUG(sp, "Temp index out of range - " << e.idx << " >= " << temps.size()); - return temps[e.idx]; + (Return, + return retval; ), (Argument, + ASSERT_BUG(sp, e.idx < args.size(), "Argument index out of range - " << e.idx << " >= " << args.size()); return args[e.idx]; ), + (Local, + if( e >= locals.size() ) + BUG(sp, "Local index out of range - " << e << " >= " << locals.size()); + return locals[e]; + ), (Static, TODO(sp, "LValue::Static"); ), - (Return, - return retval; - ), (Field, TODO(sp, "LValue::Field"); ), diff --git a/src/hir_expand/const_eval_full.cpp b/src/hir_expand/const_eval_full.cpp index 538693f4..04575a6e 100644 --- a/src/hir_expand/const_eval_full.cpp +++ b/src/hir_expand/const_eval_full.cpp @@ -292,49 +292,41 @@ namespace { ::MIR::TypeResolve state { sp, resolve, name, ::HIR::TypeRef(), {}, fcn }; ::HIR::Literal retval; - ::std::vector< ::HIR::Literal> locals; - ::std::vector< ::HIR::Literal> temps; - locals.resize( fcn.named_variables.size() ); - temps.resize( fcn.temporaries.size() ); + ::std::vector< ::HIR::Literal> locals( fcn.locals.size() ); struct LocalState { typedef ::std::vector< ::HIR::Literal> t_vec_lit; ::MIR::TypeResolve& state; ::HIR::Literal& retval; - ::std::vector< ::HIR::Literal>& locals; - ::std::vector< ::HIR::Literal>& temps; ::std::vector< ::HIR::Literal>& args; + ::std::vector< ::HIR::Literal>& locals; - LocalState(::MIR::TypeResolve& state, ::HIR::Literal& retval, t_vec_lit& locals, t_vec_lit& temps, t_vec_lit& args): + LocalState(::MIR::TypeResolve& state, ::HIR::Literal& retval, t_vec_lit& args, t_vec_lit& locals): state(state), retval(retval), - locals(locals), - temps(temps), - args(args) + args(args), + locals(locals) {} ::HIR::Literal& get_lval(const ::MIR::LValue& lv) { TU_MATCHA( (lv), (e), - (Variable, + (Return, + return retval; + ), + (Local, if( e >= locals.size() ) MIR_BUG(state, "Local index out of range - " << e << " >= " << locals.size()); return locals[e]; ), - (Temporary, - if( e.idx >= temps.size() ) - MIR_BUG(state, "Temp index out of range - " << e.idx << " >= " << temps.size()); - return temps[e.idx]; - ), (Argument, + if( e.idx >= args.size() ) + MIR_BUG(state, "Local index out of range - " << e.idx << " >= " << args.size()); return args[e.idx]; ), (Static, MIR_TODO(state, "LValue::Static - " << e); ), - (Return, - return retval; - ), (Field, auto& val = get_lval(*e.val); MIR_ASSERT(state, val.is_List(), "LValue::Field on non-list literal - " << val.tag_str() << " - " << lv); @@ -362,7 +354,7 @@ namespace { throw ""; } }; - LocalState local_state( state, retval, locals, temps, args ); + LocalState local_state( state, retval, args, locals ); auto get_lval = [&](const ::MIR::LValue& lv) -> ::HIR::Literal& { return local_state.get_lval(lv); }; auto read_lval = [&](const ::MIR::LValue& lv) -> ::HIR::Literal { diff --git a/src/hir_expand/erased_types.cpp b/src/hir_expand/erased_types.cpp index aca58207..caf42a79 100644 --- a/src/hir_expand/erased_types.cpp +++ b/src/hir_expand/erased_types.cpp @@ -14,21 +14,23 @@ const ::HIR::Function& HIR_Expand_ErasedType_GetFunction(const Span& sp, const StaticTraitResolve& resolve, const ::HIR::Path& origin_path, t_cb_generic& monomorph_cb, ::HIR::PathParams& impl_params) { const ::HIR::Function* fcn_ptr = nullptr; - switch(origin_path.m_data.tag()) - { - case ::HIR::Path::Data::TAG_UfcsUnknown: - BUG(Span(), "UfcsUnknown in ErasedType - " << origin_path); - case ::HIR::Path::Data::TAG_Generic: { - const auto& pe = origin_path.m_data.as_Generic(); - monomorph_cb = monomorphise_type_get_cb(sp, nullptr, nullptr, &pe.m_params); - fcn_ptr = &resolve.m_crate.get_function_by_path(sp, pe.m_path); - } break; - case ::HIR::Path::Data::TAG_UfcsKnown: - // NOTE: This isn't possible yet (will it be? or will it expand to an associated type?) - TODO(sp, "Replace ErasedType - " << origin_path << " with source (UfcsKnown)"); - break; - case ::HIR::Path::Data::TAG_UfcsInherent: { - const auto& pe = origin_path.m_data.as_UfcsInherent(); + switch(origin_path.m_data.tag()) + { + case ::HIR::Path::Data::TAGDEAD: + BUG(Span(), "DEAD in ErasedType - " << origin_path); + case ::HIR::Path::Data::TAG_UfcsUnknown: + BUG(Span(), "UfcsUnknown in ErasedType - " << origin_path); + case ::HIR::Path::Data::TAG_Generic: { + const auto& pe = origin_path.m_data.as_Generic(); + monomorph_cb = monomorphise_type_get_cb(sp, nullptr, nullptr, &pe.m_params); + fcn_ptr = &resolve.m_crate.get_function_by_path(sp, pe.m_path); + } break; + case ::HIR::Path::Data::TAG_UfcsKnown: + // NOTE: This isn't possible yet (will it be? or will it expand to an associated type?) + TODO(sp, "Replace ErasedType - " << origin_path << " with source (UfcsKnown)"); + break; + case ::HIR::Path::Data::TAG_UfcsInherent: { + const auto& pe = origin_path.m_data.as_UfcsInherent(); // 1. Find correct impl block for the path const ::HIR::TypeImpl* impl_ptr = nullptr; resolve.m_crate.find_type_impls(*pe.type, [&](const auto& ty)->const auto& { return ty; }, @@ -52,16 +54,16 @@ const ::HIR::Function& HIR_Expand_ErasedType_GetFunction(const Span& sp, const S return ::HIR::Compare::Equal; }); for(const auto& t : impl_params.m_types) - { + { if( t == ::HIR::TypeRef() ) - { + { TODO(sp, "Handle ErasedType where an impl parameter comes from a bound - " << origin_path); - } - } + } + } monomorph_cb = monomorphise_type_get_cb(sp, &*pe.type, &impl_params, &pe.params); - } break; - } + } break; + } assert(fcn_ptr); return *fcn_ptr; } diff --git a/src/hir_expand/ufcs_everything.cpp b/src/hir_expand/ufcs_everything.cpp index 44921105..adae855c 100644 --- a/src/hir_expand/ufcs_everything.cpp +++ b/src/hir_expand/ufcs_everything.cpp @@ -289,7 +289,7 @@ namespace { ASSERT_BUG(sp, ty_slot == ty_val, "Types must equal for non-operator assignment, " << ty_slot << " != " << ty_val); return ; _(Shr): {langitem = "shr_assign"; opname = "shr_assign"; } if(0) - _(Shl): {langitem = "shl_assign"; opname = "shl_assign"; } + _(Shl): {langitem = "shl_assign"; opname = "shl_assign"; } if( is_op_valid_shift(ty_slot, ty_val) ) { return ; } @@ -385,8 +385,8 @@ namespace { auto ty_r_ref = ::HIR::TypeRef::new_borrow( ::HIR::BorrowType::Shared, ty_r.clone() ); ::std::vector< ::HIR::ExprNodeP> args; - auto sp_left = node.m_left ->span(); - auto sp_right = node.m_right->span(); + auto sp_left = node.m_left ->span(); + auto sp_right = node.m_right->span(); args.push_back(NEWNODE(ty_l_ref.clone(), Borrow, sp_left , ::HIR::BorrowType::Shared, mv$(node.m_left ) )); args.push_back(NEWNODE(ty_r_ref.clone(), Borrow, sp_right, ::HIR::BorrowType::Shared, mv$(node.m_right) )); diff --git a/src/hir_typeck/expr_check.cpp b/src/hir_typeck/expr_check.cpp index fe67865f..a5d34186 100644 --- a/src/hir_typeck/expr_check.cpp +++ b/src/hir_typeck/expr_check.cpp @@ -640,7 +640,7 @@ namespace { fcn_ptr = &fcn; - monomorph_cb = monomorphise_type_get_cb(sp, &*e.type, &trait_params, &path_params); + monomorph_cb = monomorphise_type_get_cb(sp, &*e.type, &trait_params, &path_params); ), (UfcsUnknown, TODO(sp, "Hit a UfcsUnknown (" << path << ") - Is this an error?"); @@ -672,7 +672,7 @@ namespace { // Create monomorphise callback const auto& fcn_params = e.params; - monomorph_cb = monomorphise_type_get_cb(sp, &*e.type, &impl_params, &fcn_params); + monomorph_cb = monomorphise_type_get_cb(sp, &*e.type, &impl_params, &fcn_params); ) ) diff --git a/src/hir_typeck/expr_cs.cpp b/src/hir_typeck/expr_cs.cpp index eb922414..de7f05ec 100644 --- a/src/hir_typeck/expr_cs.cpp +++ b/src/hir_typeck/expr_cs.cpp @@ -1643,9 +1643,7 @@ namespace { (Function, fix_param_count(sp, this->context, *e.type, false, node.m_path, ie.m_params, e.params); - const auto& fcn_params = e.params; - const auto& trait_params = e.trait.m_params; - auto monomorph_cb = monomorphise_type_get_cb(sp, &*e.type, &e.trait.m_params, &e.params); + auto monomorph_cb = monomorphise_type_get_cb(sp, &*e.type, &e.trait.m_params, &e.params); ::HIR::FunctionType ft { ie.m_unsafe, ie.m_abi, box$( monomorphise_type_with(sp, ie.m_return, monomorph_cb) ), @@ -2368,148 +2366,148 @@ namespace { const auto& ty = *ty_p; DEBUG("- ty = " << ty); - if( const auto* e = ty.m_data.opt_Closure() ) - { - for( const auto& arg : e->m_arg_types ) - node.m_arg_types.push_back(arg.clone()); - node.m_arg_types.push_back(e->m_rettype->clone()); - node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Unknown; - } - else if( const auto* e = ty.m_data.opt_Function() ) - { - for( const auto& arg : e->m_arg_types ) - node.m_arg_types.push_back(arg.clone()); - node.m_arg_types.push_back(e->m_rettype->clone()); - node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Fn; - } - else if( ty.m_data.is_Infer() ) - { - // No idea yet - return ; - } - else - { - ::HIR::TypeRef fcn_args_tup; - ::HIR::TypeRef fcn_ret; - - // TODO: Use `find_trait_impls` instead of two different calls - // - This will get the TraitObject impl search too - - // Locate an impl of FnOnce (exists for all other Fn* traits) - unsigned int count = 0; - this->context.m_resolve.find_trait_impls(node.span(), lang_FnOnce, trait_pp, ty, [&](auto impl, auto cmp)->bool { - count++; - - auto tup = impl.get_trait_ty_param(0); - if (!tup.m_data.is_Tuple()) - ERROR(node.span(), E0000, "FnOnce expects a tuple argument, got " << tup); - fcn_args_tup = mv$(tup); - - fcn_ret = impl.get_type("Output"); - DEBUG("[visit:_CallValue] fcn_args_tup=" << fcn_args_tup << ", fcn_ret=" << fcn_ret); - return cmp == ::HIR::Compare::Equal; - }); - DEBUG("Found " << count << " impls of FnOnce"); - if(count > 1) { - return; - } - if(count == 1) - { - - // 3. Locate the most permissive implemented Fn* trait (Fn first, then FnMut, then assume just FnOnce) - // NOTE: Borrowing is added by the expansion to CallPath - if( this->context.m_resolve.find_trait_impls(node.span(), lang_Fn, trait_pp, ty, [&](auto impl, auto cmp) { - // TODO: Take the value of `cmp` into account - fcn_ret = impl.get_type("Output"); - return true; - //return cmp == ::HIR::Compare::Equal; - }) ) - { - DEBUG("-- Using Fn"); - node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Fn; - - this->context.equate_types_assoc(node.span(), node.m_res_type, lang_Fn, ::make_vec1(fcn_args_tup.clone()), ty, "Output"); - } - else if( this->context.m_resolve.find_trait_impls(node.span(), lang_FnMut, trait_pp, ty, [&](auto impl, auto cmp) { - // TODO: Take the value of `cmp` into account - fcn_ret = impl.get_type("Output"); - return true; - //return cmp == ::HIR::Compare::Equal; - }) ) - { - DEBUG("-- Using FnMut"); - node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::FnMut; - - this->context.equate_types_assoc(node.span(), node.m_res_type, lang_FnMut, ::make_vec1(fcn_args_tup.clone()), ty, "Output"); - } - else - { - DEBUG("-- Using FnOnce (default)"); - node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::FnOnce; - - this->context.equate_types_assoc(node.span(), node.m_res_type, lang_FnOnce, ::make_vec1(fcn_args_tup.clone()), ty, "Output"); - } - - // If the return type wasn't found in the impls, emit it as a UFCS - if(fcn_ret == ::HIR::TypeRef()) - { - fcn_ret = ::HIR::TypeRef(::HIR::Path(::HIR::Path::Data::make_UfcsKnown({ - box$(ty.clone()), - // - Clone argument tuple, as it's stolen into cache below - ::HIR::GenericPath(lang_FnOnce, ::HIR::PathParams(fcn_args_tup.clone())), - "Output", - {} - }))); - } - } - else if( const auto* e = ty.m_data.opt_Borrow() ) - { - deref_count++; - ty_p = &this->context.get_type(*e->inner); - DEBUG("Deref " << ty << " -> " << *ty_p); - keep_looping = true; - continue; - } - else - { - if( !ty.m_data.is_Generic() ) - { - bool found = this->context.m_resolve.find_trait_impls_crate(node.span(), lang_FnOnce, trait_pp, ty, [&](auto impl, auto cmp)->bool { - if (cmp == ::HIR::Compare::Fuzzy) - TODO(node.span(), "Handle fuzzy match - " << impl); - - auto tup = impl.get_trait_ty_param(0); - if (!tup.m_data.is_Tuple()) - ERROR(node.span(), E0000, "FnOnce expects a tuple argument, got " << tup); - fcn_args_tup = mv$(tup); - fcn_ret = impl.get_type("Output"); - ASSERT_BUG(node.span(), fcn_ret != ::HIR::TypeRef(), "Impl didn't have a type for Output - " << impl); - return true; - }); - if (found) { - // Fill cache and leave the TU_MATCH - node.m_arg_types = mv$(fcn_args_tup.m_data.as_Tuple()); - node.m_arg_types.push_back(mv$(fcn_ret)); - node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Unknown; - break; // leaves TU_MATCH - } - } - if( const auto* next_ty_p = this->context.m_resolve.autoderef(node.span(), ty, tmp_type) ) - { - DEBUG("Deref (autoderef) " << ty << " -> " << *next_ty_p); - deref_count++; - ty_p = next_ty_p; - keep_looping = true; - continue; - } - - // Didn't find anything. Error? - ERROR(node.span(), E0000, "Unable to find an implementation of Fn*" << trait_pp << " for " << this->context.m_ivars.fmt_type(ty)); - } - - node.m_arg_types = mv$(fcn_args_tup.m_data.as_Tuple()); - node.m_arg_types.push_back(mv$(fcn_ret)); - } + if( const auto* e = ty.m_data.opt_Closure() ) + { + for( const auto& arg : e->m_arg_types ) + node.m_arg_types.push_back(arg.clone()); + node.m_arg_types.push_back(e->m_rettype->clone()); + node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Unknown; + } + else if( const auto* e = ty.m_data.opt_Function() ) + { + for( const auto& arg : e->m_arg_types ) + node.m_arg_types.push_back(arg.clone()); + node.m_arg_types.push_back(e->m_rettype->clone()); + node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Fn; + } + else if( ty.m_data.is_Infer() ) + { + // No idea yet + return ; + } + else + { + ::HIR::TypeRef fcn_args_tup; + ::HIR::TypeRef fcn_ret; + + // TODO: Use `find_trait_impls` instead of two different calls + // - This will get the TraitObject impl search too + + // Locate an impl of FnOnce (exists for all other Fn* traits) + unsigned int count = 0; + this->context.m_resolve.find_trait_impls(node.span(), lang_FnOnce, trait_pp, ty, [&](auto impl, auto cmp)->bool { + count++; + + auto tup = impl.get_trait_ty_param(0); + if (!tup.m_data.is_Tuple()) + ERROR(node.span(), E0000, "FnOnce expects a tuple argument, got " << tup); + fcn_args_tup = mv$(tup); + + fcn_ret = impl.get_type("Output"); + DEBUG("[visit:_CallValue] fcn_args_tup=" << fcn_args_tup << ", fcn_ret=" << fcn_ret); + return cmp == ::HIR::Compare::Equal; + }); + DEBUG("Found " << count << " impls of FnOnce"); + if(count > 1) { + return; + } + if(count == 1) + { + + // 3. Locate the most permissive implemented Fn* trait (Fn first, then FnMut, then assume just FnOnce) + // NOTE: Borrowing is added by the expansion to CallPath + if( this->context.m_resolve.find_trait_impls(node.span(), lang_Fn, trait_pp, ty, [&](auto impl, auto cmp) { + // TODO: Take the value of `cmp` into account + fcn_ret = impl.get_type("Output"); + return true; + //return cmp == ::HIR::Compare::Equal; + }) ) + { + DEBUG("-- Using Fn"); + node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Fn; + + this->context.equate_types_assoc(node.span(), node.m_res_type, lang_Fn, ::make_vec1(fcn_args_tup.clone()), ty, "Output"); + } + else if( this->context.m_resolve.find_trait_impls(node.span(), lang_FnMut, trait_pp, ty, [&](auto impl, auto cmp) { + // TODO: Take the value of `cmp` into account + fcn_ret = impl.get_type("Output"); + return true; + //return cmp == ::HIR::Compare::Equal; + }) ) + { + DEBUG("-- Using FnMut"); + node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::FnMut; + + this->context.equate_types_assoc(node.span(), node.m_res_type, lang_FnMut, ::make_vec1(fcn_args_tup.clone()), ty, "Output"); + } + else + { + DEBUG("-- Using FnOnce (default)"); + node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::FnOnce; + + this->context.equate_types_assoc(node.span(), node.m_res_type, lang_FnOnce, ::make_vec1(fcn_args_tup.clone()), ty, "Output"); + } + + // If the return type wasn't found in the impls, emit it as a UFCS + if(fcn_ret == ::HIR::TypeRef()) + { + fcn_ret = ::HIR::TypeRef(::HIR::Path(::HIR::Path::Data::make_UfcsKnown({ + box$(ty.clone()), + // - Clone argument tuple, as it's stolen into cache below + ::HIR::GenericPath(lang_FnOnce, ::HIR::PathParams(fcn_args_tup.clone())), + "Output", + {} + }))); + } + } + else if( const auto* e = ty.m_data.opt_Borrow() ) + { + deref_count++; + ty_p = &this->context.get_type(*e->inner); + DEBUG("Deref " << ty << " -> " << *ty_p); + keep_looping = true; + continue; + } + else + { + if( !ty.m_data.is_Generic() ) + { + bool found = this->context.m_resolve.find_trait_impls_crate(node.span(), lang_FnOnce, trait_pp, ty, [&](auto impl, auto cmp)->bool { + if (cmp == ::HIR::Compare::Fuzzy) + TODO(node.span(), "Handle fuzzy match - " << impl); + + auto tup = impl.get_trait_ty_param(0); + if (!tup.m_data.is_Tuple()) + ERROR(node.span(), E0000, "FnOnce expects a tuple argument, got " << tup); + fcn_args_tup = mv$(tup); + fcn_ret = impl.get_type("Output"); + ASSERT_BUG(node.span(), fcn_ret != ::HIR::TypeRef(), "Impl didn't have a type for Output - " << impl); + return true; + }); + if (found) { + // Fill cache and leave the TU_MATCH + node.m_arg_types = mv$(fcn_args_tup.m_data.as_Tuple()); + node.m_arg_types.push_back(mv$(fcn_ret)); + node.m_trait_used = ::HIR::ExprNode_CallValue::TraitUsed::Unknown; + break; // leaves TU_MATCH + } + } + if( const auto* next_ty_p = this->context.m_resolve.autoderef(node.span(), ty, tmp_type) ) + { + DEBUG("Deref (autoderef) " << ty << " -> " << *next_ty_p); + deref_count++; + ty_p = next_ty_p; + keep_looping = true; + continue; + } + + // Didn't find anything. Error? + ERROR(node.span(), E0000, "Unable to find an implementation of Fn*" << trait_pp << " for " << this->context.m_ivars.fmt_type(ty)); + } + + node.m_arg_types = mv$(fcn_args_tup.m_data.as_Tuple()); + node.m_arg_types.push_back(mv$(fcn_ret)); + } } while( keep_looping ); if( deref_count > 0 ) diff --git a/src/hir_typeck/helpers.cpp b/src/hir_typeck/helpers.cpp index a3fe9b1c..c860c6ce 100644 --- a/src/hir_typeck/helpers.cpp +++ b/src/hir_typeck/helpers.cpp @@ -436,7 +436,7 @@ void HMTypeInferrence::add_ivars(::HIR::TypeRef& type) e.index = this->new_ivar(); this->get_type(type).m_data.as_Infer().ty_class = e.ty_class; this->mark_change(); - DEBUG("New ivar " << type); + DEBUG("New ivar " << type); } ), (Diverge, @@ -2020,7 +2020,7 @@ void TraitResolution::expand_associated_types_inplace__UfcsKnown(const Span& sp, ERROR(sp, E0000, "Couldn't find assocated type " << pe.item << " in " << pe.trait); if( impl.has_magic_params() ) { - } + } // TODO: What if there's multiple impls? DEBUG("Converted UfcsKnown - " << e.path << " = " << ty); diff --git a/src/hir_typeck/static.cpp b/src/hir_typeck/static.cpp index d8b692c9..0067eb7f 100644 --- a/src/hir_typeck/static.cpp +++ b/src/hir_typeck/static.cpp @@ -331,39 +331,39 @@ bool StaticTraitResolve::find_impl( if( m_crate.get_trait_by_path(sp, trait_path).m_is_marker ) { - struct H { - static bool find_impl__auto_trait_check(const StaticTraitResolve& self, - const Span& sp, const ::HIR::SimplePath& trait_path, const ::HIR::PathParams* trait_params, const ::HIR::TypeRef& type, - t_cb_find_impl found_cb, - const ::HIR::MarkerImpl& impl, bool& out_rv - ) - { - DEBUG("- Auto " << (impl.is_positive ? "Pos" : "Neg") - << " impl" << impl.m_params.fmt_args() << " " << trait_path << impl.m_trait_args << " for " << impl.m_type << " " << impl.m_params.fmt_bounds()); - if (impl.is_positive) - { - return self.find_impl__check_crate_raw(sp, trait_path, trait_params, type, impl.m_params, impl.m_trait_args, impl.m_type, - [&](auto impl_params, auto placeholders, auto cmp)->bool { - //rv = found_cb( ImplRef(impl_params, trait_path, impl, mv$(placeholders)), (cmp == ::HIR::Compare::Fuzzy) ); - out_rv = found_cb(ImplRef(&type, trait_params, &null_assoc), cmp == ::HIR::Compare::Fuzzy); - return out_rv; - }); - } - else - { - return self.find_impl__check_crate_raw(sp, trait_path, trait_params, type, impl.m_params, impl.m_trait_args, impl.m_type, - [&](auto impl_params, auto placeholders, auto cmp)->bool { - out_rv = false; - return true; - }); - } - } - }; + struct H { + static bool find_impl__auto_trait_check(const StaticTraitResolve& self, + const Span& sp, const ::HIR::SimplePath& trait_path, const ::HIR::PathParams* trait_params, const ::HIR::TypeRef& type, + t_cb_find_impl found_cb, + const ::HIR::MarkerImpl& impl, bool& out_rv + ) + { + DEBUG("- Auto " << (impl.is_positive ? "Pos" : "Neg") + << " impl" << impl.m_params.fmt_args() << " " << trait_path << impl.m_trait_args << " for " << impl.m_type << " " << impl.m_params.fmt_bounds()); + if (impl.is_positive) + { + return self.find_impl__check_crate_raw(sp, trait_path, trait_params, type, impl.m_params, impl.m_trait_args, impl.m_type, + [&](auto impl_params, auto placeholders, auto cmp)->bool { + //rv = found_cb( ImplRef(impl_params, trait_path, impl, mv$(placeholders)), (cmp == ::HIR::Compare::Fuzzy) ); + out_rv = found_cb(ImplRef(&type, trait_params, &null_assoc), cmp == ::HIR::Compare::Fuzzy); + return out_rv; + }); + } + else + { + return self.find_impl__check_crate_raw(sp, trait_path, trait_params, type, impl.m_params, impl.m_trait_args, impl.m_type, + [&](auto impl_params, auto placeholders, auto cmp)->bool { + out_rv = false; + return true; + }); + } + } + }; // Positive/negative impls bool rv = false; ret = this->m_crate.find_auto_trait_impls(trait_path, type, cb_ident, [&](const auto& impl)->bool { - return H::find_impl__auto_trait_check(*this, sp, trait_path, trait_params, type, found_cb, impl, rv); + return H::find_impl__auto_trait_check(*this, sp, trait_path, trait_params, type, found_cb, impl, rv); }); if(ret) return rv; @@ -592,9 +592,9 @@ bool StaticTraitResolve::find_impl__check_crate_raw( // Bounds for(const auto& bound : impl_params_def.m_bounds) { - if( const auto* ep = bound.opt_TraitBound() ) - { - const auto& e = *ep; + if( const auto* ep = bound.opt_TraitBound() ) + { + const auto& e = *ep; DEBUG("Trait bound " << e.type << " : " << e.trait); auto b_ty_mono = monomorphise_type_with(sp, e.type, cb_monomorph); @@ -665,11 +665,11 @@ bool StaticTraitResolve::find_impl__check_crate_raw( return false; } } - } - else // bound.opt_TraitBound() - { - // Ignore - } + } + else // bound.opt_TraitBound() + { + // Ignore + } } return found_cb( mv$(impl_params), mv$(placeholders), match ); @@ -705,9 +705,9 @@ bool StaticTraitResolve::find_impl__check_crate( }; // - If the type is a path (struct/enum/...), search for impls for all contained types. - if( const auto* ep = type.m_data.opt_Path() ) - { - const auto& e = *ep; + if( const auto* ep = type.m_data.opt_Path() ) + { + const auto& e = *ep; ::HIR::Compare res = ::HIR::Compare::Equal; TU_MATCH( ::HIR::Path::Data, (e.path.m_data), (pe), (Generic, @@ -828,9 +828,9 @@ bool StaticTraitResolve::find_impl__check_crate( ) ) return res; - } - else if( const auto* ep = type.m_data.opt_Tuple() ) - { + } + else if( const auto* ep = type.m_data.opt_Tuple() ) + { ::HIR::Compare res = ::HIR::Compare::Equal; for(const auto& sty : *ep) { @@ -839,11 +839,11 @@ bool StaticTraitResolve::find_impl__check_crate( return ::HIR::Compare::Unequal; } return res; - } + } else if( const auto* e = type.m_data.opt_Array() ) - { + { return type_impls_trait(*e->inner); - } + } // Otherwise, there's no negative so it must be positive else { return ::HIR::Compare::Equal; @@ -986,9 +986,9 @@ void StaticTraitResolve::expand_associated_types__UfcsKnown(const Span& sp, ::HI bool rv; bool assume_opaque = true; rv = this->iterate_bounds([&](const auto& b)->bool { - if( const auto* bep = b.opt_TraitBound() ) - { - const auto& be = *bep; + if( const auto* bep = b.opt_TraitBound() ) + { + const auto& be = *bep; DEBUG("Trait bound - " << be.type << " : " << be.trait); // 1. Check if the type matches // - TODO: This should be a fuzzier match? @@ -1040,20 +1040,20 @@ void StaticTraitResolve::expand_associated_types__UfcsKnown(const Span& sp, ::HI } // - Didn't match - } - else if( const auto* bep = b.opt_TypeEquality() ) - { - const auto& be = *bep; + } + else if( const auto* bep = b.opt_TypeEquality() ) + { + const auto& be = *bep; DEBUG("Equality - " << be.type << " = " << be.other_type); if( input == be.type ) { input = be.other_type.clone(); return true; } - } - else - { - // Nothing. - } + } + else + { + // Nothing. + } return false; }); if( rv ) { @@ -1670,6 +1670,7 @@ bool StaticTraitResolve::type_needs_drop_glue(const Span& sp, const ::HIR::TypeR TU_MATCH(::HIR::TypeRef::Data, (ty.m_data), (e), (Generic, + // TODO: Is this an error? return true; ), (Path, @@ -1797,7 +1798,7 @@ bool StaticTraitResolve::type_needs_drop_glue(const Span& sp, const ::HIR::TypeR (Tuple, for(const auto& ty : e) { - if( !type_needs_drop_glue(sp, ty) ) + if( type_needs_drop_glue(sp, ty) ) return true; } return false; diff --git a/src/include/debug.hpp b/src/include/debug.hpp index 2bbecb39..2f593cfb 100644 --- a/src/include/debug.hpp +++ b/src/include/debug.hpp @@ -42,10 +42,10 @@ class NullSink { public: NullSink() - {} + {} - template<typename T> - const NullSink& operator<<(const T&) const { return *this; } + template<typename T> + const NullSink& operator<<(const T&) const { return *this; } }; class TraceLog @@ -62,9 +62,9 @@ public: struct FmtLambda { ::std::function<void(::std::ostream&)> m_cb; - FmtLambda(::std::function<void(::std::ostream&)> cb): - m_cb(cb) - { } + FmtLambda(::std::function<void(::std::ostream&)> cb): + m_cb(cb) + { } friend ::std::ostream& operator<<(::std::ostream& os, const FmtLambda& x) { x.m_cb(os); return os; diff --git a/src/include/span.hpp b/src/include/span.hpp index 11ffc005..59c960fc 100644 --- a/src/include/span.hpp +++ b/src/include/span.hpp @@ -9,6 +9,7 @@ #include <rc_string.hpp> #include <functional> +#include <memory> enum ErrorType { @@ -30,6 +31,7 @@ struct ProtoSpan }; struct Span { + ::std::shared_ptr<Span> outer_span; // Expansion target for macros RcString filename; unsigned int start_line; diff --git a/src/include/synext_decorator.hpp b/src/include/synext_decorator.hpp index 4988c624..77b55710 100644 --- a/src/include/synext_decorator.hpp +++ b/src/include/synext_decorator.hpp @@ -63,19 +63,19 @@ extern void Register_Synext_Decorator_Static(DecoratorDef* def); struct DecoratorDef { - DecoratorDef* prev; - ::std::string name; - ::std::unique_ptr<ExpandDecorator> def; - DecoratorDef(::std::string name, ::std::unique_ptr<ExpandDecorator> def): - name(::std::move(name)), - def(::std::move(def)), - prev(nullptr) - { - Register_Synext_Decorator_Static(this); - } + DecoratorDef* prev; + ::std::string name; + ::std::unique_ptr<ExpandDecorator> def; + DecoratorDef(::std::string name, ::std::unique_ptr<ExpandDecorator> def): + prev(nullptr), + name(::std::move(name)), + def(::std::move(def)) + { + Register_Synext_Decorator_Static(this); + } }; -#define STATIC_DECORATOR(ident, _handler_class) static DecoratorDef s_register_##_handler_class ( ident, ::std::unique_ptr<ExpandDecorator>(new _handler_class()) ); +#define STATIC_DECORATOR(ident, _handler_class) static DecoratorDef s_register_##_handler_class ( ident, ::std::unique_ptr<ExpandDecorator>(new _handler_class()) ); #endif diff --git a/src/include/synext_macro.hpp b/src/include/synext_macro.hpp index c109b56e..0359d508 100644 --- a/src/include/synext_macro.hpp +++ b/src/include/synext_macro.hpp @@ -31,19 +31,19 @@ extern void Register_Synext_Macro_Static(MacroDef* def); struct MacroDef { - MacroDef* prev; - ::std::string name; - ::std::unique_ptr<ExpandProcMacro> def; - MacroDef(::std::string name, ::std::unique_ptr<ExpandProcMacro> def) : - name(::std::move(name)), - def(::std::move(def)), - prev(nullptr) - { - Register_Synext_Macro_Static(this); - } + MacroDef* prev; + ::std::string name; + ::std::unique_ptr<ExpandProcMacro> def; + MacroDef(::std::string name, ::std::unique_ptr<ExpandProcMacro> def) : + prev(nullptr), + name(::std::move(name)), + def(::std::move(def)) + { + Register_Synext_Macro_Static(this); + } }; -#define STATIC_MACRO(ident, _handler_class) static MacroDef s_register_##_handler_class(ident, ::std::unique_ptr<ExpandProcMacro>(new _handler_class())); +#define STATIC_MACRO(ident, _handler_class) static MacroDef s_register_##_handler_class(ident, ::std::unique_ptr<ExpandProcMacro>(new _handler_class())); #endif diff --git a/src/include/tagged_union.hpp b/src/include/tagged_union.hpp index fbd42a77..f30cb7f2 100644 --- a/src/include/tagged_union.hpp +++ b/src/include/tagged_union.hpp @@ -13,13 +13,13 @@ #include <string> #define TU_FIRST(a, ...) a -#define TU_EXP1(x) x +#define TU_EXP1(x) x #define TU_EXP(...) __VA_ARGS__ -#define TU_CASE_ITEM(src, mod, var, name) mod auto& name = src.as_##var(); (void)&name; -#define TU_CASE_BODY(class,var, ...) case class::var: { __VA_ARGS__ } break; -#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__) +#define TU_CASE_ITEM(src, mod, var, name) mod auto& name = src.as_##var(); (void)&name; +#define TU_CASE_BODY(class,var, ...) case class::var: { __VA_ARGS__ } break; +#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 @@ -31,7 +31,7 @@ #define TU_DISP5(n, a1,a2,a3, b1,b2 ) TU_DISP3(n, a1,a2,a3) TU_DISP2(n, b1,b2) #define TU_DISP6(n, a1,a2,a3, b1,b2,b3) TU_DISP3(n, a1,a2,a3) TU_DISP3(n, b1,b2,b3) #define TU_DISP7(n, a1,a2,a3,a4, b1,b2,b3 ) TU_DISP4(n, a1,a2,a3,a4) TU_DISP3(n, b1,b2,b3) -#define TU_DISP8(n, a1,a2,a3,a4, b1,b2,b3,b4) TU_DISP4(n, a1,a2,a3,a4) TU_DISP4(n, b1,b2,b3,b4) +#define TU_DISP8(n, a1,a2,a3,a4, b1,b2,b3,b4) TU_DISP4(n, a1,a2,a3,a4) TU_DISP4(n, b1,b2,b3,b4) #define TU_DISP9(n, a1,a2,a3,a4, b1,b2,b3,b4, c1 ) TU_DISP4(n, a1,a2,a3,a4) TU_DISP3(n, b1,b2,b3 ) TU_DISP2(n, b4,c1) #define TU_DISP10(n, a1,a2,a3,a4, b1,b2,b3,b4, c1,c2 ) TU_DISP4(n, a1,a2,a3,a4) TU_DISP4(n, b1,b2,b3,b4) TU_DISP2(n, c1,c2) #define TU_DISP11(n, a1,a2,a3,a4, b1,b2,b3,b4, c1,c2,c3 ) TU_DISP4(n, a1,a2,a3,a4) TU_DISP4(n, b1,b2,b3,b4) TU_DISP3(n, c1,c2,c3) @@ -48,7 +48,7 @@ #define TU_DISPO4(n, v, v2, v3, v4) n(v) n(v2) n(v3) n(v4) #define TU_DISPO5(n, a1,a2,a3, b1,b2 ) TU_DISPO3(n, a1,a2,a3) TU_DISPO2(n, b1,b2) #define TU_DISPO6(n, a1,a2,a3, b1,b2,b3) TU_DISPO3(n, a1,a2,a3) TU_DISPO3(n, b1,b2,b3) -#define TU_DISPO7(n, a1,a2,a3,a4, b1,b2,b3 ) TU_DISPO4(n, a1,a2,a3,a4) TU_DISPO3(n, b1,b2,b3) +#define TU_DISPO7(n, a1,a2,a3,a4, b1,b2,b3 ) TU_DISPO4(n, a1,a2,a3,a4) TU_DISPO3(n, b1,b2,b3) #define TU_DISPO8(n, a1,a2,a3,a4, b1,b2,b3,b4) TU_DISPO4(n, a1,a2,a3,a4) TU_DISPO4(n, b1,b2,b3,b4) #define TU_DISPO9(n, a1,a2,a3,a4, b1,b2,b3,b4, c1) TU_DISPO4(n, a1,a2,a3,a4) TU_DISPO3(n, b1,b2,b3) TU_DISPO2(n, b4,c1) #define TU_DISPO10(n, a1,a2,a3,a4, b1,b2,b3,b4, c1,c2) TU_DISPO4(n, a1,a2,a3,a4) TU_DISPO4(n, b1,b2,b3,b4) TU_DISPO2(n, c1,c2) @@ -61,8 +61,8 @@ #define TU_DISPA(n, a) n a #define TU_DISPA1(n, a, _1) TU_DISPA(n, (TU_EXP a, TU_EXP _1)) -#define TU_DISPA2(n, a, _1, _2) TU_DISPA(n, (TU_EXP a, TU_EXP _1)) TU_DISPA(n, (TU_EXP a, TU_EXP _2)) -#define TU_DISPA3(n, a, _1, _2, _3) TU_DISPA(n, (TU_EXP a, TU_EXP _1)) TU_DISPA(n, (TU_EXP a, TU_EXP _2)) TU_DISPA(n, (TU_EXP a, TU_EXP _3)) +#define TU_DISPA2(n, a, _1, _2) TU_DISPA(n, (TU_EXP a, TU_EXP _1)) TU_DISPA(n, (TU_EXP a, TU_EXP _2)) +#define TU_DISPA3(n, a, _1, _2, _3) TU_DISPA(n, (TU_EXP a, TU_EXP _1)) TU_DISPA(n, (TU_EXP a, TU_EXP _2)) TU_DISPA(n, (TU_EXP a, TU_EXP _3)) #define TU_DISPA4(n, a, a1,a2, b1,b2) TU_DISPA2(n,a, a1,a2) TU_DISPA2(n,a, b1,b2) #define TU_DISPA5(n, a, a1,a2,a3, b1,b2) TU_DISPA3(n,a, a1,a2,a3) TU_DISPA2(n,a, b1,b2) #define TU_DISPA6(n, a, a1,a2,a3, b1,b2,b3) TU_DISPA3(n,a, a1,a2,a3) TU_DISPA3(n,a, b1,b2,b3) diff --git a/src/macro_rules/eval.cpp b/src/macro_rules/eval.cpp index 18ef563e..fffe9b68 100644 --- a/src/macro_rules/eval.cpp +++ b/src/macro_rules/eval.cpp @@ -577,6 +577,7 @@ class MacroExpander: const RcString m_macro_filename; const ::std::string m_crate_name; + ::std::shared_ptr<Span> m_invocation_span; ParameterMappings m_mappings; MacroExpandState m_state; @@ -588,9 +589,10 @@ class MacroExpander: public: MacroExpander(const MacroExpander& x) = delete; - MacroExpander(const ::std::string& macro_name, const Ident::Hygiene& parent_hygiene, const ::std::vector<MacroExpansionEnt>& contents, ParameterMappings mappings, ::std::string crate_name): + MacroExpander(const ::std::string& macro_name, const Span& sp, const Ident::Hygiene& parent_hygiene, const ::std::vector<MacroExpansionEnt>& contents, ParameterMappings mappings, ::std::string crate_name): m_macro_filename( FMT("Macro:" << macro_name) ), m_crate_name( mv$(crate_name) ), + m_invocation_span( new Span(sp) ), m_mappings( mv$(mappings) ), m_state( contents, m_mappings ), m_hygiene( Ident::Hygiene::new_scope_chained(parent_hygiene) ) @@ -598,6 +600,7 @@ public: } Position getPosition() const override; + ::std::shared_ptr<Span> outerSpan() const override; Ident::Hygiene realGetHygiene() const override; Token realGetToken() override; }; @@ -618,9 +621,9 @@ bool Macro_TryPatternCap(TokenStream& lex, MacroPatEnt::Type type) switch(type) { case MacroPatEnt::PAT_TOKEN: - BUG(lex.getPosition(), ""); + BUG(lex.point_span(), ""); case MacroPatEnt::PAT_LOOP: - BUG(lex.getPosition(), ""); + BUG(lex.point_span(), ""); case MacroPatEnt::PAT_BLOCK: return LOOK_AHEAD(lex) == TOK_BRACE_OPEN || LOOK_AHEAD(lex) == TOK_INTERPOLATED_BLOCK; case MacroPatEnt::PAT_IDENT: @@ -651,7 +654,7 @@ bool Macro_TryPatternCap(TokenStream& lex, MacroPatEnt::Type type) case MacroPatEnt::PAT_ITEM: return is_token_item( LOOK_AHEAD(lex) ); } - BUG(lex.getPosition(), ""); + BUG(lex.point_span(), "Fell through"); } bool Macro_TryPattern(TokenStream& lex, const MacroPatEnt& pat) { @@ -680,9 +683,9 @@ InterpolatedFragment Macro_HandlePatternCap(TokenStream& lex, MacroPatEnt::Type switch(type) { case MacroPatEnt::PAT_TOKEN: - BUG(lex.getPosition(), "Encountered PAT_TOKEN when handling capture"); + BUG(lex.point_span(), "Encountered PAT_TOKEN when handling capture"); case MacroPatEnt::PAT_LOOP: - BUG(lex.getPosition(), "Encountered PAT_LOOP when handling capture"); + BUG(lex.point_span(), "Encountered PAT_LOOP when handling capture"); case MacroPatEnt::PAT_TT: if( GET_TOK(tok, lex) == TOK_EOF ) @@ -722,7 +725,7 @@ InterpolatedFragment Macro_HandlePatternCap(TokenStream& lex, MacroPatEnt::Type } /// Parse the input TokenTree according to the `macro_rules!` patterns and return a token stream of the replacement -::std::unique_ptr<TokenStream> Macro_InvokeRules(const char *name, const MacroRules& rules, TokenTree input, AST::Module& mod) +::std::unique_ptr<TokenStream> Macro_InvokeRules(const char *name, const MacroRules& rules, const Span& sp, TokenTree input, AST::Module& mod) { TRACE_FUNCTION_F("'" << name << "', " << input); @@ -741,7 +744,7 @@ InterpolatedFragment Macro_HandlePatternCap(TokenStream& lex, MacroPatEnt::Type // Run through the expansion counting the number of times each fragment is used Macro_InvokeRules_CountSubstUses(bound_tts, rule.m_contents); - TokenStream* ret_ptr = new MacroExpander(name, rules.m_hygiene, rule.m_contents, mv$(bound_tts), rules.m_source_crate); + TokenStream* ret_ptr = new MacroExpander(name, sp, rules.m_hygiene, rule.m_contents, mv$(bound_tts), rules.m_source_crate); return ::std::unique_ptr<TokenStream>( ret_ptr ); } @@ -1065,9 +1068,13 @@ void Macro_InvokeRules_CountSubstUses(ParameterMappings& bound_tts, const ::std: Position MacroExpander::getPosition() const { - // TODO: Return a far better span - invocaion location? + // TODO: Return the attached position of the last fetched token return Position(m_macro_filename, 0, m_state.top_pos()); } +::std::shared_ptr<Span> MacroExpander::outerSpan() const +{ + return m_invocation_span; +} Ident::Hygiene MacroExpander::realGetHygiene() const { if( m_ttstream ) diff --git a/src/macro_rules/macro_rules.hpp b/src/macro_rules/macro_rules.hpp index aed0e8ce..2a588a78 100644 --- a/src/macro_rules/macro_rules.hpp +++ b/src/macro_rules/macro_rules.hpp @@ -156,7 +156,7 @@ public: SERIALISABLE_PROTOTYPES(); }; -extern ::std::unique_ptr<TokenStream> Macro_InvokeRules(const char *name, const MacroRules& rules, TokenTree input, AST::Module& mod); +extern ::std::unique_ptr<TokenStream> Macro_InvokeRules(const char *name, const MacroRules& rules, const Span& sp, TokenTree input, AST::Module& mod); extern MacroRulesPtr Parse_MacroRules(TokenStream& lex); #endif // MACROS_HPP_INCLUDED diff --git a/src/macro_rules/parse.cpp b/src/macro_rules/parse.cpp index 8264180f..1b7509fd 100644 --- a/src/macro_rules/parse.cpp +++ b/src/macro_rules/parse.cpp @@ -82,7 +82,7 @@ public: else if( type == "item" ) ret.push_back( MacroPatEnt(name, idx, MacroPatEnt::PAT_ITEM) ); else - ERROR(lex.getPosition(), E0000, "Unknown fragment type '" << type << "'"); + ERROR(lex.point_span(), E0000, "Unknown fragment type '" << type << "'"); break; } case TOK_PAREN_OPEN: { auto subpat = Parse_MacroRules_Pat(lex, TOK_PAREN_OPEN, TOK_PAREN_CLOSE, names); @@ -155,7 +155,7 @@ public: { DEBUG("depth--"); if(depth == 0) - ERROR(lex.getPosition(), E0000, "Unmatched " << Token(close) << " in macro content"); + ERROR(lex.point_span(), E0000, "Unmatched " << Token(close) << " in macro content"); depth --; } @@ -206,7 +206,7 @@ public: auto name = tok.type() == TOK_IDENT ? tok.str() : FMT(tok); unsigned int idx = ::std::find(var_names.begin(), var_names.end(), name) - var_names.begin(); if( idx == var_names.size() ) - ERROR(lex.getPosition(), E0000, "Macro variable $" << name << " not found"); + ERROR(lex.point_span(), E0000, "Macro variable $" << name << " not found"); if( var_set_ptr ) { var_set_ptr->insert( ::std::make_pair(idx,true) ); } diff --git a/src/main.cpp b/src/main.cpp index ec4e9f38..c442fd94 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ #include "hir_expand/main_bindings.hpp" #include "mir/main_bindings.hpp" #include "trans/main_bindings.hpp" +#include "trans/target.hpp" #include "expand/cfg.hpp" @@ -63,6 +64,7 @@ void init_debug_list() g_debug_disable_map.insert( "Dump HIR" ); g_debug_disable_map.insert( "Lower MIR" ); g_debug_disable_map.insert( "MIR Validate" ); + g_debug_disable_map.insert( "MIR Validate Full Early" ); g_debug_disable_map.insert( "Dump MIR" ); g_debug_disable_map.insert( "Constant Evaluate Full" ); g_debug_disable_map.insert( "MIR Cleanup" ); @@ -142,6 +144,13 @@ struct ProgramParams ::std::set< ::std::string> features; + + struct { + bool disable_mir_optimisations = false; + bool full_validate = false; + bool full_validate_early = false; + } debug; + ProgramParams(int argc, char *argv[]); }; @@ -174,26 +183,10 @@ int main(int argc, char *argv[]) // Set up cfg values Cfg_SetValue("rust_compiler", "mrustc"); - // TODO: Target spec - Cfg_SetFlag("unix"); - Cfg_SetFlag("linux"); - Cfg_SetValue("target_os", "linux"); - Cfg_SetValue("target_family", "unix"); - Cfg_SetValue("target_pointer_width", "64"); - Cfg_SetValue("target_endian", "little"); - Cfg_SetValue("target_arch", "x86_64"); - Cfg_SetValue("target_env", "gnu"); - Cfg_SetValueCb("target_has_atomic", [](const ::std::string& s) { - if(s == "8") return true; // Has an atomic byte - if(s == "ptr") return true; // Has an atomic pointer-sized value - return false; - }); - Cfg_SetValueCb("target_feature", [](const ::std::string& s) { - return false; - }); Cfg_SetValueCb("feature", [¶ms](const ::std::string& s) { return params.features.count(s) != 0; }); + Target_SetCfg(); if( params.test_harness ) @@ -258,18 +251,18 @@ int main(int argc, char *argv[]) crate_name = ::std::string(params.infile.begin() + s, params.infile.begin() + e); for(auto& b : crate_name) { - if ('0' <= b && b <= '9') { - } - else if ('A' <= b && b <= 'Z') { - } - else if (b == '_') { - } - else if (b == '-') { - b = '_'; - } - else { - // TODO: Error? - } + if ('0' <= b && b <= '9') { + } + else if ('A' <= b && b <= 'Z') { + } + else if (b == '_') { + } + else if (b == '-') { + b = '_'; + } + else { + // TODO: Error? + } } } crate.m_crate_name = crate_name; @@ -448,16 +441,16 @@ int main(int argc, char *argv[]) CompilePhaseV("MIR Cleanup", [&]() { MIR_CleanupCrate(*hir_crate); }); - if( getenv("MRUSTC_FULL_VALIDATE_PREOPT") ) + if( params.debug.full_validate_early || getenv("MRUSTC_FULL_VALIDATE_PREOPT") ) { - CompilePhaseV("MIR Validate Full", [&]() { + CompilePhaseV("MIR Validate Full Early", [&]() { MIR_CheckCrate_Full(*hir_crate); }); } // Optimise the MIR CompilePhaseV("MIR Optimise", [&]() { - MIR_OptimiseCrate(*hir_crate); + MIR_OptimiseCrate(*hir_crate, params.debug.disable_mir_optimisations); }); CompilePhaseV("Dump MIR", [&]() { @@ -470,7 +463,7 @@ int main(int argc, char *argv[]) // - Exhaustive MIR validation (follows every code path and checks variable validity) // > DEBUGGING ONLY CompilePhaseV("MIR Validate Full", [&]() { - if( getenv("MRUSTC_FULL_VALIDATE") ) + if( params.debug.full_validate || getenv("MRUSTC_FULL_VALIDATE") ) MIR_CheckCrate_Full(*hir_crate); }); @@ -571,14 +564,14 @@ ProgramParams::ProgramParams(int argc, char *argv[]) if( arg[0] != '-' ) { - if (this->infile == "") - { - this->infile = arg; - } - else - { - // TODO: Error - } + if (this->infile == "") + { + this->infile = arg; + } + else + { + // TODO: Error + } } else if( arg[1] != '-' ) { @@ -610,6 +603,32 @@ ProgramParams::ProgramParams(int argc, char *argv[]) this->libraries.push_back( arg+1 ); } continue ; + case 'Z': { + ::std::string optname; + if( arg[1] == '\0' ) { + if( i == argc - 1) { + exit(1); + } + optname = argv[++i]; + } + else { + optname = arg+1; + } + + if( optname == "disable-mir-opt" ) { + this->debug.disable_mir_optimisations = true; + } + else if( optname == "full-validate" ) { + this->debug.full_validate = true; + } + else if( optname == "full-validate-early" ) { + this->debug.full_validate_early = true; + } + else { + ::std::cerr << "Unknown debug option: '" << optname << "'" << ::std::endl; + exit(1); + } + } continue; default: break; @@ -647,15 +666,15 @@ ProgramParams::ProgramParams(int argc, char *argv[]) } this->crate_path = argv[++i]; } - else if (strcmp(arg, "--out-dir") == 0) { - if (i == argc - 1) { - ::std::cerr << "Flag " << arg << " requires an argument" << ::std::endl; - exit(1); - } - this->output_dir = argv[++i]; - if (this->output_dir == "") { - // TODO: Error? - } + else if (strcmp(arg, "--out-dir") == 0) { + if (i == argc - 1) { + ::std::cerr << "Flag " << arg << " requires an argument" << ::std::endl; + exit(1); + } + this->output_dir = argv[++i]; + if (this->output_dir == "") { + // TODO: Error? + } if( this->output_dir.back() != '/' ) this->output_dir += '/'; } diff --git a/src/mir/check.cpp b/src/mir/check.cpp index 4b9dfd8b..7c0cd4d8 100644 --- a/src/mir/check.cpp +++ b/src/mir/check.cpp @@ -105,15 +105,13 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn Valid, }; State ret_state = State::Invalid; - ::std::vector<State> arguments; - ::std::vector<State> temporaries; - ::std::vector<State> variables; + ::std::vector<State> args; + ::std::vector<State> locals; ValStates() {} - ValStates(size_t n_args, size_t n_temps, size_t n_vars): - arguments(n_args, State::Valid), - temporaries(n_temps), - variables(n_vars) + ValStates(size_t n_args, size_t n_locals): + args(n_args, State::Valid), + locals(n_locals) { } @@ -144,22 +142,20 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn } } }; - fmt_val_range("arg", this->arguments); - fmt_val_range("tmp", this->temporaries); - fmt_val_range("var", this->variables); + fmt_val_range("arg", this->args); + fmt_val_range("_", this->locals); os << "}"; } bool operator==(const ValStates& x) const { - if( ret_state != x.ret_state ) return false; - if( arguments != x.arguments ) return false; - if( temporaries != x.temporaries ) return false; - if( variables != x.variables ) return false; + if( ret_state != x.ret_state ) return false; + if( args != x.args ) return false; + if( locals != x.locals ) return false; return true; } bool empty() const { - return arguments.empty() && temporaries.empty() && variables.empty(); + return locals.empty() && args.empty(); } bool merge(unsigned bb_idx, ValStates& other) @@ -178,9 +174,8 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn { bool rv = false; rv |= ValStates::merge_state(this->ret_state, other.ret_state); - rv |= ValStates::merge_lists(this->arguments , other.arguments); - rv |= ValStates::merge_lists(this->temporaries, other.temporaries); - rv |= ValStates::merge_lists(this->variables , other.variables); + rv |= ValStates::merge_lists(this->args , other.args ); + rv |= ValStates::merge_lists(this->locals, other.locals); return rv; } } @@ -194,42 +189,32 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn ret_state = is_valid ? State::Valid : State::Invalid; ), (Argument, - MIR_ASSERT(state, e.idx < this->arguments.size(), ""); - DEBUG("arg" << e.idx << " = " << (is_valid ? "Valid" : "Invalid")); - this->arguments[e.idx] = is_valid ? State::Valid : State::Invalid; + 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; ), - (Variable, - MIR_ASSERT(state, e < this->variables.size(), ""); - DEBUG("var" << e << " = " << (is_valid ? "Valid" : "Invalid")); - this->variables[e] = is_valid ? State::Valid : State::Invalid; - ), - (Temporary, - MIR_ASSERT(state, e.idx < this->temporaries.size(), ""); - DEBUG("tmp" << e.idx << " = " << (is_valid ? "Valid" : "Invalid")); - this->temporaries[e.idx] = is_valid ? State::Valid : State::Invalid; + (Local, + MIR_ASSERT(state, e < this->locals.size(), "Local index out of range"); + DEBUG("_" << e << " = " << (is_valid ? "Valid" : "Invalid")); + this->locals[e] = is_valid ? State::Valid : State::Invalid; ) ) } void ensure_valid(const ::MIR::TypeResolve& state, const ::MIR::LValue& lv) { TU_MATCH( ::MIR::LValue, (lv), (e), - (Variable, - MIR_ASSERT(state, e < this->variables.size(), ""); - if( this->variables[e] != State::Valid ) - MIR_BUG(state, "Use of non-valid variable - " << lv); - ), - (Temporary, - MIR_ASSERT(state, e.idx < this->temporaries.size(), ""); - if( this->temporaries[e.idx] != State::Valid ) - MIR_BUG(state, "Use of non-valid temporary - " << lv); + (Return, + if( this->ret_state != State::Valid ) + MIR_BUG(state, "Use of non-valid lvalue - " << lv); ), (Argument, - MIR_ASSERT(state, e.idx < this->arguments.size(), ""); - if( this->arguments[e.idx] != State::Valid ) - MIR_BUG(state, "Use of non-valid argument - " << lv); + MIR_ASSERT(state, e.idx < this->args.size(), "Arg index out of range"); + if( this->args[e.idx] != State::Valid ) + MIR_BUG(state, "Use of non-valid lvalue - " << lv); ), - (Return, - if( this->ret_state != State::Valid ) + (Local, + MIR_ASSERT(state, e < this->locals.size(), "Local index out of range"); + if( this->locals[e] != State::Valid ) MIR_BUG(state, "Use of non-valid lvalue - " << lv); ), (Static, @@ -309,7 +294,7 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn src_path.push_back(idx); to_visit_blocks.push_back( ToVisit { idx, mv$(src_path), mv$(vs) } ); }; - add_to_visit( 0, {}, ValStates { state.m_args.size(), fcn.temporaries.size(), fcn.named_variables.size() } ); + add_to_visit( 0, {}, ValStates { state.m_args.size(), fcn.locals.size() } ); while( to_visit_blocks.size() > 0 ) { auto block = to_visit_blocks.back().bb; @@ -430,12 +415,12 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn // Check if the return value has been set val_state.ensure_valid( state, ::MIR::LValue::make_Return({}) ); // Ensure that no other non-Copy values are valid - for(unsigned int i = 0; i < val_state.variables.size(); i ++) + for(unsigned int i = 0; i < val_state.locals.size(); i ++) { - if( val_state.variables[i] == ValStates::State::Invalid ) + if( val_state.locals[i] == ValStates::State::Invalid ) { } - else if( state.m_resolve.type_is_copy(state.sp, fcn.named_variables[i]) ) + else if( state.m_resolve.type_is_copy(state.sp, fcn.locals[i]) ) { } else @@ -467,6 +452,14 @@ void MIR_Validate_ValState(::MIR::TypeResolve& state, const ::MIR::Function& fcn add_to_visit( tgt, path, val_state ); } ), + (SwitchValue, + val_state.ensure_valid( state, e.val ); + for(const auto& tgt : e.targets) + { + add_to_visit( tgt, path, val_state ); + } + add_to_visit( e.def_target, path, val_state ); + ), (Call, if( e.fcn.is_Value() ) val_state.ensure_valid( state, e.fcn.as_Value() ); @@ -549,6 +542,12 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path PUSH_BB(e.targets[i], "Switch V" << i); } ), + (SwitchValue, + for(unsigned int i = 0; i < e.targets.size(); i++ ) { + PUSH_BB(e.targets[i], "SwitchValue " << i); + } + PUSH_BB(e.def_target, "SwitchValue def"); + ), (Call, PUSH_BB(e.ret_block, "Call ret"); PUSH_BB(e.panic_block, "Call panic"); @@ -689,7 +688,7 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path // TODO: Check suitability of source type (COMPLEX) ), (BinOp, - /* + /* ::HIR::TypeRef tmp_l, tmp_r; const auto& ty_l = state.get_lvalue_type(tmp_l, e.val_l); const auto& ty_r = state.get_lvalue_type(tmp_r, e.val_r); @@ -723,7 +722,7 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path ity_p = &*ty.m_data.as_Pointer().inner; else { MIR_BUG(state, "DstMeta requires a &-ptr as input, got " << ty); - } + } const auto& ity = *ity_p; if( ity.m_data.is_Generic() ) ; @@ -757,7 +756,7 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path ity_p = &*ty.m_data.as_Pointer().inner; else { MIR_BUG(state, "DstPtr requires a &-ptr as input, got " << ty); - } + } const auto& ity = *ity_p; if( ity.m_data.is_Slice() ) ; @@ -785,7 +784,7 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path ity_p = &*te->inner; else { MIR_BUG(state, "DstMeta requires a pointer as output, got " << ty); - } + } assert(ity_p); auto meta = get_metadata_type(state, *ity_p); if( meta == ::HIR::TypeRef() ) @@ -844,6 +843,9 @@ void MIR_Validate(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path (Switch, // Check that the condition is an enum ), + (SwitchValue, + // Check that the condition's type matches the values + ), (Call, if( e.fcn.is_Value() ) { diff --git a/src/mir/check_full.cpp b/src/mir/check_full.cpp index cacd9bef..1f86c40a 100644 --- a/src/mir/check_full.cpp +++ b/src/mir/check_full.cpp @@ -24,6 +24,11 @@ namespace // other = 1-based index into `inner_states` unsigned int index; + explicit State(const State&) = default; + State(State&& x) = default; + State& operator=(const State&) = delete; + State& operator=(State&& ) = default; + State(): index(0) {} State(bool valid): index(valid ? ~0u : 0) {} State(size_t idx): @@ -51,20 +56,20 @@ namespace struct StateFmt { const ValueStates& vss; - State s; - StateFmt( const ValueStates& vss, State s ): + const State& s; + StateFmt( const ValueStates& vss, const State& s ): vss(vss), s(s) {} }; +::std::ostream& operator<<(::std::ostream& os, const StateFmt& x); namespace { struct ValueStates { - ::std::vector<State> vars; - ::std::vector<State> temporaries; - ::std::vector<State> arguments; State return_value; + ::std::vector<State> args; + ::std::vector<State> locals; ::std::vector<bool> drop_flags; ::std::vector< ::std::vector<State> > inner_states; @@ -73,8 +78,27 @@ namespace ValueStates clone() const { + struct H { + static ::std::vector<State> clone_state_list(const ::std::vector<State>& l) { + ::std::vector<State> rv; + rv.reserve(l.size()); + for(const auto& s : l) + rv.push_back( State(s) ); + return rv; + } + }; + ValueStates rv; + rv.return_value = State(this->return_value); + rv.args = H::clone_state_list(this->args); + rv.locals = H::clone_state_list(this->locals); + rv.drop_flags = this->drop_flags; + rv.inner_states.reserve( this->inner_states.size() ); + for(const auto& isl : this->inner_states) + rv.inner_states.push_back( H::clone_state_list(isl) ); + rv.bb_path = this->bb_path; return *this; } + bool is_equivalent_to(const ValueStates& x) const { struct H { @@ -113,22 +137,18 @@ namespace return false; if( ! H::equal(*this, return_value, x, x.return_value) ) return false; - assert(vars.size() == x.vars.size()); - for(size_t i = 0; i < vars.size(); i ++) - { - if( ! H::equal(*this, vars[i], x, x.vars[i]) ) - return false; - } - assert(temporaries.size() == x.temporaries.size()); - for(size_t i = 0; i < temporaries.size(); i ++) + + assert(args.size() == x.args.size()); + for(size_t i = 0; i < args.size(); i ++) { - if( ! H::equal(*this, temporaries[i], x, x.temporaries[i]) ) + if( ! H::equal(*this, args[i], x, x.args[i]) ) return false; } - assert(arguments.size() == x.arguments.size()); - for(size_t i = 0; i < arguments.size(); i ++) + + assert(locals.size() == x.locals.size()); + for(size_t i = 0; i < locals.size(); i ++) { - if( ! H::equal(*this, arguments[i], x, x.arguments[i]) ) + if( ! H::equal(*this, locals[i], x, x.locals[i]) ) return false; } return true; @@ -147,7 +167,7 @@ namespace } void ensure_lvalue_valid(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const { - auto vs = get_lvalue_state(mir_res, lv); + const auto& vs = get_lvalue_state(mir_res, lv); ::std::vector<unsigned int> path; ensure_valid(mir_res, lv, vs, path); } @@ -371,29 +391,46 @@ namespace Marker m; m.used.resize(this->inner_states.size(), false); - for(const auto& s : this->vars) - m.mark_from_state(*this, s); - for(const auto& s : this->temporaries) + m.mark_from_state(*this, this->return_value); + for(const auto& s : this->args) m.mark_from_state(*this, s); - for(const auto& s : this->arguments) + for(const auto& s : this->locals) m.mark_from_state(*this, s); - m.mark_from_state(*this, this->return_value); } private: - State allocate_composite(unsigned int n_fields, State basis) + ::std::vector<State>& allocate_composite_int(State& out_state) { - assert(n_fields > 0); + // 1. Search for an unused (empty) slot for(size_t i = 0; i < this->inner_states.size(); i ++) { if( this->inner_states[i].size() == 0 ) { - inner_states[i] = ::std::vector<State>(n_fields, basis); - return State(i); + out_state = State(i); + return inner_states[i]; } } + // 2. If none avaliable, allocate a new slot auto idx = inner_states.size(); - inner_states.push_back( ::std::vector<State>(n_fields, basis) ); - return State(idx); + inner_states.push_back({}); + out_state = State(idx); + return inner_states.back(); + } + State allocate_composite(unsigned int n_fields, const State& basis) + { + assert(n_fields > 0); + assert(!basis.is_composite()); + + State rv; + auto& sub_states = allocate_composite_int(rv); + assert(sub_states.size() == 0); + + sub_states.reserve(n_fields); + while(n_fields--) + { + sub_states.push_back( State(basis) ); + } + + return rv; } public: @@ -407,26 +444,24 @@ namespace MIR_ASSERT(mir_res, vs.index-1 < this->inner_states.size(), ""); return this->inner_states.at( vs.index - 1 ); } - State get_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const + const State& get_lvalue_state(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& lv) const { TU_MATCHA( (lv), (e), - (Variable, - return vars.at(e); - ), - (Temporary, - return temporaries.at(e.idx); + (Return, + return return_value; ), (Argument, - return arguments.at(e.idx); + return args.at(e.idx); ), - (Static, - return State(true); + (Local, + return locals.at(e); ), - (Return, - return return_value; + (Static, + static State state_of_static(true); + return state_of_static; ), (Field, - auto vs = get_lvalue_state(mir_res, *e.val); + const auto& vs = get_lvalue_state(mir_res, *e.val); if( vs.is_composite() ) { const auto& states = this->get_composite(mir_res, vs); @@ -439,7 +474,7 @@ namespace } ), (Deref, - auto vs = get_lvalue_state(mir_res, *e.val); + const auto& vs = get_lvalue_state(mir_res, *e.val); if( vs.is_composite() ) { MIR_TODO(mir_res, "Deref with composite state"); @@ -450,18 +485,20 @@ namespace } ), (Index, - auto vs_v = get_lvalue_state(mir_res, *e.val); - auto vs_i = get_lvalue_state(mir_res, *e.idx); + 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()); + //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, - auto vs_v = get_lvalue_state(mir_res, *e.val); + 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"); + MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size - " << StateFmt(*this, vs_v)); return states[0]; } else @@ -473,32 +510,45 @@ namespace throw ""; } + void clear_state(const ::MIR::TypeResolve& mir_res, State& s) { + if(s.is_composite()) { + auto& sub_states = this->get_composite(mir_res, s); + for(auto& ss : sub_states) + this->clear_state(mir_res, ss); + sub_states.clear(); + } + } + 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), - (Variable, - vars.at(e) = new_vs; - ), - (Temporary, - temporaries.at(e.idx) = new_vs; + (Return, + this->clear_state(mir_res, return_value); + return_value = mv$(new_vs); ), (Argument, - arguments.at(e.idx) = new_vs; + auto& slot = args.at(e.idx); + this->clear_state(mir_res, slot); + slot = mv$(new_vs); + ), + (Local, + auto& slot = locals.at(e); + this->clear_state(mir_res, slot); + slot = mv$(new_vs); ), (Static, // Ignore. ), - (Return, - return_value = new_vs; - ), (Field, - auto cur_vs = get_lvalue_state(mir_res, *e.val); + 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; if( !cur_vs.is_composite() ) { ::HIR::TypeRef tmp; @@ -526,41 +576,55 @@ namespace else { MIR_BUG(mir_res, "Unknown type being accessed with Field - " << ty); } - cur_vs = this->allocate_composite(n_fields, cur_vs); - set_lvalue_state(mir_res, *e.val, cur_vs); + + 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); } // Get composite state and assign into it - auto& states = this->get_composite(mir_res, cur_vs); + auto& states = *states_p; MIR_ASSERT(mir_res, e.field_index < states.size(), "Field index out of range"); - states[e.field_index] = new_vs; + this->clear_state(mir_res, states[e.field_index]); + states[e.field_index] = mv$(new_vs); } ), (Deref, - auto cur_vs = get_lvalue_state(mir_res, *e.val); + 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; if( !cur_vs.is_composite() ) { //::HIR::TypeRef tmp; //const auto& ty = mir_res.get_lvalue_type(tmp, *e.val); // TODO: Should this check if the type is Box? - cur_vs = this->allocate_composite(2, cur_vs); - set_lvalue_state(mir_res, *e.val, cur_vs); + 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); } // Get composite state and assign into it - auto& states = this->get_composite(mir_res, cur_vs); + auto& states = *states_p; MIR_ASSERT(mir_res, states.size() == 2, "Deref with invalid state list size"); - states[1] = new_vs; + this->clear_state(mir_res, states[1]); + states[1] = mv$(new_vs); } ), (Index, - auto vs_v = get_lvalue_state(mir_res, *e.val); - auto vs_i = get_lvalue_state(mir_res, *e.idx); + 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(), ""); @@ -570,22 +634,30 @@ namespace // NOTE: Ignore ), (Downcast, - auto cur_vs = get_lvalue_state(mir_res, *e.val); + 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; if( !cur_vs.is_composite() ) { - cur_vs = this->allocate_composite(1, cur_vs); - set_lvalue_state(mir_res, *e.val, cur_vs); + 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); + } + else + { + states_p = &this->get_composite(mir_res, cur_vs); } + // Get composite state and assign into it - auto& states = this->get_composite(mir_res, cur_vs); - MIR_ASSERT(mir_res, states.size() == 1, "Downcast on composite of invalid size"); - states[0] = new_vs; + 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)); + this->clear_state(mir_res, states[0]); + states[0] = mv$(new_vs); } ) ) @@ -648,12 +720,10 @@ namespace std { os << "ValueStates(path=[" << x.bb_path << "]"; print_val(",rv", x.return_value); - for(unsigned int i = 0; i < x.arguments.size(); i ++) - print_val(FMT_CB(ss, ss << ",a" << i;), x.arguments[i]); - for(unsigned int i = 0; i < x.vars.size(); i ++) - print_val(FMT_CB(ss, ss << ",_" << i;), x.vars[i]); - for(unsigned int i = 0; i < x.temporaries.size(); i ++) - print_val(FMT_CB(ss, ss << ",t" << i;), x.temporaries[i]); + for(unsigned int i = 0; i < x.args.size(); i ++) + print_val(FMT_CB(ss, ss << ",a" << i;), x.args[i]); + for(unsigned int i = 0; i < x.locals.size(); i ++) + print_val(FMT_CB(ss, ss << ",_" << i;), x.locals[i]); for(unsigned int i = 0; i < x.drop_flags.size(); i++) if(x.drop_flags[i]) os << ",df" << i; @@ -677,9 +747,17 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio DEBUG(lifetimes.m_block_offsets); ValueStates state; - state.arguments.resize( mir_res.m_args.size(), State(true) ); - state.vars.resize( fcn.named_variables.size() ); - state.temporaries.resize( fcn.temporaries.size() ); + struct H { + static ::std::vector<State> make_list(size_t n, bool pop) { + ::std::vector<State> rv; + rv.reserve(n); + while(n--) + rv.push_back(State(pop)); + return rv; + } + }; + state.args = H::make_list(mir_res.m_args.size(), true); + state.locals = H::make_list(fcn.locals.size(), false); state.drop_flags = fcn.drop_flags; ::std::vector< ::std::pair<unsigned int, ValueStates> > todo_queue; @@ -692,46 +770,25 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio // Mask off any values which aren't valid in the first statement of this block { - for(unsigned i = 0; i < state.vars.size(); i ++) - { - /*if( !variables_copy[i] ) - { - // Not Copy, don't apply masking - } - else*/ if( ! state.vars[i].is_valid() ) - { - // Already invalid - } - else if( lifetimes.var_valid(i, cur_block, 0) ) - { - // Expected to be valid in this block, leave as-is - } - else - { - // Copy value not used at/after this block, mask to false - DEBUG("BB" << cur_block << " - var$" << i << " - Outside lifetime, discard"); - state.vars[i] = State(false); - } - } - for(unsigned i = 0; i < state.temporaries.size(); i ++) + for(unsigned i = 0; i < state.locals.size(); i ++) { /*if( !variables_copy[i] ) { // Not Copy, don't apply masking } - else*/ if( ! state.temporaries[i].is_valid() ) + else*/ if( ! state.locals[i].is_valid() ) { // Already invalid } - else if( lifetimes.tmp_valid(i, cur_block, 0) ) + else if( lifetimes.slot_valid(i, cur_block, 0) ) { // Expected to be valid in this block, leave as-is } else { // Copy value not used at/after this block, mask to false - DEBUG("BB" << cur_block << " - tmp$" << i << " - Outside lifetime, discard"); - state.temporaries[i] = State(false); + DEBUG("BB" << cur_block << " - _" << i << " - Outside lifetime, discard"); + state.locals[i] = State(false); } } } @@ -755,9 +812,9 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio TU_MATCHA( (blk.statements[i]), (se), (Assign, if( ENABLE_LEAK_DETECTOR ) - { - // TODO: Check if the target isn't valid. Allow if either invaid, or too complex to know. - } + { + // TODO: Check if the target isn't valid. Allow if either invaid, or too complex to know. + } TU_MATCHA( (se.src), (ve), (Use, state.move_lvalue(mir_res, ve); @@ -845,7 +902,7 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio { // HACK: A move out of a Box generates the following pattern: `[[[[X_]]X]]` // - Ensure that that is the pattern we're seeing here. - auto vs = state.get_lvalue_state(mir_res, se.slot); + const auto& vs = state.get_lvalue_state(mir_res, se.slot); MIR_ASSERT(mir_res, vs.index != ~0u, "Shallow drop on fully-valid value - " << se.slot); @@ -910,11 +967,11 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio } } }; - for(unsigned i = 0; i < state.arguments.size(); i ++ ) { - ensure_dropped(state.arguments[i], ::MIR::LValue::make_Argument({i})); + for(unsigned i = 0; i < state.locals.size(); i ++ ) { + ensure_dropped(state.locals[i], ::MIR::LValue::make_Local(i)); } - for(unsigned i = 0; i < state.vars.size(); i ++ ) { - ensure_dropped(state.vars[i], ::MIR::LValue::make_Variable(i)); + for(unsigned i = 0; i < state.args.size(); i ++ ) { + ensure_dropped(state.args[i], ::MIR::LValue::make_Argument({i})); } } ), @@ -938,6 +995,14 @@ void MIR_Validate_FullValState(::MIR::TypeResolve& mir_res, const ::MIR::Functio todo_queue.push_back( ::std::make_pair(te.targets[i], i == te.targets.size()-1 ? mv$(state) : state.clone()) ); } ), + (SwitchValue, + state.ensure_lvalue_valid(mir_res, te.val); + for(size_t i = 0; i < te.targets.size(); i ++) + { + todo_queue.push_back( ::std::make_pair(te.targets[i], state.clone()) ); + } + todo_queue.push_back( ::std::make_pair(te.def_target, mv$(state)) ); + ), (Call, if(const auto* e = te.fcn.opt_Value()) { diff --git a/src/mir/cleanup.cpp b/src/mir/cleanup.cpp index 17dca948..f26f2bdd 100644 --- a/src/mir/cleanup.cpp +++ b/src/mir/cleanup.cpp @@ -32,8 +32,8 @@ struct MirMutator ::MIR::LValue new_temporary(::HIR::TypeRef ty) { - auto rv = ::MIR::LValue::make_Temporary({ static_cast<unsigned int>(m_fcn.temporaries.size()) }); - m_fcn.temporaries.push_back( mv$(ty) ); + auto rv = ::MIR::LValue::make_Local( static_cast<unsigned int>(m_fcn.locals.size()) ); + m_fcn.locals.push_back( mv$(ty) ); return rv; } @@ -831,15 +831,13 @@ 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), - (Variable, - ), - (Temporary, + (Return, ), (Argument, ), - (Static, + (Local, ), - (Return, + (Static, ), (Field, MIR_Cleanup_LValue(state, mutator, *le.val); @@ -1105,6 +1103,9 @@ void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, (Switch, MIR_Cleanup_LValue(state, mutator, e.val); ), + (SwitchValue, + MIR_Cleanup_LValue(state, mutator, e.val); + ), (Call, MIR_Cleanup_LValue(state, mutator, e.ret_val); if( e.fcn.is_Value() ) { diff --git a/src/mir/dump.cpp b/src/mir/dump.cpp index b4177295..a029023a 100644 --- a/src/mir/dump.cpp +++ b/src/mir/dump.cpp @@ -25,13 +25,9 @@ namespace { void dump_mir(const ::MIR::Function& fcn) { - for(unsigned int i = 0; i < fcn.named_variables.size(); i ++) + for(size_t i = 0; i < fcn.locals.size(); i ++) { - m_os << indent() << "let _#" << i << ": " << fcn.named_variables[i] << ";\n"; - } - for(unsigned int i = 0; i < fcn.temporaries.size(); i ++) - { - m_os << indent() << "let tmp$" << i << ": " << fcn.temporaries[i] << ";\n"; + m_os << indent() << "let _$" << i << ": " << fcn.locals[i] << ";\n"; } for(unsigned int i = 0; i < fcn.drop_flags.size(); i ++) { @@ -102,10 +98,8 @@ namespace { ), (ScopeEnd, m_os << "// Scope End: "; - for(auto idx : e.vars) - m_os << "var$" << idx << ","; - for(auto idx : e.tmps) - m_os << "tmp$" << idx << ","; + for(auto idx : e.slots) + m_os << "_$" << idx << ","; m_os << "\n"; ) ) @@ -137,6 +131,24 @@ namespace { m_os << j << " => bb" << e.targets[j] << ", "; m_os << "}\n"; ), + (SwitchValue, + m_os << "switch " << FMT_M(e.val) << " {"; + TU_MATCHA( (e.values), (ve), + (Unsigned, + for(unsigned int j = 0; j < e.targets.size(); j ++) + m_os << ve[j] << " => bb" << e.targets[j] << ", "; + ), + (Signed, + for(unsigned int j = 0; j < e.targets.size(); j ++) + m_os << (ve[j] >= 0 ? "+" : "") << ve[j] << " => bb" << e.targets[j] << ", "; + ), + (String, + for(unsigned int j = 0; j < e.targets.size(); j ++) + m_os << "\"" << ve[j] << "\" => bb" << e.targets[j] << ", "; + ) + ) + m_os << "_ => bb" << e.def_target << "}\n"; + ), (Call, m_os << FMT_M(e.ret_val) << " = "; TU_MATCHA( (e.fcn), (e2), @@ -165,21 +177,18 @@ namespace { } void fmt_val(::std::ostream& os, const ::MIR::LValue& lval) { TU_MATCHA( (lval), (e), - (Variable, - os << "_#" << e; - ), - (Temporary, - os << "tmp$" << e.idx; + (Return, + os << "RETURN"; ), (Argument, os << "arg$" << e.idx; ), + (Local, + os << "_$" << e; + ), (Static, os << e; ), - (Return, - os << "RETURN"; - ), (Field, os << "("; fmt_val(os, *e.val); diff --git a/src/mir/from_hir.cpp b/src/mir/from_hir.cpp index d9796aed..786c2243 100644 --- a/src/mir/from_hir.cpp +++ b/src/mir/from_hir.cpp @@ -175,16 +175,16 @@ namespace { switch( pat.m_binding.m_type ) { case ::HIR::PatternBinding::Type::Move: - m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(pat.m_binding.m_slot), mv$(lval) ); + m_builder.push_stmt_assign( sp, m_builder.get_variable(sp, pat.m_binding.m_slot), mv$(lval) ); break; case ::HIR::PatternBinding::Type::Ref: if(m_borrow_raise_target) { DEBUG("- Raising destructure borrow of " << lval << " to scope " << *m_borrow_raise_target); - m_builder.raise_variables(sp, lval, *m_borrow_raise_target); + m_builder.raise_temporaries(sp, lval, *m_borrow_raise_target); } - m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(pat.m_binding.m_slot), ::MIR::RValue::make_Borrow({ + m_builder.push_stmt_assign( sp, m_builder.get_variable(sp, pat.m_binding.m_slot), ::MIR::RValue::make_Borrow({ 0, ::HIR::BorrowType::Shared, mv$(lval) }) ); break; @@ -192,9 +192,9 @@ namespace { if(m_borrow_raise_target) { DEBUG("- Raising destructure borrow of " << lval << " to scope " << *m_borrow_raise_target); - m_builder.raise_variables(sp, lval, *m_borrow_raise_target); + m_builder.raise_temporaries(sp, lval, *m_borrow_raise_target); } - m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(pat.m_binding.m_slot), ::MIR::RValue::make_Borrow({ + m_builder.push_stmt_assign( sp, m_builder.get_variable(sp, pat.m_binding.m_slot), ::MIR::RValue::make_Borrow({ 0, ::HIR::BorrowType::Unique, mv$(lval) }) ); break; @@ -390,7 +390,7 @@ namespace { ); // Construct fat pointer - m_builder.push_stmt_assign( sp, ::MIR::LValue::make_Variable(e.extra_bind.m_slot), ::MIR::RValue::make_MakeDst({ mv$(ptr_val), mv$(len_val) }) ); + m_builder.push_stmt_assign( sp, m_builder.get_variable(sp, e.extra_bind.m_slot), ::MIR::RValue::make_MakeDst({ mv$(ptr_val), mv$(len_val) }) ); } if( e.trailing.size() > 0 ) { @@ -536,7 +536,7 @@ namespace { if( node.m_pattern.m_binding.is_valid() && node.m_pattern.m_data.is_Any() && node.m_pattern.m_binding.m_type == ::HIR::PatternBinding::Type::Move ) { - m_builder.push_stmt_assign( node.span(), ::MIR::LValue::make_Variable(node.m_pattern.m_binding.m_slot), mv$(res) ); + m_builder.push_stmt_assign( node.span(), m_builder.get_variable(node.span(), node.m_pattern.m_binding.m_slot), mv$(res) ); } else { @@ -548,8 +548,8 @@ namespace { void visit(::HIR::ExprNode_Loop& node) override { TRACE_FUNCTION_FR("_Loop", "_Loop"); - auto loop_body_scope = m_builder.new_scope_loop(node.span()); auto loop_block = m_builder.new_bb_linked(); + auto loop_body_scope = m_builder.new_scope_loop(node.span()); auto loop_next = m_builder.new_bb_unlinked(); auto loop_tmp_scope = m_builder.new_scope_temp(node.span()); @@ -663,7 +663,7 @@ namespace { if( m_builder.block_active() ) { auto res = m_builder.get_result(arm.m_code->span()); - m_builder.raise_variables( arm.m_code->span(), res, scope, /*to_above=*/true); + m_builder.raise_temporaries( arm.m_code->span(), res, scope, /*to_above=*/true); m_builder.set_result(arm.m_code->span(), mv$(res)); m_builder.terminate_scope( node.span(), mv$(tmp_scope) ); @@ -1157,7 +1157,7 @@ namespace { if( m_borrow_raise_target ) { DEBUG("- Raising borrow to scope " << *m_borrow_raise_target); - m_builder.raise_variables(node.span(), val, *m_borrow_raise_target); + m_builder.raise_temporaries(node.span(), val, *m_borrow_raise_target); } m_builder.set_result( node.span(), ::MIR::RValue::make_Borrow({ 0, node.m_type, mv$(val) }) ); @@ -1438,7 +1438,7 @@ namespace { if( m_borrow_raise_target && m_in_borrow ) { DEBUG("- Raising deref in borrow to scope " << *m_borrow_raise_target); - m_builder.raise_variables(node.span(), val, *m_borrow_raise_target); + m_builder.raise_temporaries(node.span(), val, *m_borrow_raise_target); } @@ -2057,7 +2057,7 @@ namespace { void visit(::HIR::ExprNode_Variable& node) override { TRACE_FUNCTION_F("_Variable - " << node.m_name << " #" << node.m_slot); - m_builder.set_result( node.span(), ::MIR::LValue::make_Variable(node.m_slot) ); + m_builder.set_result( node.span(), m_builder.get_variable(node.span(), node.m_slot) ); } void visit(::HIR::ExprNode_StructLiteral& node) override @@ -2239,9 +2239,9 @@ namespace { TRACE_FUNCTION; ::MIR::Function fcn; - fcn.named_variables.reserve(ptr.m_bindings.size()); + fcn.locals.reserve(ptr.m_bindings.size()); for(const auto& t : ptr.m_bindings) - fcn.named_variables.push_back( t.clone() ); + fcn.locals.push_back( t.clone() ); // Scope ensures that builder cleanup happens before `fcn` is moved { @@ -2252,8 +2252,15 @@ namespace { unsigned int i = 0; for( const auto& arg : args ) { - ev.define_vars_from(ptr->span(), arg.first); - ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::make_Argument({i})); + const auto& pat = arg.first; + if( pat.m_binding.is_valid() && pat.m_binding.m_type == ::HIR::PatternBinding::Type::Move ) + { + } + else + { + ev.define_vars_from(ptr->span(), arg.first); + ev.destructure_from(ptr->span(), arg.first, ::MIR::LValue::make_Argument({i})); + } i ++; } @@ -2262,8 +2269,14 @@ namespace { root_node.visit( ev ); } + // NOTE: Can't clean up yet, as consteval isn't done + //MIR_Cleanup(resolve, path, fcn, args, ptr->m_res_type); MIR_Validate(resolve, path, fcn, args, ptr->m_res_type); + if( getenv("MRUSTC_VALIDATE_FULL_EARLY") ) { + MIR_Validate_Full(resolve, path, fcn, args, ptr->m_res_type); + } + return ::MIR::FunctionPointer(new ::MIR::Function(mv$(fcn))); } diff --git a/src/mir/from_hir.hpp b/src/mir/from_hir.hpp index 6b10d5bd..c4be91a2 100644 --- a/src/mir/from_hir.hpp +++ b/src/mir/from_hir.hpp @@ -79,20 +79,18 @@ extern ::std::ostream& operator<<(::std::ostream& os, const VarState& x); struct SplitArm { bool has_early_terminated = false; bool always_early_terminated = false; // Populated on completion - ::std::map<unsigned int, VarState> var_states; - ::std::map<unsigned int, VarState> tmp_states; + ::std::map<unsigned int, VarState> states; + ::std::map<unsigned int, VarState> arg_states; }; struct SplitEnd { - ::std::map<unsigned int, VarState> var_states; - ::std::map<unsigned int, VarState> tmp_states; + ::std::map<unsigned int, VarState> states; + ::std::map<unsigned int, VarState> arg_states; }; -TAGGED_UNION(ScopeType, Variables, - (Variables, struct { - ::std::vector<unsigned int> vars; // List of owned variables - }), - (Temporaries, struct { - ::std::vector<unsigned int> temporaries; // Controlled temporaries +TAGGED_UNION(ScopeType, Owning, + (Owning, struct { + bool is_temporary; + ::std::vector<unsigned int> slots; // List of owned variables }), (Split, struct { bool end_state_valid = false; @@ -101,10 +99,18 @@ TAGGED_UNION(ScopeType, Variables, }), (Loop, struct { // NOTE: This contains the original state for variables changed after `exit_state_valid` is true - ::std::map<unsigned int,VarState> changed_vars; - ::std::map<unsigned int,VarState> changed_tmps; + ::std::map<unsigned int,VarState> changed_slots; + ::std::map<unsigned int,VarState> changed_args; bool exit_state_valid; SplitEnd exit_state; + // TODO: Any drop flags allocated in the loop must be re-initialised at the start of the loop (or before a loopback) + ::MIR::BasicBlockId entry_bb; + ::std::vector<unsigned> drop_flags; + }), + // State which should end up with no mutation of variable states + (Freeze, struct { + //::std::map<unsigned int,VarState> changed_slots; + //::std::map<unsigned int,VarState> changed_args; }) ); @@ -134,11 +140,13 @@ class MirBuilder ::MIR::RValue m_result; bool m_result_valid; - // TODO: Extra information. + // TODO: Extra information (e.g. mutability) VarState m_return_state; ::std::vector<VarState> m_arg_states; - ::std::vector<VarState> m_variable_states; - ::std::vector<VarState> m_temporary_states; + ::std::vector<VarState> m_slot_states; + size_t m_first_temp_idx; + + ::std::map<unsigned,unsigned> m_var_arg_mappings; struct ScopeDef { @@ -177,6 +185,15 @@ public: const ::HIR::TypeRef* is_type_owned_box(const ::HIR::TypeRef& ty) const; // - 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 ); + } ::MIR::LValue new_temporary(const ::HIR::TypeRef& ty); ::MIR::LValue lvalue_or_temp(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::RValue val); @@ -228,8 +245,8 @@ public: void mark_value_assigned(const Span& sp, const ::MIR::LValue& val); // Moves control of temporaries up to the specified scope (or to above it) - void raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above=false); - void raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above=false); + void raise_temporaries(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above=false); + void raise_temporaries(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above=false); void set_cur_block(unsigned int new_block); ::MIR::BasicBlockId pause_cur_block(); @@ -248,6 +265,7 @@ public: ScopeHandle new_scope_temp(const Span& sp); ScopeHandle new_scope_split(const Span& sp); ScopeHandle new_scope_loop(const Span& sp); + ScopeHandle new_scope_freeze(const Span& sp); /// Raises every variable defined in the source scope into the target scope void raise_all(const Span& sp, ScopeHandle src, const ScopeHandle& target); @@ -256,7 +274,7 @@ public: /// Terminates a scope early (e.g. via return/break/...) void terminate_scope_early(const Span& sp, const ScopeHandle& , bool loop_exit=false); /// Marks the end of a split arm (end match arm, if body, ...) - void end_split_arm(const Span& sp, const ScopeHandle& , bool reachable); + void end_split_arm(const Span& sp, const ScopeHandle& , bool reachable, bool early=false); /// Terminates the current split early (TODO: What does this mean?) void end_split_arm_early(const Span& sp); @@ -269,13 +287,12 @@ public: // Helper - Marks a variable/... as moved (and checks if the move is valid) void moved_lvalue(const Span& sp, const ::MIR::LValue& lv); private: - const VarState& get_slot_state(const Span& sp, VarGroup ty, unsigned int idx, unsigned int skip_count=0) const; - VarState& get_slot_state_mut(const Span& sp, VarGroup ty, unsigned int idx); - - const VarState& get_variable_state(const Span& sp, unsigned int idx, unsigned int skip_count=0) const; - VarState& get_variable_state_mut(const Span& sp, unsigned int idx); - const VarState& get_temp_state(const Span& sp, unsigned int idx, unsigned int skip_count=0) const; - VarState& get_temp_state_mut(const Span& sp, unsigned int idx); + enum class SlotType { + Local, // Local ~0u is return + Argument + }; + 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(const Span& sp, const ::MIR::LValue& lv); diff --git a/src/mir/from_hir_match.cpp b/src/mir/from_hir_match.cpp index a68fd1bf..c10f170b 100644 --- a/src/mir/from_hir_match.cpp +++ b/src/mir/from_hir_match.cpp @@ -82,8 +82,7 @@ struct ArmCode { ::MIR::BasicBlockId code = 0; bool has_condition = false; ::MIR::BasicBlockId cond_start; - ::MIR::BasicBlockId cond_end; - ::MIR::LValue cond_lval; + ::MIR::BasicBlockId cond_false; ::std::vector< ::MIR::BasicBlockId> destructures; // NOTE: Incomplete mutable ::MIR::BasicBlockId cond_fail_tgt = 0; @@ -219,7 +218,7 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod { if( pat.m_binding.m_type != ::HIR::PatternBinding::Type::Move) return false; - return !builder.lvalue_is_copy( sp, ::MIR::LValue::make_Variable( pat.m_binding.m_slot) ); + return !builder.lvalue_is_copy( sp, builder.get_variable(sp, pat.m_binding.m_slot) ); } TU_MATCHA( (pat.m_data), (e), (Any, @@ -355,9 +354,12 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod } builder.terminate_scope( sp, mv$(pat_scope) ); + ac.code = builder.new_bb_unlinked(); + // Condition // NOTE: Lack of drop due to early exit from this arm isn't an issue. All captures must be Copy // - The above is rustc E0008 "cannot bind by-move into a pattern guard" + // TODO: Create a special wrapping scope for the conditions that forces any moves to use a drop flag if(arm.m_cond) { if( H::is_pattern_move(sp, builder, arm.m_patterns[0]) ) @@ -370,11 +372,18 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod ac.cond_start = builder.new_bb_unlinked(); builder.set_cur_block( ac.cond_start ); + auto freeze_scope = builder.new_scope_freeze(arm.m_cond->span()); auto tmp_scope = builder.new_scope_temp(arm.m_cond->span()); conv.visit_node_ptr( arm.m_cond ); - ac.cond_lval = builder.get_result_in_if_cond(arm.m_cond->span()); + auto cond_lval = builder.get_result_in_if_cond(arm.m_cond->span()); builder.terminate_scope( arm.m_code->span(), mv$(tmp_scope) ); - ac.cond_end = builder.pause_cur_block(); + ac.cond_false = builder.new_bb_unlinked(); + builder.end_block(::MIR::Terminator::make_If({ mv$(cond_lval), ac.code, ac.cond_false })); + + builder.set_cur_block(ac.cond_false); + builder.end_split_arm(arm.m_cond->span(), match_scope, true, true); + builder.pause_cur_block(); + builder.terminate_scope( arm.m_code->span(), mv$(freeze_scope) ); // NOTE: Paused so that later code (which knows what the false branch will be) can end it correctly @@ -390,7 +399,6 @@ void MIR_LowerHIR_Match( MirBuilder& builder, MirConverter& conv, ::HIR::ExprNod // Code DEBUG("-- Body Code"); - ac.code = builder.new_bb_unlinked(); auto tmp_scope = builder.new_scope_temp(arm.m_code->span()); builder.set_cur_block( ac.code ); conv.visit_node_ptr( arm.m_code ); @@ -1923,8 +1931,8 @@ void MIR_LowerHIR_Match_Simple( MirBuilder& builder, MirConverter& conv, ::HIR:: } if( arm_code.has_condition ) { - builder.set_cur_block( arm_code.cond_end ); - builder.end_block( ::MIR::Terminator::make_If({ mv$(arm_code.cond_lval), arm_code.code, next_arm_bb }) ); + builder.set_cur_block( arm_code.cond_false ); + builder.end_block( ::MIR::Terminator::make_Goto(next_arm_bb) ); } builder.set_cur_block( next_arm_bb ); } @@ -2625,8 +2633,8 @@ void MatchGenGrouped::gen_for_slice(t_rules_subset arm_rules, size_t ofs, ::MIR: { ac.cond_fail_tgt = next; - m_builder.set_cur_block( ac.cond_end ); - m_builder.end_block( ::MIR::Terminator::make_If({ ac.cond_lval.clone(), ac.code, next }) ); + m_builder.set_cur_block( ac.cond_false ); + m_builder.end_block( ::MIR::Terminator::make_Goto(next) ); } } diff --git a/src/mir/helpers.cpp b/src/mir/helpers.cpp index c38e73e9..e51c9180 100644 --- a/src/mir/helpers.cpp +++ b/src/mir/helpers.cpp @@ -70,24 +70,20 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_static_type(::HIR::TypeRef& tmp, c const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, const ::MIR::LValue& val) const { TU_MATCH(::MIR::LValue, (val), (e), - (Variable, - MIR_ASSERT(*this, e < m_fcn.named_variables.size(), val << " out of range (" << m_fcn.named_variables.size() << ")"); - return m_fcn.named_variables.at(e); - ), - (Temporary, - MIR_ASSERT(*this, e.idx < m_fcn.temporaries.size(), val << " out of range (" << m_fcn.temporaries.size() << ")"); - return m_fcn.temporaries.at(e.idx); + (Return, + return m_ret_type; ), (Argument, - MIR_ASSERT(*this, e.idx < m_args.size(), val << " out of range (" << m_args.size() << ")"); + MIR_ASSERT(*this, e.idx < m_args.size(), "Argument " << val << " out of range (" << m_args.size() << ")"); return m_args.at(e.idx).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); + ), (Static, return get_static_type(tmp, e); ), - (Return, - return m_ret_type; - ), (Field, const auto& ty = this->get_lvalue_type(tmp, *e.val); TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), @@ -292,11 +288,40 @@ const ::HIR::TypeRef& ::MIR::TypeResolve::get_lvalue_type(::HIR::TypeRef& tmp, c } ), (ItemAddr, - MIR_TODO(*this, "get_const_type - Get type for constant `" << c << "`"); + MonomorphState p; + auto v = m_resolve.get_value(this->sp, e, p, /*signature_only=*/true); + TU_MATCHA( (v), (ve), + (NotFound, + MIR_BUG(*this, "get_const_type - ItemAddr points to unknown value - " << c); + ), + (Constant, + MIR_TODO(*this, "get_const_type - Get type for constant borrow `" << c << "`"); + ), + (Static, + MIR_TODO(*this, "get_const_type - Get type for static borrow `" << c << "`"); + ), + (Function, + ::HIR::FunctionType ft; + ft.is_unsafe = ve->m_unsafe; + ft.m_abi = ve->m_abi; + ft.m_rettype = box$( p.monomorph(this->sp, ve->m_return) ); + ft.m_arg_types.reserve(ve->m_args.size()); + for(const auto& arg : ve->m_args) + ft.m_arg_types.push_back( p.monomorph(this->sp, arg.second) ); + auto rv = ::HIR::TypeRef( mv$(ft) ); + m_resolve.expand_associated_types(this->sp, rv); + return rv; + ) + ) ) ) throw ""; } +bool ::MIR::TypeResolve::lvalue_is_copy(const ::MIR::LValue& val) const +{ + ::HIR::TypeRef tmp; + return m_resolve.type_is_copy( this->sp, get_lvalue_type(tmp, val) ); +} const ::HIR::TypeRef* ::MIR::TypeResolve::is_type_owned_box(const ::HIR::TypeRef& ty) const { return m_resolve.is_type_owned_box(ty); @@ -314,16 +339,14 @@ namespace visit { if( cb(lv, u) ) return true; TU_MATCHA( (lv), (e), - (Variable, + (Return, ), (Argument, ), - (Temporary, + (Local, ), (Static, ), - (Return, - ), (Field, return visit_mir_lvalue(*e.val, u, cb); ), @@ -458,6 +481,9 @@ namespace visit { (Switch, rv |= visit_mir_lvalue(e.val, ValUsage::Read, cb); ), + (SwitchValue, + rv |= visit_mir_lvalue(e.val, ValUsage::Read, cb); + ), (Call, if( e.fcn.is_Value() ) { rv |= visit_mir_lvalue(e.fcn.as_Value(), ValUsage::Read, cb); @@ -547,28 +573,32 @@ void MIR_Helper_GetLifetimes_DetermineValueLifetime(::MIR::TypeResolve& state, c } block_offsets.push_back(statement_count); // Store the final limit for later code to use. - ::std::vector<ValueLifetime> temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) ); - ::std::vector<ValueLifetime> variable_lifetimes( fcn.named_variables.size(), ValueLifetime(statement_count) ); - + ::std::vector<ValueLifetime> slot_lifetimes( fcn.locals.size(), ValueLifetime(statement_count) ); // Enumerate direct assignments of variables (linear iteration of BB list) for(size_t bb_idx = 0; bb_idx < fcn.blocks.size(); bb_idx ++) { auto assigned_lvalue = [&](size_t bb_idx, size_t stmt_idx, const ::MIR::LValue& lv) { // NOTE: Fills the first statement after running, just to ensure that any assigned value has _a_ lifetime - if( const auto* de = lv.opt_Variable() ) - { - MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, variable_lifetimes[*de]); - variable_lifetimes[*de].fill(block_offsets, bb_idx, stmt_idx, stmt_idx); - } - else if( const auto* de = lv.opt_Temporary() ) + if( const auto* de = lv.opt_Local() ) { - MIR_Helper_GetLifetimes_DetermineValueLifetime(state, fcn, bb_idx, stmt_idx, lv, block_offsets, temporary_lifetimes[de->idx]); - temporary_lifetimes[de->idx].fill(block_offsets, bb_idx, stmt_idx, stmt_idx); + 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 + // 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 ) + { + 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; + }); } }; @@ -589,6 +619,14 @@ void MIR_Helper_GetLifetimes_DetermineValueLifetime(::MIR::TypeResolve& state, c assigned_lvalue(bb_idx, stmt_idx+1, e.second); } } + 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() ) + { + slot_lifetimes[*de].fill(block_offsets, bb_idx, stmt_idx,stmt_idx); + } + } } state.set_cur_stmt_term(bb_idx); @@ -601,25 +639,18 @@ void MIR_Helper_GetLifetimes_DetermineValueLifetime(::MIR::TypeResolve& state, c // Dump out variable lifetimes. if( dump_debug ) { - for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++) - { - temporary_lifetimes[i].dump_debug("tmp", i, block_offsets); - } - for(unsigned int i = 0; i < variable_lifetimes.size(); i ++) + for(size_t i = 0; i < slot_lifetimes.size(); i ++) { - variable_lifetimes[i].dump_debug("var", i, block_offsets); + slot_lifetimes[i].dump_debug("_", i, block_offsets); } } ::MIR::ValueLifetimes rv; rv.m_block_offsets = mv$(block_offsets); - rv.m_temporaries.reserve( temporary_lifetimes.size() ); - for(auto& lft : temporary_lifetimes) - rv.m_temporaries.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) ); - rv.m_variables.reserve( variable_lifetimes.size() ); - for(auto& lft : variable_lifetimes) - rv.m_variables.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) ); + rv.m_slots.reserve( slot_lifetimes.size() ); + for(auto& lft : slot_lifetimes) + rv.m_slots.push_back( ::MIR::ValueLifetime(mv$(lft.stmt_bitmap)) ); return rv; } void MIR_Helper_GetLifetimes_DetermineValueLifetime( @@ -923,6 +954,13 @@ void MIR_Helper_GetLifetimes_DetermineValueLifetime( m_states_to_do.push_back( ::std::make_pair(te.targets[i], mv$(s)) ); } ), + (SwitchValue, + for(size_t i = 0; i < te.targets.size(); i ++) + { + m_states_to_do.push_back( ::std::make_pair(te.targets[i], state.clone()) ); + } + m_states_to_do.push_back( ::std::make_pair(te.def_target, mv$(state)) ); + ), (Call, if( te.ret_val == m_lv ) { diff --git a/src/mir/helpers.hpp b/src/mir/helpers.hpp index 802ce88f..f241753e 100644 --- a/src/mir/helpers.hpp +++ b/src/mir/helpers.hpp @@ -104,6 +104,7 @@ public: ::HIR::TypeRef get_const_type(const ::MIR::Constant& c) const; + bool lvalue_is_copy(const ::MIR::LValue& val) const; const ::HIR::TypeRef* is_type_owned_box(const ::HIR::TypeRef& ty) const; friend ::std::ostream& operator<<(::std::ostream& os, const TypeResolve& x) { @@ -158,14 +159,10 @@ public: struct ValueLifetimes { ::std::vector<size_t> m_block_offsets; - ::std::vector<ValueLifetime> m_temporaries; - ::std::vector<ValueLifetime> m_variables; + ::std::vector<ValueLifetime> m_slots; - bool var_valid(unsigned var_idx, unsigned bb_idx, unsigned stmt_idx) const { - return m_variables.at(var_idx).valid_at( m_block_offsets[bb_idx] + stmt_idx ); - } - bool tmp_valid(unsigned tmp_idx, unsigned bb_idx, unsigned stmt_idx) const { - return m_temporaries.at(tmp_idx).valid_at( m_block_offsets[bb_idx] + stmt_idx ); + bool slot_valid(unsigned idx, unsigned bb_idx, unsigned stmt_idx) const { + return m_slots.at(idx).valid_at( m_block_offsets[bb_idx] + stmt_idx ); } }; diff --git a/src/mir/main_bindings.hpp b/src/mir/main_bindings.hpp index 0d6074cb..4e7e3b78 100644 --- a/src/mir/main_bindings.hpp +++ b/src/mir/main_bindings.hpp @@ -18,4 +18,4 @@ extern void MIR_CheckCrate(/*const*/ ::HIR::Crate& crate); extern void MIR_CheckCrate_Full(/*const*/ ::HIR::Crate& crate); extern void MIR_CleanupCrate(::HIR::Crate& crate); -extern void MIR_OptimiseCrate(::HIR::Crate& crate); +extern void MIR_OptimiseCrate(::HIR::Crate& crate, bool minimal_optimisations); diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp index 9edc925b..09e978f9 100644 --- a/src/mir/mir.cpp +++ b/src/mir/mir.cpp @@ -92,21 +92,18 @@ namespace MIR { ::std::ostream& operator<<(::std::ostream& os, const LValue& x) { TU_MATCHA( (x), (e), - (Variable, - os << "Variable(" << e << ")"; - ), - (Temporary, - os << "Temporary(" << e.idx << ")"; + (Return, + os << "Return"; ), (Argument, os << "Argument(" << e.idx << ")"; ), + (Local, + os << "Local(" << e << ")"; + ), (Static, os << "Static(" << e << ")"; ), - (Return, - os << "Return"; - ), (Field, os << "Field(" << e.field_index << ", " << *e.val << ")"; ), @@ -127,20 +124,17 @@ namespace MIR { if( a.tag() != b.tag() ) return a.tag() < b.tag(); TU_MATCHA( (a, b), (ea, eb), - (Variable, - return ea < eb; - ), - (Temporary, - return ea.idx < eb.idx; + (Return, + return false; ), (Argument, return ea.idx < eb.idx; ), - (Static, + (Local, return ea < eb; ), - (Return, - return false; + (Static, + return ea < eb; ), (Field, if( *ea.val != *eb.val ) @@ -170,20 +164,17 @@ namespace MIR { if( a.tag() != b.tag() ) return false; TU_MATCHA( (a, b), (ea, eb), - (Variable, - return ea == eb; - ), - (Temporary, - return ea.idx == eb.idx; + (Return, + return true; ), (Argument, return ea.idx == eb.idx; ), - (Static, + (Local, return ea == eb; ), - (Return, - return true; + (Static, + return ea == eb; ), (Field, if( *ea.val != *eb.val ) @@ -428,6 +419,24 @@ namespace MIR { os << j << " => bb" << e.targets[j] << ", "; os << ")"; ), + (SwitchValue, + os << "SwitchValue( " << e.val << " : "; + TU_MATCHA( (e.values), (ve), + (Unsigned, + for(unsigned int j = 0; j < e.targets.size(); j ++) + os << ve[j] << " => bb" << e.targets[j] << ", "; + ), + (Signed, + for(unsigned int j = 0; j < e.targets.size(); j ++) + os << (ve[j] >= 0 ? "+" : "") << ve[j] << " => bb" << e.targets[j] << ", "; + ), + (String, + for(unsigned int j = 0; j < e.targets.size(); j ++) + os << "\"" << ve[j] << "\" => bb" << e.targets[j] << ", "; + ) + ) + os << "else bb" << e.def_target << ")"; + ), (Call, os << "Call( " << e.ret_val << " = "; TU_MATCHA( (e.fcn), (e2), @@ -486,10 +495,8 @@ namespace MIR { ), (ScopeEnd, os << "ScopeEnd("; - for(auto idx : e.vars) - os << "var$" << idx << ","; - for(auto idx : e.tmps) - os << "tmp$" << idx << ","; + for(auto idx : e.slots) + os << "_$" << idx << ","; os << ")"; ) ) @@ -500,11 +507,10 @@ namespace MIR { ::MIR::LValue MIR::LValue::clone() const { TU_MATCHA( (*this), (e), - (Variable, return LValue(e); ), - (Temporary, return LValue(e); ), + (Return, return LValue(e); ), (Argument, return LValue(e); ), + (Local, return LValue(e); ), (Static, return LValue(e.clone()); ), - (Return, return LValue(e); ), (Field, return LValue::make_Field({ box$( e.val->clone() ), e.field_index @@ -616,3 +622,19 @@ namespace MIR { throw ""; } +::MIR::SwitchValues MIR::SwitchValues::clone() const +{ + TU_MATCHA( (*this), (ve), + (Unsigned, + return ve; + ), + (Signed, + return ve; + ), + (String, + return ve; + ) + ) + throw ""; +} + diff --git a/src/mir/mir.hpp b/src/mir/mir.hpp index c22f8d5d..987e0498 100644 --- a/src/mir/mir.hpp +++ b/src/mir/mir.hpp @@ -17,21 +17,15 @@ typedef unsigned int RegionId; typedef unsigned int BasicBlockId; // "LVALUE" - Assignable values -TAGGED_UNION_EX(LValue, (), Variable, ( - // User-named variable - (Variable, unsigned int), - // Temporary with no user-defined name - (Temporary, struct { - unsigned int idx; - }), - // Function argument (matters for destructuring) - (Argument, struct { - unsigned int idx; - }), - // `static` or `static mut` - (Static, ::HIR::Path), +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, ::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 { @@ -217,6 +211,14 @@ TAGGED_UNION(CallTarget, Intrinsic, ::HIR::PathParams params; }) ); +TAGGED_UNION_EX(SwitchValues, (), Unsigned, ( + (Unsigned, ::std::vector<uint64_t>), + (Signed, ::std::vector<int64_t>), + (String, ::std::vector<::std::string>) + ), (),(), ( + SwitchValues clone() const; + ) + ); TAGGED_UNION(Terminator, Incomplete, (Incomplete, struct {}), // Block isn't complete (ERROR in output) @@ -233,6 +235,12 @@ TAGGED_UNION(Terminator, Incomplete, LValue val; ::std::vector<BasicBlockId> targets; }), + (SwitchValue, struct { + LValue val; + BasicBlockId def_target; + ::std::vector<BasicBlockId> targets; + SwitchValues values; + }), (Call, struct { BasicBlockId ret_block; BasicBlockId panic_block; @@ -274,8 +282,7 @@ TAGGED_UNION(Statement, Assign, unsigned int flag_idx; // Valid if != ~0u }), (ScopeEnd, struct { - ::std::vector<unsigned> vars; - ::std::vector<unsigned> tmps; + ::std::vector<unsigned> slots; }) ); extern ::std::ostream& operator<<(::std::ostream& os, const Statement& x); @@ -290,9 +297,8 @@ struct BasicBlock class Function { public: - // TODO: Unify Variables, Temporaries, and Arguments - ::std::vector< ::HIR::TypeRef> named_variables; - ::std::vector< ::HIR::TypeRef> temporaries; + ::std::vector< ::HIR::TypeRef> locals; + //::std::vector< ::std::string> local_names; ::std::vector<bool> drop_flags; ::std::vector<BasicBlock> blocks; diff --git a/src/mir/mir_builder.cpp b/src/mir/mir_builder.cpp index 478146e8..97942ba2 100644 --- a/src/mir/mir_builder.cpp +++ b/src/mir/mir_builder.cpp @@ -26,21 +26,30 @@ MirBuilder::MirBuilder(const Span& sp, const StaticTraitResolve& resolve, const } set_cur_block( new_bb_unlinked() ); - m_scopes.push_back( ScopeDef { sp } ); + m_scopes.push_back( ScopeDef { sp, ScopeType::make_Owning({ false, {} }) } ); m_scope_stack.push_back( 0 ); - m_scopes.push_back( ScopeDef { sp, ScopeType::make_Temporaries({}) } ); + m_scopes.push_back( ScopeDef { sp, ScopeType::make_Owning({ true, {} }) } ); m_scope_stack.push_back( 1 ); + m_arg_states.reserve( args.size() ); + for(size_t i = 0; i < args.size(); i ++) + m_arg_states.push_back( VarState::make_Valid({}) ); + m_slot_states.resize( output.locals.size() ); + m_first_temp_idx = output.locals.size(); + DEBUG("First temporary will be " << m_first_temp_idx); m_if_cond_lval = this->new_temporary(::HIR::CoreType::Bool); - m_arg_states.reserve( args.size() ); - for(size_t i = 0; i < args.size(); i ++ ) - m_arg_states.push_back( VarState::make_Valid({}) ); - m_variable_states.reserve( output.named_variables.size() ); - for(size_t i = 0; i < output.named_variables.size(); i ++ ) - m_variable_states.push_back( VarState::make_Invalid(InvalidType::Uninit) ); + // Determine which variables can be replaced by arguents + for(size_t i = 0; i < args.size(); i ++) + { + const auto& pat = args[i].first; + if( pat.m_binding.is_valid() && pat.m_binding.m_type == ::HIR::PatternBinding::Type::Move ) + { + m_var_arg_mappings[pat.m_binding.m_slot] = i; + } + } } MirBuilder::~MirBuilder() { @@ -51,8 +60,9 @@ MirBuilder::~MirBuilder() { push_stmt_assign( sp, ::MIR::LValue::make_Return({}), get_result(sp) ); } - terminate_scope( sp, ScopeHandle { *this, 1 } ); - terminate_scope( sp, mv$(m_fcn_scope) ); + + terminate_scope_early(sp, fcn_scope()); + end_block( ::MIR::Terminator::make_Return({}) ); } } @@ -74,7 +84,7 @@ const ::HIR::TypeRef* MirBuilder::is_type_owned_box(const ::HIR::TypeRef& ty) co if( pe.m_path != *m_lang_Box ) { return nullptr; } - // TODO: Properly assert? + // TODO: Properly assert the size? return &pe.m_params.m_types.at(0); } else @@ -85,18 +95,21 @@ const ::HIR::TypeRef* MirBuilder::is_type_owned_box(const ::HIR::TypeRef& ty) co void MirBuilder::define_variable(unsigned int idx) { - DEBUG("DEFINE var" << idx << ": " << m_output.named_variables.at(idx)); + DEBUG("DEFINE (var) _" << idx << ": " << m_output.locals.at(idx)); for( auto scope_idx : ::reverse(m_scope_stack) ) { auto& scope_def = m_scopes.at(scope_idx); TU_MATCH_DEF( ScopeType, (scope_def.data), (e), ( ), - (Variables, - auto it = ::std::find(e.vars.begin(), e.vars.end(), idx); - assert(it == e.vars.end()); - e.vars.push_back( idx ); - return ; + (Owning, + if( !e.is_temporary ) + { + auto it = ::std::find(e.slots.begin(), e.slots.end(), idx); + assert(it == e.slots.end()); + e.slots.push_back( idx ); + return ; + } ), (Split, BUG(Span(), "Variable " << idx << " introduced within a Split"); @@ -107,20 +120,24 @@ void MirBuilder::define_variable(unsigned int idx) } ::MIR::LValue MirBuilder::new_temporary(const ::HIR::TypeRef& ty) { - unsigned int rv = m_output.temporaries.size(); - DEBUG("DEFINE tmp" << rv << ": " << ty); + unsigned int rv = m_output.locals.size(); + DEBUG("DEFINE (temp) _" << rv << ": " << ty); - m_output.temporaries.push_back( ty.clone() ); - m_temporary_states.push_back( VarState::make_Invalid(InvalidType::Uninit) ); - assert(m_output.temporaries.size() == m_temporary_states.size()); + assert(m_output.locals.size() == m_slot_states.size()); + m_output.locals.push_back( ty.clone() ); + m_slot_states.push_back( VarState::make_Invalid(InvalidType::Uninit) ); + assert(m_output.locals.size() == m_slot_states.size()); ScopeDef* top_scope = nullptr; for(unsigned int i = m_scope_stack.size(); i --; ) { auto idx = m_scope_stack[i]; - if( m_scopes.at( idx ).data.is_Temporaries() ) { - top_scope = &m_scopes.at(idx); - break ; + if( const auto* e = m_scopes.at( idx ).data.opt_Owning() ) { + if( e->is_temporary ) + { + top_scope = &m_scopes.at(idx); + break ; + } } else if( m_scopes.at(idx).data.is_Loop() ) { @@ -140,9 +157,10 @@ void MirBuilder::define_variable(unsigned int idx) } } assert( top_scope ); - auto& tmp_scope = top_scope->data.as_Temporaries(); - tmp_scope.temporaries.push_back( rv ); - return ::MIR::LValue::make_Temporary({rv}); + 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); } ::MIR::LValue MirBuilder::lvalue_or_temp(const Span& sp, const ::HIR::TypeRef& ty, ::MIR::RValue val) { @@ -151,7 +169,7 @@ void MirBuilder::define_variable(unsigned int idx) ) else { auto temp = new_temporary(ty); - push_stmt_assign( sp, ::MIR::LValue(temp.as_Temporary()), mv$(val) ); + push_stmt_assign( sp, temp.clone(), mv$(val) ); return temp; } } @@ -320,12 +338,6 @@ void MirBuilder::push_stmt_drop(const Span& sp, ::MIR::LValue val, unsigned int } this->push_stmt(sp, ::MIR::Statement::make_Drop({ ::MIR::eDropKind::DEEP, mv$(val), flag })); - - if( flag != ~0u ) - { - // Reset flag value back to default. - push_stmt_set_dropflag_val(sp, flag, m_output.drop_flags.at(flag)); - } } void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsigned int flag/*=~0u*/) { @@ -335,12 +347,6 @@ void MirBuilder::push_stmt_drop_shallow(const Span& sp, ::MIR::LValue val, unsig // TODO: Ensure that the type is a Box? this->push_stmt(sp, ::MIR::Statement::make_Drop({ ::MIR::eDropKind::SHALLOW, mv$(val), flag })); - - if( flag != ~0u ) - { - // Reset flag value back to default. - push_stmt_set_dropflag_val(sp, flag, m_output.drop_flags.at(flag)); - } } void MirBuilder::push_stmt_asm(const Span& sp, ::MIR::Statement::Data_Asm data) { @@ -355,7 +361,7 @@ void MirBuilder::push_stmt_asm(const Span& sp, ::MIR::Statement::Data_Asm data) } void MirBuilder::push_stmt_set_dropflag_val(const Span& sp, unsigned int idx, bool value) { - this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, value })); + this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, value, ~0u })); } void MirBuilder::push_stmt_set_dropflag_other(const Span& sp, unsigned int idx, unsigned int other) { @@ -363,7 +369,7 @@ void MirBuilder::push_stmt_set_dropflag_other(const Span& sp, unsigned int idx, } void MirBuilder::push_stmt_set_dropflag_default(const Span& sp, unsigned int idx) { - this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, this->get_drop_flag_default(sp, idx) })); + this->push_stmt(sp, ::MIR::Statement::make_SetDropFlag({ idx, this->get_drop_flag_default(sp, idx), ~0u })); } void MirBuilder::push_stmt(const Span& sp, ::MIR::Statement stmt) { @@ -378,26 +384,15 @@ void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) TU_MATCH_DEF(::MIR::LValue, (dst), (e), ( ), - (Temporary, - state_p = &get_temp_state_mut(sp, e.idx); - if( const auto* se = state_p->opt_Invalid() ) - { - if( *se != InvalidType::Uninit ) { - BUG(sp, "Reassigning temporary " << e.idx << " - " << *state_p); - } - } - else { - // TODO: This should be a bug, but some of the match code ends up reassigning so.. - //BUG(sp, "Reassigning temporary " << e.idx << " - " << *state_p); - } - ), (Return, // Don't drop. // No state tracking for the return value ), - (Variable, - // TODO: Ensure that slot is mutable (information is lost, assume true) - state_p = &get_variable_state_mut(sp, e); + (Argument, + state_p = &get_slot_state_mut(sp, e.idx, SlotType::Argument); + ), + (Local, + state_p = &get_slot_state_mut(sp, e, SlotType::Local); ) ) @@ -411,7 +406,7 @@ void MirBuilder::mark_value_assigned(const Span& sp, const ::MIR::LValue& dst) } } -void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const ScopeHandle& scope, bool to_above/*=false*/) +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), @@ -422,29 +417,34 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const // 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_variables(sp, *e.val, scope, to_above); - raise_variables(sp, *e.idx, scope, to_above); + raise_temporaries(sp, *e.val, scope, to_above); + raise_temporaries(sp, *e.idx, scope, to_above); return ; ), (Deref, - raise_variables(sp, *e.val, scope, to_above); + raise_temporaries(sp, *e.val, scope, to_above); return ; ), (Field, - raise_variables(sp, *e.val, scope, to_above); + raise_temporaries(sp, *e.val, scope, to_above); return ; ), (Downcast, - raise_variables(sp, *e.val, scope, to_above); + raise_temporaries(sp, *e.val, scope, to_above); return ; ), // Actual value types - (Variable, - ), - (Temporary, + (Local, ) ) - ASSERT_BUG(sp, val.is_Variable() || val.is_Temporary(), "Hit value raising code with non-variable value - " << val); + ASSERT_BUG(sp, val.is_Local(), "Hit value raising code with non-variable value - " << val); + const auto idx = val.as_Local(); + bool is_temp = (idx >= m_first_temp_idx); + /* + if( !is_temp ) { + return ; + } + */ // Find controlling scope auto scope_it = m_scope_stack.rbegin(); @@ -457,30 +457,20 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const DEBUG(val << " defined in or above target (scope " << scope << ")"); } - TU_IFLET( ScopeType, scope_def.data, Variables, e, - if( const auto* ve = val.opt_Variable() ) + TU_IFLET( ScopeType, scope_def.data, Owning, e, + if( e.is_temporary == is_temp ) { - auto idx = *ve; - auto tmp_it = ::std::find( e.vars.begin(), e.vars.end(), idx ); - if( tmp_it != e.vars.end() ) + auto tmp_it = ::std::find(e.slots.begin(), e.slots.end(), idx); + if( tmp_it != e.slots.end() ) { - e.vars.erase( tmp_it ); - DEBUG("Raise variable " << idx << " from " << *scope_it); + e.slots.erase( tmp_it ); + DEBUG("Raise slot " << idx << " from " << *scope_it); break ; } } - ) - else TU_IFLET( ScopeType, scope_def.data, Temporaries, e, - if( const auto* ve = val.opt_Temporary() ) + else { - auto idx = ve->idx; - auto tmp_it = ::std::find( e.temporaries.begin(), e.temporaries.end(), idx ); - if( tmp_it != e.temporaries.end() ) - { - e.temporaries.erase( tmp_it ); - DEBUG("Raise temporary " << idx << " from " << *scope_it); - break ; - } + // TODO: Should this care about variables? } ) else @@ -536,26 +526,12 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const target_seen = true; } - TU_IFLET( ScopeType, scope_def.data, Variables, e, - if( target_seen ) + TU_IFLET( ScopeType, scope_def.data, Owning, e, + if( target_seen && e.is_temporary == is_temp ) { - if( const auto* ve = val.opt_Variable() ) - { - e.vars.push_back( *ve ); - DEBUG("- to " << *scope_it); - return ; - } - } - ) - else TU_IFLET( ScopeType, scope_def.data, Temporaries, e, - if( target_seen ) - { - if( const auto* ve = val.opt_Temporary() ) - { - e.temporaries.push_back( ve->idx ); - DEBUG("- to " << *scope_it); - return ; - } + e.slots.push_back( idx ); + DEBUG("- to " << *scope_it); + return ; } ) else if( auto* sd_loop = scope_def.data.opt_Loop() ) @@ -566,19 +542,8 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const if( sd_loop->exit_state_valid ) { DEBUG("Adding " << val << " as unset to loop exit state"); - if( const auto* ve = val.opt_Variable() ) - { - auto v = sd_loop->exit_state.var_states.insert( ::std::make_pair(*ve, VarState(InvalidType::Uninit)) ); - ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); - } - else if( const auto* ve = val.opt_Temporary() ) - { - auto v = sd_loop->exit_state.tmp_states.insert( ::std::make_pair(ve->idx, VarState(InvalidType::Uninit)) ); - ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); - } - else { - BUG(sp, "Impossible raise value"); - } + auto v = sd_loop->exit_state.states.insert( ::std::make_pair(idx, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); } else { @@ -592,19 +557,8 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const if( sd_split->end_state_valid ) { DEBUG("Adding " << val << " as unset to loop exit state"); - if( const auto* ve = val.opt_Variable() ) - { - auto v = sd_split->end_state.var_states.insert( ::std::make_pair(*ve, VarState(InvalidType::Uninit)) ); - ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); - } - else if( const auto* ve = val.opt_Temporary() ) - { - auto v = sd_split->end_state.tmp_states.insert( ::std::make_pair(ve->idx, VarState(InvalidType::Uninit)) ); - ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); - } - else { - BUG(sp, "Impossible raise value"); - } + auto v = sd_split->end_state.states.insert( ::std::make_pair(idx, VarState(InvalidType::Uninit)) ); + ASSERT_BUG(sp, v.second, "Raising " << val << " which already had a state entry"); } else { @@ -613,20 +567,8 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const // TODO: This should update the outer state to unset. auto& arm = sd_split->arms.back(); - if( const auto* ve = val.opt_Variable() ) - { - arm.var_states.insert(::std::make_pair( *ve, get_variable_state(sp, *ve).clone() )); - m_variable_states.at(*ve) = VarState(InvalidType::Uninit); - } - else if( const auto* ve = val.opt_Temporary() ) - { - arm.tmp_states.insert(::std::make_pair( ve->idx, get_temp_state(sp, ve->idx).clone() )); - m_temporary_states.at(ve->idx) = VarState(InvalidType::Uninit); - } - else - { - BUG(sp, "Impossible raise value"); - } + arm.states.insert(::std::make_pair( idx, get_slot_state(sp, idx, SlotType::Local).clone() )); + m_slot_states.at(idx) = VarState(InvalidType::Uninit); } else { @@ -635,15 +577,15 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::LValue& val, const } BUG(sp, "Couldn't find a scope to raise " << val << " into"); } -void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above/*=false*/) +void MirBuilder::raise_temporaries(const Span& sp, const ::MIR::RValue& rval, const ScopeHandle& scope, bool to_above/*=false*/) { auto raise_vars = [&](const ::MIR::Param& p) { if( const auto* e = p.opt_LValue() ) - this->raise_variables(sp, *e, scope, to_above); + this->raise_temporaries(sp, *e, scope, to_above); }; TU_MATCHA( (rval), (e), (Use, - this->raise_variables(sp, e, scope, to_above); + this->raise_temporaries(sp, e, scope, to_above); ), (Constant, ), @@ -652,23 +594,23 @@ void MirBuilder::raise_variables(const Span& sp, const ::MIR::RValue& rval, cons ), (Borrow, // TODO: Wait, is this valid? - this->raise_variables(sp, e.val, scope, to_above); + this->raise_temporaries(sp, e.val, scope, to_above); ), (Cast, - this->raise_variables(sp, e.val, scope, to_above); + this->raise_temporaries(sp, e.val, scope, to_above); ), (BinOp, raise_vars(e.val_l); raise_vars(e.val_r); ), (UniOp, - this->raise_variables(sp, e.val, scope, to_above); + this->raise_temporaries(sp, e.val, scope, to_above); ), (DstMeta, - this->raise_variables(sp, e.val, scope, to_above); + this->raise_temporaries(sp, e.val, scope, to_above); ), (DstPtr, - this->raise_variables(sp, e.val, scope, to_above); + this->raise_temporaries(sp, e.val, scope, to_above); ), (MakeDst, raise_vars(e.ptr_val); @@ -743,6 +685,14 @@ unsigned int MirBuilder::new_drop_flag(bool default_state) { auto rv = m_output.drop_flags.size(); m_output.drop_flags.push_back(default_state); + for(size_t i = m_scope_stack.size(); i --;) + { + if( auto* e = m_scopes.at(m_scope_stack[i]).data.opt_Loop() ) + { + e->drop_flags.push_back(rv); + break; + } + } DEBUG("(" << default_state << ") = " << rv); return rv; } @@ -760,7 +710,7 @@ bool MirBuilder::get_drop_flag_default(const Span& sp, unsigned int idx) ScopeHandle MirBuilder::new_scope_var(const Span& sp) { unsigned int idx = m_scopes.size(); - m_scopes.push_back( ScopeDef {sp, ScopeType::make_Variables({})} ); + m_scopes.push_back( ScopeDef {sp, ScopeType::make_Owning({ false, {} })} ); m_scope_stack.push_back( idx ); DEBUG("START (var) scope " << idx); return ScopeHandle { *this, idx }; @@ -768,7 +718,8 @@ ScopeHandle MirBuilder::new_scope_var(const Span& sp) ScopeHandle MirBuilder::new_scope_temp(const Span& sp) { unsigned int idx = m_scopes.size(); - m_scopes.push_back( ScopeDef {sp, ScopeType::make_Temporaries({})} ); + + m_scopes.push_back( ScopeDef {sp, ScopeType::make_Owning({ true, {} })} ); m_scope_stack.push_back( idx ); DEBUG("START (temp) scope " << idx); return ScopeHandle { *this, idx }; @@ -786,10 +737,19 @@ ScopeHandle MirBuilder::new_scope_loop(const Span& sp) { unsigned int idx = m_scopes.size(); m_scopes.push_back( ScopeDef {sp, ScopeType::make_Loop({})} ); + m_scopes.back().data.as_Loop().entry_bb = m_current_block; m_scope_stack.push_back( idx ); DEBUG("START (loop) scope " << idx); return ScopeHandle { *this, idx }; } +ScopeHandle MirBuilder::new_scope_freeze(const Span& sp) +{ + unsigned int idx = m_scopes.size(); + m_scopes.push_back( ScopeDef {sp, ScopeType::make_Freeze({})} ); + m_scope_stack.push_back( idx ); + DEBUG("START (freeze) scope " << idx); + return ScopeHandle { *this, idx }; +} void MirBuilder::terminate_scope(const Span& sp, ScopeHandle scope, bool emit_cleanup/*=true*/) { TRACE_FUNCTION_F("DONE scope " << scope.idx << " - " << (emit_cleanup ? "CLEANUP" : "NO CLEANUP")); @@ -852,11 +812,13 @@ 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_Temporaries(), "Rasising scopes can only be done on temporaries (source)"); - auto& src_list = src_scope_def.data.as_Temporaries().temporaries; + 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_Temporary({ idx })); + DEBUG("> Raising " << ::MIR::LValue::make_Local(idx)); + assert(idx >= m_first_temp_idx); } // Seek up stack until the target scope is seen @@ -873,7 +835,7 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle // Insert these values as Invalid, both in the existing exit state, and in the changed list for(auto idx : src_list) { - auto v = sd_loop->exit_state.tmp_states.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); + auto v = sd_loop->exit_state.states.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); ASSERT_BUG(sp, v.second, ""); } } @@ -884,7 +846,7 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle for(auto idx : src_list) { - auto v2 = sd_loop->changed_tmps.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); + auto v2 = sd_loop->changed_slots.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); ASSERT_BUG(sp, v2.second, ""); } } @@ -896,7 +858,7 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle // Insert these indexes as Invalid for(auto idx : src_list) { - auto v = sd_split->end_state.tmp_states.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); + auto v = sd_split->end_state.states.insert(::std::make_pair( idx, VarState(InvalidType::Uninit) )); ASSERT_BUG(sp, v.second, ""); } } @@ -910,8 +872,8 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle auto& arm = sd_split->arms.back(); for(auto idx : src_list) { - arm.tmp_states.insert(::std::make_pair( idx, mv$(m_temporary_states.at(idx)) )); - m_temporary_states.at(idx) = VarState(InvalidType::Uninit); + arm.states.insert(::std::make_pair( idx, mv$(m_slot_states.at(idx)) )); + m_slot_states.at(idx) = VarState(InvalidType::Uninit); } } } @@ -920,16 +882,17 @@ void MirBuilder::raise_all(const Span& sp, ScopeHandle source, const ScopeHandle BUG(sp, "Moving values to a scope not on the stack - scope " << target.idx); } auto& tgt_scope_def = m_scopes.at(target.idx); - ASSERT_BUG(sp, tgt_scope_def.data.is_Temporaries(), "Rasising scopes can only be done on temporaries (target)"); + ASSERT_BUG(sp, tgt_scope_def.data.is_Owning(), "Rasising scopes can only be done on temporaries (target)"); + ASSERT_BUG(sp, tgt_scope_def.data.as_Owning().is_temporary, "Rasising scopes can only be done on temporaries (target)"); // Move all defined variables from one to the other - auto& tgt_list = tgt_scope_def.data.as_Temporaries().temporaries; + 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_variables(sp, ::MIR::LValue::make_Temporary({ idx }), target); + this->raise_temporaries(sp, ::MIR::LValue::make_Temporary({ idx }), target); } #endif @@ -985,6 +948,18 @@ void MirBuilder::terminate_scope_early(const Span& sp, const ScopeHandle& scope, ) } } + + + // Index 0 is the function scope, this only happens when about to return/panic + if( scope.idx == 0 ) + { + // Ensure that all arguments are dropped if they were not moved + 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) })); + } + } } namespace @@ -1017,6 +992,9 @@ namespace old_state = VarState::make_Optional( new_flag ); #else // TODO: Rewrite history. I.e. visit all previous branches and set this drop flag to `false` in all of them + for(auto pos : other_arms) { + builder.push_df_set_at(pos, flag_idx, false); + } TODO(sp, "Drop flag default not false when going Invalid->Optional"); #endif } @@ -1041,6 +1019,10 @@ namespace builder.push_stmt_set_dropflag_other(sp, new_flag, nse.outer_flag); builder.push_stmt_set_dropflag_default(sp, nse.outer_flag); ose.outer_flag = new_flag; +#if 0 + for(auto pos : other_arms) { + } +#endif } } else @@ -1388,45 +1370,39 @@ void MirBuilder::terminate_loop_early(const Span& sp, ScopeType::Data_Loop& sd_l { // Insert copies of parent state for newly changed values // and Merge all changed values - for(const auto& ent : sd_loop.changed_vars) - { - auto idx = ent.first; - if( sd_loop.exit_state.var_states.count(idx) == 0 ) { - sd_loop.exit_state.var_states.insert(::std::make_pair( idx, ent.second.clone() )); - } - auto& old_state = sd_loop.exit_state.var_states.at(idx); - merge_state(sp, *this, ::MIR::LValue::make_Variable(idx), old_state, get_variable_state(sp, idx)); - } - for(const auto& ent : sd_loop.changed_tmps) - { - auto idx = ent.first; - if( sd_loop.exit_state.tmp_states.count(idx) == 0 ) { - sd_loop.exit_state.tmp_states.insert(::std::make_pair( idx, ent.second.clone() )); + auto merge_list = [sp,this](const auto& changed, auto& exit_states, ::std::function<::MIR::LValue(unsigned)> val_cb, auto type) { + for(const auto& ent : changed) + { + auto idx = ent.first; + auto it = exit_states.find(idx); + if( it == exit_states.end() ) { + it = exit_states.insert(::std::make_pair( idx, ent.second.clone() )).first; + } + auto& old_state = it->second; + merge_state(sp, *this, val_cb(idx), old_state, get_slot_state(sp, idx, type)); } - auto& old_state = sd_loop.exit_state.tmp_states.at(idx); - merge_state(sp, *this, ::MIR::LValue::make_Temporary({idx}), old_state, get_temp_state(sp, idx)); - } + }; + 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); } else { + auto init_list = [sp,this](const auto& changed, auto& exit_states, auto type) { + for(const auto& ent : changed) + { + DEBUG("Slot(" << ent.first << ") = " << ent.second); + auto idx = ent.first; + exit_states.insert(::std::make_pair( idx, get_slot_state(sp, idx, type).clone() )); + } + }; // Obtain states of changed variables/temporaries - for(const auto& ent : sd_loop.changed_vars) - { - DEBUG("Variable(" << ent.first << ") = " << ent.second); - auto idx = ent.first; - sd_loop.exit_state.var_states.insert(::std::make_pair( idx, get_variable_state(sp, idx).clone() )); - } - for(const auto& ent : sd_loop.changed_tmps) - { - DEBUG("Temporary(" << ent.first << ") = " << ent.second); - auto idx = ent.first; - sd_loop.exit_state.tmp_states.insert(::std::make_pair( idx, get_temp_state(sp, idx).clone() )); - } + init_list(sd_loop.changed_slots, sd_loop.exit_state.states, SlotType::Local); + init_list(sd_loop.changed_args, sd_loop.exit_state.arg_states, SlotType::Argument); sd_loop.exit_state_valid = true; } } -void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable) +void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool reachable, bool early/*=false*/) { ASSERT_BUG(sp, handle.idx < m_scopes.size(), "Handle passed to end_split_arm is invalid"); auto& sd = m_scopes.at( handle.idx ); @@ -1445,41 +1421,29 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r { if( reachable ) { - // Insert copies of the parent state - for(const auto& ent : this_arm_state.var_states) { - if( sd_split.end_state.var_states.count(ent.first) == 0 ) { - sd_split.end_state.var_states.insert(::std::make_pair( ent.first, get_variable_state(sp, ent.first, 1).clone() )); - } - } - for(const auto& ent : this_arm_state.tmp_states) { - if( sd_split.end_state.tmp_states.count(ent.first) == 0 ) { - sd_split.end_state.tmp_states.insert(::std::make_pair( ent.first, get_temp_state(sp, ent.first, 1).clone() )); + auto merge_list = [sp,this](const auto& states, auto& end_states, auto type) { + // Insert copies of the parent state + for(const auto& ent : states) { + if( end_states.count(ent.first) == 0 ) { + end_states.insert(::std::make_pair( ent.first, get_slot_state(sp, ent.first, type, 1).clone() )); + } } - } - - // Merge state - for(auto& ent : sd_split.end_state.var_states) - { - auto idx = ent.first; - auto& out_state = ent.second; - - // Merge the states - auto it = this_arm_state.var_states.find(idx); - const auto& src_state = (it != this_arm_state.var_states.end() ? it->second : get_variable_state(sp, idx, 1)); - - merge_state(sp, *this, ::MIR::LValue::make_Variable(idx), out_state, src_state); - } - for(auto& ent : sd_split.end_state.tmp_states) - { - auto idx = ent.first; - auto& out_state = ent.second; + // Merge state + for(auto& ent : end_states) + { + auto idx = ent.first; + auto& out_state = ent.second; - // Merge the states - auto it = this_arm_state.tmp_states.find(idx); - const auto& src_state = (it != this_arm_state.tmp_states.end() ? it->second : get_temp_state(sp, idx, 1)); + // Merge the states + auto it = states.find(idx); + const auto& src_state = (it != states.end() ? it->second : get_slot_state(sp, idx, type, 1)); - merge_state(sp, *this, ::MIR::LValue::make_Temporary({idx}), out_state, src_state); - } + auto lv = (type == SlotType::Local ? ::MIR::LValue::make_Local(idx) : ::MIR::LValue::make_Argument({idx})); + merge_state(sp, *this, mv$(lv), out_state, src_state); + } + }; + merge_list(this_arm_state.states, sd_split.end_state.states, SlotType::Local); + merge_list(this_arm_state.arg_states, sd_split.end_state.arg_states, SlotType::Argument); } else { @@ -1489,27 +1453,34 @@ void MirBuilder::end_split_arm(const Span& sp, const ScopeHandle& handle, bool r else { // Clone this arm's state - for(auto& ent : this_arm_state.var_states) + for(auto& ent : this_arm_state.states) { - DEBUG("Variable(" << ent.first << ") = " << ent.second); - sd_split.end_state.var_states.insert(::std::make_pair( ent.first, ent.second.clone() )); + DEBUG("Slot(" << ent.first << ") = " << ent.second); + sd_split.end_state.states.insert(::std::make_pair( ent.first, ent.second.clone() )); } - for(auto& ent : this_arm_state.tmp_states) + for(auto& ent : this_arm_state.arg_states) { - DEBUG("Temporary(" << ent.first << ") = " << ent.second); - sd_split.end_state.tmp_states.insert(::std::make_pair( ent.first, ent.second.clone() )); + DEBUG("Argument(" << ent.first << ") = " << ent.second); + sd_split.end_state.arg_states.insert(::std::make_pair( ent.first, ent.second.clone() )); } sd_split.end_state_valid = true; } - sd_split.arms.push_back( {} ); + if( reachable ) + { + assert(m_block_active); + } + if( !early ) + { + sd_split.arms.push_back( {} ); + } } void MirBuilder::end_split_arm_early(const Span& sp) { TRACE_FUNCTION_F(""); size_t i = m_scope_stack.size(); - // Terminate all scopes until a split is found. - while( i -- && ! (m_scopes.at(m_scope_stack[i]).data.is_Split() || m_scopes.at(m_scope_stack[i]).data.is_Loop()) ) + // Terminate every sequence of owning scopes + while( i -- && m_scopes.at(m_scope_stack[i]).data.is_Owning() ) { auto& scope_def = m_scopes[m_scope_stack[i]]; // Fully drop the scope @@ -1529,6 +1500,7 @@ void MirBuilder::end_split_arm_early(const Span& sp) // TODO: Create drop flags if required? } + // TODO: What if this is a loop? } } void MirBuilder::complete_scope(ScopeDef& sd) @@ -1536,37 +1508,37 @@ void MirBuilder::complete_scope(ScopeDef& sd) sd.complete = true; TU_MATCHA( (sd.data), (e), - (Temporaries, - DEBUG("Temporaries - " << e.temporaries); - ), - (Variables, - DEBUG("Variables - " << e.vars); + (Owning, + DEBUG("Owning (" << (e.is_temporary ? "temps" : "vars") << ") - " << e.slots); ), (Loop, DEBUG("Loop"); ), (Split, + ), + (Freeze, + //DEBUG("Freeze"); ) ) struct H { static void apply_end_state(const Span& sp, MirBuilder& builder, SplitEnd& end_state) { - for(auto& ent : end_state.var_states) + for(auto& ent : end_state.states) { - auto& vs = builder.get_variable_state_mut(sp, ent.first); + auto& vs = builder.get_slot_state_mut(sp, ent.first, SlotType::Local); if( vs != ent.second ) { - DEBUG(::MIR::LValue::make_Variable(ent.first) << " " << vs << " => " << ent.second); + DEBUG(::MIR::LValue::make_Local(ent.first) << " " << vs << " => " << ent.second); vs = ::std::move(ent.second); } } - for(auto& ent : end_state.tmp_states) + for(auto& ent : end_state.arg_states) { - auto& vs = builder.get_temp_state_mut(sp, ent.first); + auto& vs = builder.get_slot_state_mut(sp, ent.first, SlotType::Argument); if( vs != ent.second ) { - DEBUG(::MIR::LValue::make_Temporary({ent.first}) << " " << vs << " => " << ent.second); + DEBUG(::MIR::LValue::make_Argument({ent.first}) << " " << vs << " => " << ent.second); vs = ::std::move(ent.second); } } @@ -1574,13 +1546,20 @@ void MirBuilder::complete_scope(ScopeDef& sd) }; // No macro for better debug output. - if( sd.data.is_Loop() ) + if( auto* e = sd.data.opt_Loop() ) { - auto& e = sd.data.as_Loop(); TRACE_FUNCTION_F("Loop"); - if( e.exit_state_valid ) + if( e->exit_state_valid ) + { + H::apply_end_state(sd.span, *this, e->exit_state); + } + + // Insert sets of drop flags to the first block (at the start of that block) + auto& stmts = m_output.blocks.at(e->entry_bb).statements; + for(auto idx : e->drop_flags) { - H::apply_end_state(sd.span, *this, e.exit_state); + DEBUG("Reset df$" << idx); + stmts.insert( stmts.begin(), ::MIR::Statement::make_SetDropFlag({ idx, m_output.drop_flags.at(idx), ~0u }) ); } } else if( sd.data.is_Split() ) @@ -1596,16 +1575,15 @@ 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 { TU_MATCH(::MIR::LValue, (val), (e), - (Variable, - cb( m_output.named_variables.at(e) ); - ), - (Temporary, - cb( m_output.temporaries.at(e.idx) ); + (Return, + TODO(sp, "Return"); ), (Argument, - ASSERT_BUG(sp, e.idx < m_args.size(), "Argument number out of range"); cb( m_args.at(e.idx).second ); ), + (Local, + cb( m_output.locals.at(e) ); + ), (Static, TU_MATCHA( (e.m_data), (pe), (Generic, @@ -1624,9 +1602,6 @@ void MirBuilder::with_val_type(const Span& sp, const ::MIR::LValue& val, ::std:: ) ) ), - (Return, - TODO(sp, "Return"); - ), (Field, with_val_type(sp, *e.val, [&](const auto& ty){ TU_MATCH_DEF( ::HIR::TypeRef::Data, (ty.m_data), (te), @@ -1821,7 +1796,7 @@ bool MirBuilder::lvalue_is_copy(const Span& sp, const ::MIR::LValue& val) const return rv == 2; } -const VarState& MirBuilder::get_slot_state(const Span& sp, VarGroup ty, unsigned int idx, unsigned int skip_count/*=0*/) const +const VarState& MirBuilder::get_slot_state(const Span& sp, unsigned int idx, SlotType type, unsigned int skip_count/*=0*/) const { // 1. Find an applicable Split scope for( auto scope_idx : ::reverse(m_scope_stack) ) @@ -1830,146 +1805,114 @@ const VarState& MirBuilder::get_slot_state(const Span& sp, VarGroup ty, unsigned TU_MATCH_DEF( ScopeType, (scope_def.data), (e), ( ), - (Temporaries, - if( ty == VarGroup::Temporary ) + (Owning, + if( type == SlotType::Local ) { - auto it = ::std::find(e.temporaries.begin(), e.temporaries.end(), idx); - if( it != e.temporaries.end() ) { - break ; - } - } - ), - (Variables, - if( ty == VarGroup::Variable ) - { - auto it = ::std::find(e.vars.begin(), e.vars.end(), idx); - if( it != e.vars.end() ) { - // If controlled by this block, exit early (won't find it elsewhere) + auto it = ::std::find(e.slots.begin(), e.slots.end(), idx); + if( it != e.slots.end() ) { break ; } } ), (Split, const auto& cur_arm = e.arms.back(); - if( ty == VarGroup::Variable ) + const auto& list = (type == SlotType::Local ? cur_arm.states : cur_arm.arg_states); + auto it = list.find(idx); + if( it != list.end() ) { - auto it = cur_arm.var_states.find(idx); - if( it != cur_arm.var_states.end() ) + if( ! skip_count -- ) { - if( ! skip_count -- ) - { - return it->second; - } - } - } - else if( ty == VarGroup::Temporary ) - { - auto it = cur_arm.tmp_states.find(idx); - if( it != cur_arm.tmp_states.end() ) - { - if( ! skip_count -- ) - { - return it->second; - } + return it->second; } } ) ) } - switch(ty) + switch(type) { - case VarGroup::Return: - return m_return_state; - case VarGroup::Argument: + case SlotType::Local: + if( idx == ~0u ) + { + return m_return_state; + } + else + { + ASSERT_BUG(sp, idx < m_slot_states.size(), "Slot " << idx << " out of range for state table"); + return m_slot_states.at(idx); + } + break; + case SlotType::Argument: ASSERT_BUG(sp, idx < m_arg_states.size(), "Argument " << idx << " out of range for state table"); return m_arg_states.at(idx); - case VarGroup::Variable: - ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); - return m_variable_states[idx]; - case VarGroup::Temporary: - ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); - return m_temporary_states[idx]; } - BUG(sp, "Fell off the end of get_slot_state"); + throw ""; } -VarState& MirBuilder::get_slot_state_mut(const Span& sp, VarGroup ty, unsigned int idx) +VarState& MirBuilder::get_slot_state_mut(const Span& sp, unsigned int idx, SlotType type) { VarState* ret = nullptr; for( auto scope_idx : ::reverse(m_scope_stack) ) { auto& scope_def = m_scopes.at(scope_idx); - if( const auto* e = scope_def.data.opt_Variables() ) - { - if( ty == VarGroup::Variable ) - { - auto it = ::std::find(e->vars.begin(), e->vars.end(), idx); - if( it != e->vars.end() ) { - break ; - } - } - } - else if( const auto* e = scope_def.data.opt_Temporaries() ) + if( const auto* e = scope_def.data.opt_Owning() ) { - if( ty == VarGroup::Temporary ) + if( type == SlotType::Local ) { - auto it = ::std::find(e->temporaries.begin(), e->temporaries.end(), idx); - if( it != e->temporaries.end() ) { + auto it = ::std::find(e->slots.begin(), e->slots.end(), idx); + if( it != e->slots.end() ) { break ; } } } - else if( scope_def.data.is_Split() ) + else if( auto* e = scope_def.data.opt_Split() ) { - auto& e = scope_def.data.as_Split(); - auto& cur_arm = e.arms.back(); + auto& cur_arm = e->arms.back(); if( ! ret ) { - ::std::map<unsigned int, VarState>* states; - switch(ty) - { - case VarGroup::Return: states = nullptr; break; - case VarGroup::Argument: BUG(sp, "Mutating state of argument"); break; - case VarGroup::Variable: states = &cur_arm.var_states; break; - case VarGroup::Temporary: states = &cur_arm.tmp_states; break; + if( idx == ~0u ) { } - - if( states ) - { - auto it = states->find(idx); - if( it == states->end() ) + else { + auto& states = (type == SlotType::Local ? cur_arm.states : cur_arm.arg_states); + auto it = states.find(idx); + if( it == states.end() ) { DEBUG("Split new (scope " << scope_idx << ")"); - ret = &( (*states)[idx] = get_slot_state(sp, ty, idx).clone() ); + it = states.insert(::std::make_pair( idx, get_slot_state(sp, idx, type).clone() )).first; } else { DEBUG("Split existing (scope " << scope_idx << ")"); - ret = &it->second; } + ret = &it->second; } } } - else if( scope_def.data.is_Loop() ) + else if( auto* e = scope_def.data.opt_Loop() ) { - auto& e = scope_def.data.as_Loop(); - ::std::map<unsigned int, VarState>* states = nullptr; - switch(ty) + if( idx == ~0u ) { - case VarGroup::Return: states = nullptr; break; - case VarGroup::Argument: BUG(sp, "Mutating state of argument"); break; - case VarGroup::Variable: states = &e.changed_vars; break; - case VarGroup::Temporary: states = &e.changed_tmps; break; } - - if( states ) + else { - if( states->count(idx) == 0 ) + auto& states = (type == SlotType::Local ? e->changed_slots : e->changed_args); + if( states.count(idx) == 0 ) { - auto state = e.exit_state_valid ? get_slot_state(sp, ty, idx).clone() : VarState::make_Valid({}); - states->insert(::std::make_pair( idx, mv$(state) )); + auto state = e->exit_state_valid ? get_slot_state(sp, idx, type).clone() : VarState::make_Valid({}); + states.insert(::std::make_pair( idx, mv$(state) )); } } } + else if( scope_def.data.is_Freeze() ) + { + if( type == SlotType::Local && idx == m_if_cond_lval.as_Local() ) + { + } + else + { + // NOTE: This is only used in match conditions + DEBUG("Mutating state of ?" << idx); + ERROR(sp, E0000, "Attempting to move/initialise a value where not allowed"); + } + } else { } @@ -1980,39 +1923,23 @@ VarState& MirBuilder::get_slot_state_mut(const Span& sp, VarGroup ty, unsigned i } else { - switch(ty) + switch(type) { - case VarGroup::Return: - return m_return_state; - case VarGroup::Argument: - ASSERT_BUG(sp, idx < m_arg_states.size(), "Argument " << idx << " out of range for state table"); + case SlotType::Local: + if( idx == ~0u ) + { + return m_return_state; + } + else + { + return m_slot_states.at(idx); + } + case SlotType::Argument: return m_arg_states.at(idx); - case VarGroup::Variable: - ASSERT_BUG(sp, idx < m_variable_states.size(), "Variable " << idx << " out of range for state table"); - return m_variable_states[idx]; - case VarGroup::Temporary: - ASSERT_BUG(sp, idx < m_temporary_states.size(), "Temporary " << idx << " out of range for state table"); - return m_temporary_states[idx]; - } - BUG(sp, "Fell off the end of get_slot_state_mut"); + } + throw ""; } } -const VarState& MirBuilder::get_variable_state(const Span& sp, unsigned int idx, unsigned int skip_count) const -{ - return get_slot_state(sp, VarGroup::Variable, idx, skip_count); -} -VarState& MirBuilder::get_variable_state_mut(const Span& sp, unsigned int idx) -{ - return get_slot_state_mut(sp, VarGroup::Variable, idx); -} -const VarState& MirBuilder::get_temp_state(const Span& sp, unsigned int idx, unsigned int skip_count) const -{ - return get_slot_state(sp, VarGroup::Temporary, idx, skip_count); -} -VarState& MirBuilder::get_temp_state_mut(const Span& sp, unsigned int idx) -{ - return get_slot_state_mut(sp, VarGroup::Temporary, idx); -} const VarState& MirBuilder::get_val_state(const Span& sp, const ::MIR::LValue& lv, unsigned int skip_count) { @@ -2022,22 +1949,19 @@ VarState& MirBuilder::get_val_state_mut(const Span& sp, const ::MIR::LValue& lv) { TRACE_FUNCTION_F(lv); TU_MATCHA( (lv), (e), - (Variable, - return get_slot_state_mut(sp, VarGroup::Variable, e); - ), - (Temporary, - return get_slot_state_mut(sp, VarGroup::Temporary, e.idx); + (Return, + BUG(sp, "Move of return value"); + return get_slot_state_mut(sp, ~0u, SlotType::Local); ), (Argument, - return get_slot_state_mut(sp, VarGroup::Argument, e.idx); + return get_slot_state_mut(sp, e.idx, SlotType::Argument); + ), + (Local, + return get_slot_state_mut(sp, e, SlotType::Local); ), (Static, BUG(sp, "Attempting to mutate state of a static"); ), - (Return, - BUG(sp, "Move of return value"); - return get_slot_state_mut(sp, VarGroup::Return, 0); - ), (Field, auto& ivs = get_val_state_mut(sp, *e.val); VarState tpl; @@ -2117,13 +2041,10 @@ VarState& MirBuilder::get_val_state_mut(const Span& sp, const ::MIR::LValue& lv) this->push_stmt_assign(sp, inner_lv.clone(), ::MIR::RValue( mv$(*e.val) )); *e.val = inner_lv.clone(); ), - (Variable, - inner_lv = ::MIR::LValue(ei); - ), - (Temporary, + (Argument, inner_lv = ::MIR::LValue(ei); ), - (Argument, + (Local, inner_lv = ::MIR::LValue(ei); ) ) @@ -2252,20 +2173,12 @@ void MirBuilder::drop_value_from_state(const Span& sp, const VarState& vs, ::MIR void MirBuilder::drop_scope_values(const ScopeDef& sd) { TU_MATCHA( (sd.data), (e), - (Temporaries, - for(auto tmp_idx : ::reverse(e.temporaries)) - { - const auto& vs = get_temp_state(sd.span, tmp_idx); - DEBUG("tmp" << tmp_idx << " - " << vs); - drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Temporary({ tmp_idx }) ); - } - ), - (Variables, - for(auto var_idx : ::reverse(e.vars)) + (Owning, + for(auto idx : ::reverse(e.slots)) { - const auto& vs = get_variable_state(sd.span, var_idx); - DEBUG("var" << var_idx << " - " << vs); - drop_value_from_state( sd.span, vs, ::MIR::LValue::make_Variable(var_idx) ); + 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) ); } ), (Split, @@ -2273,6 +2186,8 @@ void MirBuilder::drop_scope_values(const ScopeDef& sd) ), (Loop, // No values + ), + (Freeze, ) ) } diff --git a/src/mir/operations.hpp b/src/mir/operations.hpp index 1c06bc8c..d8769dc4 100644 --- a/src/mir/operations.hpp +++ b/src/mir/operations.hpp @@ -16,5 +16,6 @@ extern void MIR_Validate_Full(const StaticTraitResolve& resolve, const ::HIR::It extern void MIR_Cleanup(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type); // Optimise the MIR extern void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type); +extern void MIR_SortBlocks(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn); extern void MIR_Dump_Fcn(::std::ostream& sink, const ::MIR::Function& fcn, unsigned int il=0); diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp index 8e350c45..73cbaa04 100644 --- a/src/mir/optimise.cpp +++ b/src/mir/optimise.cpp @@ -17,9 +17,11 @@ #include <iomanip> #include <trans/target.hpp> -#define DUMP_BEFORE_ALL 0 +#define DUMP_BEFORE_ALL 1 #define DUMP_BEFORE_CONSTPROPAGATE 0 -#define CHECK_AFTER_PASS 0 +#define CHECK_AFTER_PASS 1 +#define CHECK_AFTER_ALL 1 +#define DUMP_AFTER_PASS 1 #define DUMP_AFTER_DONE 0 #define CHECK_AFTER_DONE 1 @@ -59,16 +61,14 @@ namespace { if( cb(lv, u) ) return true; TU_MATCHA( (lv), (e), - (Variable, + (Return, ), (Argument, ), - (Temporary, + (Local, ), (Static, ), - (Return, - ), (Field, return visit_mir_lvalue_mut(*e.val, u, cb); ), @@ -222,6 +222,9 @@ namespace { (Switch, visit_mir_lvalue_mut(e.val, ValUsage::Read, cb); ), + (SwitchValue, + visit_mir_lvalue_mut(e.val, ValUsage::Read, cb); + ), (Call, if( e.fcn.is_Value() ) { visit_mir_lvalue_mut(e.fcn.as_Value(), ValUsage::Read, cb); @@ -392,6 +395,42 @@ namespace { } + void visit_terminator_target_mut(::MIR::Terminator& term, ::std::function<void(::MIR::BasicBlockId&)> cb) { + TU_MATCHA( (term), (e), + (Incomplete, + ), + (Return, + ), + (Diverge, + ), + (Goto, + cb(e); + ), + (Panic, + ), + (If, + cb(e.bb0); + cb(e.bb1); + ), + (Switch, + for(auto& target : e.targets) + cb(target); + ), + (SwitchValue, + for(auto& target : e.targets) + cb(target); + cb(e.def_target); + ), + (Call, + cb(e.ret_block); + cb(e.panic_block); + ) + ) + } + void visit_terminator_target(const ::MIR::Terminator& term, ::std::function<void(const ::MIR::BasicBlockId&)> cb) { + visit_terminator_target_mut(const_cast<::MIR::Terminator&>(term), cb); + } + void visit_blocks_mut(::MIR::TypeResolve& state, ::MIR::Function& fcn, ::std::function<void(::MIR::BasicBlockId, ::MIR::BasicBlock&)> cb) { ::std::vector<bool> visited( fcn.blocks.size() ); @@ -406,47 +445,47 @@ namespace { cb(bb, block); - TU_MATCHA( (block.terminator), (e), - (Incomplete, - ), - (Return, - ), - (Diverge, - ), - (Goto, + visit_terminator_target(block.terminator, [&](auto e){ if( !visited[e] ) to_visit.push_back(e); - ), - (Panic, - ), - (If, - if( !visited[e.bb0] ) - to_visit.push_back(e.bb0); - if( !visited[e.bb1] ) - to_visit.push_back(e.bb1); - ), - (Switch, - for(auto& target : e.targets) - if( !visited[target] ) - to_visit.push_back(target); - ), - (Call, - if( !visited[e.ret_block] ) - to_visit.push_back(e.ret_block); - if( !visited[e.panic_block] ) - to_visit.push_back(e.panic_block); - ) - ) + }); } } void visit_blocks(::MIR::TypeResolve& state, const ::MIR::Function& fcn, ::std::function<void(::MIR::BasicBlockId, const ::MIR::BasicBlock&)> cb) { visit_blocks_mut(state, const_cast<::MIR::Function&>(fcn), [cb](auto id, auto& blk){ cb(id, blk); }); } + + bool statement_invalidates_lvalue(const ::MIR::Statement& stmt, const ::MIR::LValue& lv) + { + return visit_mir_lvalues(stmt, [&](const auto& v, auto vu) { + if( v == lv ) { + return vu != ValUsage::Read; + } + return false; + }); + } + bool terminator_invalidates_lvalue(const ::MIR::Terminator& term, const ::MIR::LValue& lv) + { + 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; + }); + } + else + { + return false; + } + } } bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn); -bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool minimal); bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::Function& fcn); +bool MIR_Optimise_PropagateKnownValues(::MIR::TypeResolve& state, ::MIR::Function& fcn); bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& fcn); bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn); bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn); @@ -454,12 +493,40 @@ bool MIR_Optimise_DeadDropFlags(::MIR::TypeResolve& state, ::MIR::Function& fcn) bool MIR_Optimise_GarbageCollect_Partial(::MIR::TypeResolve& state, ::MIR::Function& fcn); bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn); -void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) +/// A minimum set of optimisations: +/// - Inlines `#[inline(always)]` functions +/// - Simplifies the call graph (by removing chained gotos) +/// - Sorts blocks into a rough flow order +void MIR_OptimiseMin(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) { static Span sp; TRACE_FUNCTION_F(path); ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; + while( MIR_Optimise_Inlining(state, fcn, true) ) + { + MIR_Cleanup(resolve, path, fcn, args, ret_type); + //MIR_Dump_Fcn(::std::cout, fcn); + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + } + + MIR_Optimise_BlockSimplify(state, fcn); + MIR_Optimise_UnifyBlocks(state, fcn); + + //MIR_Optimise_GarbageCollect_Partial(state, fcn); + + MIR_Optimise_GarbageCollect(state, fcn); + //MIR_Validate_Full(resolve, path, fcn, args, ret_type); + MIR_SortBlocks(resolve, path, fcn); + return ; +} +void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn, const ::HIR::Function::args_t& args, const ::HIR::TypeRef& ret_type) +{ + static Span sp; + TRACE_FUNCTION_F(path); + ::MIR::TypeResolve state { sp, resolve, FMT_CB(ss, ss << path;), ret_type, args, fcn }; bool change_happened; unsigned int pass_num = 0; @@ -468,39 +535,68 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path change_happened = false; TRACE_FUNCTION_FR("Pass " << pass_num, change_happened); - // >> Simplify call graph + // >> Simplify call graph (removes gotos to blocks with a single use) MIR_Optimise_BlockSimplify(state, fcn); // >> Apply known constants change_happened |= MIR_Optimise_ConstPropagte(state, fcn); - - // >> Inline short functions - bool inline_happened = MIR_Optimise_Inlining(state, fcn); - if( inline_happened ) - { - // Apply cleanup again (as monomorpisation in inlining may have exposed a vtable call) - MIR_Cleanup(resolve, path, fcn, args, ret_type); - //MIR_Dump_Fcn(::std::cout, fcn); - change_happened = true; - } + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + + // >> Replace values from composites if they're known + // - Undoes the inefficiencies from the `match (a, b) { ... }` pattern + change_happened |= MIR_Optimise_PropagateKnownValues(state, fcn); +#if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); +#endif // TODO: Convert `&mut *mut_foo` into `mut_foo` if the source is movable and not used afterwards +#if DUMP_BEFORE_ALL || DUMP_BEFORE_PSA + if( debug_enabled() ) MIR_Dump_Fcn(::std::cout, fcn); +#endif // >> Propagate/remove dead assignments while( MIR_Optimise_PropagateSingleAssignments(state, fcn) ) change_happened = true; + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); // >> Unify duplicate temporaries // If two temporaries don't overlap in lifetime (blocks in which they're valid), unify the two change_happened |= MIR_Optimise_UnifyTemporaries(state, fcn); + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif // >> Combine Duplicate Blocks change_happened |= MIR_Optimise_UnifyBlocks(state, fcn); // >> Remove assignments of unsed drop flags change_happened |= MIR_Optimise_DeadDropFlags(state, fcn); + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + + // >> Inline short functions + if( !change_happened ) + { + bool inline_happened = MIR_Optimise_Inlining(state, fcn, false); + if( inline_happened ) + { + // Apply cleanup again (as monomorpisation in inlining may have exposed a vtable call) + MIR_Cleanup(resolve, path, fcn, args, ret_type); + //MIR_Dump_Fcn(::std::cout, fcn); + change_happened = true; + } + #if CHECK_AFTER_ALL + MIR_Validate(resolve, path, fcn, args, ret_type); + #endif + } + if( change_happened ) { #if DUMP_AFTER_PASS @@ -508,7 +604,7 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path MIR_Dump_Fcn(::std::cout, fcn); } #endif - #if CHECK_AFTER_PASS + #if CHECK_AFTER_PASS && !CHECK_AFTER_ALL MIR_Validate(resolve, path, fcn, args, ret_type); #endif } @@ -531,8 +627,9 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path // - Find unused blocks, then delete and rewrite all references. MIR_Optimise_GarbageCollect(state, fcn); - //MIR_Validate(resolve, path, fcn, args, ret_type); //MIR_Validate_Full(resolve, path, fcn, args, ret_type); + + MIR_SortBlocks(resolve, path, fcn); } // -------------------------------------------------------------------- @@ -553,12 +650,9 @@ bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn) auto& dst = (it-1)->as_ScopeEnd(); const auto& src = it->as_ScopeEnd(); DEBUG("Unify " << *(it-1) << " and " << *it); - for(auto v : src.vars) - dst.vars.push_back(v); - for(auto v : src.tmps) - dst.tmps.push_back(v); - ::std::sort(dst.vars.begin(), dst.vars.end()); - ::std::sort(dst.tmps.begin(), dst.tmps.end()); + for(auto v : src.slots) + dst.slots.push_back(v); + ::std::sort(dst.slots.begin(), dst.slots.end()); it = block.statements.erase(it); } else @@ -568,32 +662,10 @@ bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn) } } - TU_MATCHA( (block.terminator), (e), - (Incomplete, - ), - (Return, - ), - (Diverge, - ), - (Goto, + visit_terminator_target_mut(block.terminator, [&](auto& e) { if( &fcn.blocks[e] != &block ) e = get_new_target(state, e); - ), - (Panic, - ), - (If, - e.bb0 = get_new_target(state, e.bb0); - e.bb1 = get_new_target(state, e.bb1); - ), - (Switch, - for(auto& target : e.targets) - target = get_new_target(state, target); - ), - (Call, - e.ret_block = get_new_target(state, e.ret_block); - e.panic_block = get_new_target(state, e.panic_block); - ) - ) + }); } // >> Merge blocks where a block goto-s to a single-use block. @@ -611,40 +683,10 @@ bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn) visited[bb] = true; const auto& block = fcn.blocks[bb]; - TU_MATCHA( (block.terminator), (e), - (Incomplete, - ), - (Return, - ), - (Diverge, - ), - (Goto, + visit_terminator_target(block.terminator, [&](const auto& e) { if( !visited[e] ) to_visit.push_back(e); uses[e] ++; - ), - (Panic, - ), - (If, - if( !visited[e.bb0] ) to_visit.push_back(e.bb0); - if( !visited[e.bb1] ) to_visit.push_back(e.bb1); - uses[e.bb0] ++; - uses[e.bb1] ++; - ), - (Switch, - for(auto& target : e.targets) - { - if( !visited[target] ) - to_visit.push_back(target); - uses[target] ++; - } - ), - (Call, - if( !visited[e.ret_block] ) to_visit.push_back(e.ret_block); - if( !visited[e.panic_block] ) to_visit.push_back(e.panic_block); - uses[e.ret_block] ++; - uses[e.panic_block] ++; - ) - ) + }); } unsigned int i = 0; @@ -685,18 +727,26 @@ bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn) // -------------------------------------------------------------------- // If two temporaries don't overlap in lifetime (blocks in which they're valid), unify the two // -------------------------------------------------------------------- -bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) +bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn, bool minimal) { TRACE_FUNCTION; struct H { - static bool can_inline(const ::HIR::Path& path, const ::MIR::Function& fcn) + static bool can_inline(const ::HIR::Path& path, const ::MIR::Function& fcn, bool minimal) { + // TODO: If the function is marked as `inline(always)`, then inline it regardless of the contents + + if( minimal ) { + return false; + } + + // TODO: If the function is marked as `inline(never)`, then don't inline + // TODO: Allow functions that are just a switch on an input. if( fcn.blocks.size() == 1 ) { - return fcn.blocks[0].statements.size() < 5 && ! fcn.blocks[0].terminator.is_Goto(); + return fcn.blocks[0].statements.size() < 10 && ! fcn.blocks[0].terminator.is_Goto(); } else if( fcn.blocks.size() == 3 && fcn.blocks[0].terminator.is_Call() ) { @@ -724,6 +774,7 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) const Span& sp; const ::StaticTraitResolve& resolve; const ::MIR::Terminator::Data_Call& te; + ::std::vector<unsigned> copy_args; // Local indexes containing copies of Copy args ParamsSet params; unsigned int bb_base = ~0u; unsigned int tmp_base = ~0u; @@ -731,13 +782,17 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) unsigned int df_base = ~0u; size_t tmp_end = 0; - mutable ::std::vector< ::MIR::Constant > const_assignments; + mutable ::std::vector< ::MIR::Param > const_assignments; + + ::MIR::LValue retval; Cloner(const Span& sp, const ::StaticTraitResolve& resolve, ::MIR::Terminator::Data_Call& te): sp(sp), resolve(resolve), - te(te) - {} + te(te), + copy_args(te.args.size(), ~0u) + { + } ::HIR::TypeRef monomorph(const ::HIR::TypeRef& ty) const { auto rv = monomorphise_type_with(sp, ty, params.get_cb(sp)); @@ -824,17 +879,20 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) ), (ScopeEnd, ::MIR::Statement::Data_ScopeEnd new_se; - new_se.vars.reserve(se.vars.size()); - for(auto idx : se.vars) - new_se.vars.push_back(this->var_base + idx); - new_se.tmps.reserve(se.tmps.size()); - for(auto idx : se.tmps) - new_se.tmps.push_back(this->tmp_base + idx); + new_se.slots.reserve(se.slots.size()); + for(auto idx : se.slots) + new_se.slots.push_back(this->var_base + idx); rv.statements.push_back(::MIR::Statement( mv$(new_se) )); ) ) + DEBUG("-> " << rv.statements.back()); } DEBUG("BB" << src_idx << "->BB" << new_idx << "/" << rv.statements.size() << ": " << src.terminator); + if(src.terminator.is_Return()) + { + rv.statements.push_back(::MIR::Statement::make_Assign({ this->te.ret_val.clone(), this->retval.clone() })); + DEBUG("++ " << rv.statements.back()); + } rv.terminator = this->clone_term(src.terminator); DEBUG("-> " << rv.terminator); return rv; @@ -871,6 +929,13 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) arms.push_back( bbi + this->bb_base ); return ::MIR::Terminator::make_Switch({ this->clone_lval(se.val), mv$(arms) }); ), + (SwitchValue, + ::std::vector<::MIR::BasicBlockId> arms; + arms.reserve(se.targets.size()); + for(const auto& bbi : se.targets) + arms.push_back( bbi + this->bb_base ); + return ::MIR::Terminator::make_SwitchValue({ this->clone_lval(se.val), se.def_target + this->bb_base, mv$(arms), se.values.clone() }); + ), (Call, ::MIR::CallTarget tgt; TU_MATCHA( (se.fcn), (ste), @@ -919,26 +984,27 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) rv.push_back( this->clone_param(lv) ); return rv; } + ::MIR::LValue clone_lval(const ::MIR::LValue& src) const { TU_MATCHA( (src), (se), - (Variable, - return ::MIR::LValue::make_Variable(se + this->var_base); - ), - (Temporary, - return ::MIR::LValue::make_Temporary({se.idx + this->tmp_base}); + (Return, + return this->retval.clone(); ), (Argument, const auto& arg = this->te.args.at(se.idx); - if( const auto* e = arg.opt_Constant() ) { - auto tmp = ::MIR::LValue::make_Temporary({ static_cast<unsigned>(this->tmp_end + this->const_assignments.size()) }); - this->const_assignments.push_back( e->clone() ); - return tmp; + if( this->copy_args[se.idx] != ~0u ) + { + return ::MIR::LValue::make_Local(this->copy_args[se.idx]); + } + else + { + assert( !arg.is_Constant() ); // Should have been handled in the above + return arg.as_LValue().clone(); } - return arg.as_LValue().clone(); ), - (Return, - return this->te.ret_val.clone(); + (Local, + return ::MIR::LValue::make_Local(this->var_base + se); ), (Static, return this->monomorph( se ); @@ -983,8 +1049,9 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) { TU_MATCHA( (src), (se), (LValue, - if( se.is_Argument() ) - return this->te.args.at(se.as_Argument().idx).clone(); + // NOTE: No need to use `copy_args` here as all uses of Param are copies/moves + //if( const auto* ae = se.opt_Argument() ) + // return this->te.args.at(ae->idx).clone(); return clone_lval(se); ), (Constant, return clone_constant(se); ) @@ -995,9 +1062,9 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) { TU_MATCHA( (src), (se), (Use, - if( se.is_Argument() ) - if( const auto* e = this->te.args.at(se.as_Argument().idx).opt_Constant() ) - return e->clone(); + //if( const auto* ae = se.opt_Argument() ) + // if( const auto* e = this->te.args.at(ae->idx).opt_Constant() ) + // return e->clone(); return ::MIR::RValue( this->clone_lval(se) ); ), (Constant, @@ -1064,24 +1131,45 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) // Inline IF: // - First BB ends with a call and total count is 3 // - Statement count smaller than 10 - if( ! H::can_inline(path, *called_mir) ) + if( ! H::can_inline(path, *called_mir, minimal) ) { DEBUG("Can't inline " << path); continue ; } + DEBUG(state << fcn.blocks[i].terminator); TRACE_FUNCTION_F("Inline " << path); - // Monomorph values and append - cloner.var_base = fcn.named_variables.size(); - for(const auto& ty : called_mir->named_variables) - fcn.named_variables.push_back( cloner.monomorph(ty) ); - cloner.tmp_base = fcn.temporaries.size(); - for(const auto& ty : called_mir->temporaries) - fcn.temporaries.push_back( cloner.monomorph(ty) ); - cloner.tmp_end = fcn.temporaries.size(); + // Allocate a temporary for the return value + { + cloner.retval = ::MIR::LValue::make_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() ); + //fcn.local_names.push_back( "" ); + } + + // Monomorph locals and append + cloner.var_base = fcn.locals.size(); + for(const auto& ty : called_mir->locals) + fcn.locals.push_back( cloner.monomorph(ty) ); + cloner.tmp_end = fcn.locals.size(); + cloner.df_base = fcn.drop_flags.size(); fcn.drop_flags.insert( fcn.drop_flags.end(), called_mir->drop_flags.begin(), called_mir->drop_flags.end() ); cloner.bb_base = fcn.blocks.size(); + + // Store all Copy lvalue arguments and Constants in variables + for(size_t i = 0; i < te->args.size(); i++) + { + const auto& a = te->args[i]; + if( !a.is_LValue() || state.lvalue_is_copy(a.as_LValue()) ) + { + cloner.copy_args[i] = cloner.tmp_end + cloner.const_assignments.size(); + cloner.const_assignments.push_back( a.clone() ); + DEBUG("- Taking a copy of arg " << i << " (" << a << ") in Local(" << cloner.copy_args[i] << ")"); + } + } + // Append monomorphised copy of all blocks. // > Arguments replaced by input lvalues ::std::vector<::MIR::BasicBlock> new_blocks; @@ -1090,13 +1178,18 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) { new_blocks.push_back( cloner.clone_bb(bb, (&bb - called_mir->blocks.data()), fcn.blocks.size() + new_blocks.size()) ); } + // > Append new temporaries for(auto& val : cloner.const_assignments) { - auto ty = state.get_const_type(val); - auto lv = ::MIR::LValue::make_Temporary({ static_cast<unsigned>(fcn.temporaries.size()) }); - fcn.temporaries.push_back( mv$(ty) ); - new_blocks[0].statements.insert( new_blocks[0].statements.begin(), ::MIR::Statement::make_Assign({ mv$(lv), mv$(val) }) ); + ::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()) ); + 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) }); + DEBUG("++ " << stmt); + new_blocks[0].statements.insert( new_blocks[0].statements.begin(), mv$(stmt) ); } cloner.const_assignments.clear(); @@ -1120,19 +1213,19 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn) bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& fcn) { TRACE_FUNCTION; - ::std::vector<bool> replacable( fcn.temporaries.size() ); + ::std::vector<bool> replacable( fcn.locals.size() ); // 1. Enumerate which (if any) temporaries share the same type { unsigned int n_found = 0; - for(unsigned int tmpidx = 0; tmpidx < fcn.temporaries.size(); tmpidx ++) + for(unsigned int tmpidx = 0; tmpidx < fcn.locals.size(); tmpidx ++) { if( replacable[tmpidx] ) continue ; - for(unsigned int i = tmpidx+1; i < fcn.temporaries.size(); i ++ ) + for(unsigned int i = tmpidx+1; i < fcn.locals.size(); i ++ ) { if( replacable[i] ) continue ; - if( fcn.temporaries[i] == fcn.temporaries[tmpidx] ) + if( fcn.locals[i] == fcn.locals[tmpidx] ) { replacable[i] = true; replacable[tmpidx] = true; @@ -1145,34 +1238,33 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f } auto lifetimes = MIR_Helper_GetLifetimes(state, fcn, /*dump_debug=*/true); - //::std::vector<::MIR::ValueLifetime> var_lifetimes = mv$(lifetimes.m_variables); - ::std::vector<::MIR::ValueLifetime> tmp_lifetimes = mv$(lifetimes.m_temporaries); + ::std::vector<::MIR::ValueLifetime> slot_lifetimes = mv$(lifetimes.m_slots); // 2. Unify variables of the same type with distinct non-overlapping lifetimes ::std::map<unsigned int, unsigned int> replacements; - ::std::vector<bool> visited( fcn.temporaries.size() ); + ::std::vector<bool> visited( fcn.locals.size() ); bool replacement_needed = false; - for(unsigned int tmpidx = 0; tmpidx < fcn.temporaries.size(); tmpidx ++) + for(unsigned int local_idx = 0; local_idx < fcn.locals.size(); local_idx ++) { - if( ! replacable[tmpidx] ) continue ; - if( visited[tmpidx] ) continue ; - if( ! tmp_lifetimes[tmpidx].is_used() ) continue ; - visited[tmpidx] = true; + if( ! replacable[local_idx] ) continue ; + if( visited[local_idx] ) continue ; + if( ! slot_lifetimes[local_idx].is_used() ) continue ; + visited[local_idx] = true; - for(unsigned int i = tmpidx+1; i < fcn.temporaries.size(); i ++) + for(unsigned int i = local_idx+1; i < fcn.locals.size(); i ++) { if( !replacable[i] ) continue ; - if( fcn.temporaries[i] != fcn.temporaries[tmpidx] ) + if( fcn.locals[i] != fcn.locals[local_idx] ) continue ; - if( ! tmp_lifetimes[i].is_used() ) + if( ! slot_lifetimes[i].is_used() ) continue ; // Variables are of the same type, check if they overlap - if( tmp_lifetimes[tmpidx].overlaps( tmp_lifetimes[i] ) ) + if( slot_lifetimes[local_idx].overlaps( slot_lifetimes[i] ) ) continue ; // They don't overlap, unify - tmp_lifetimes[tmpidx].unify( tmp_lifetimes[i] ); - replacements[i] = tmpidx; + slot_lifetimes[local_idx].unify( slot_lifetimes[i] ); + replacements[i] = local_idx; replacement_needed = true; visited[i] = true; } @@ -1182,12 +1274,12 @@ 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_Temporary() ) { - auto it = replacements.find(ve->idx); + if( auto* ve = lv.opt_Local() ) { + auto it = replacements.find(*ve); if( it != replacements.end() ) { - MIR_DEBUG(state, lv << " => Temporary(" << it->second << ")"); - ve->idx = it->second; + MIR_DEBUG(state, lv << " => Local(" << it->second << ")"); + *ve = it->second; return true; } } @@ -1201,6 +1293,7 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f } // -------------------------------------------------------------------- +// Combine identical blocks // -------------------------------------------------------------------- bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn) { @@ -1249,9 +1342,7 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn) return false; ), (ScopeEnd, - if( ae.vars != be.vars ) - return false; - if( ae.tmps == be.tmps ) + if( ae.slots != be.slots ) return false; ) ) @@ -1287,6 +1378,30 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn) if( ae.targets != be.targets ) return false; ), + (SwitchValue, + if( ae.val != be.val ) + return false; + if( ae.targets != be.targets ) + return false; + if( ae.def_target != be.def_target ) + return false; + if( ae.values.tag() != be.values.tag() ) + return false; + TU_MATCHA( (ae.values, be.values), (ae2, be2), + (Unsigned, + if( ae2 != be2 ) + return false; + ), + (Signed, + if( ae2 != be2 ) + return false; + ), + (String, + if( ae2 != be2 ) + return false; + ) + ) + ), (Call, if( ae.ret_block != be.ret_block ) return false; @@ -1358,32 +1473,9 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn) { if( bb.terminator.tag() == ::MIR::Terminator::TAGDEAD ) continue ; - TU_MATCHA( (bb.terminator), (te), - (Incomplete, - ), - (Return, - ), - (Diverge, - ), - (Goto, + visit_terminator_target_mut(bb.terminator, [&](auto& te) { patch_tgt(te); - ), - (Panic, - patch_tgt(te.dst); - ), - (If, - patch_tgt(te.bb0); - patch_tgt(te.bb1); - ), - (Switch, - for(auto& tgt : te.targets) - patch_tgt(tgt); - ), - (Call, - patch_tgt(te.ret_block); - patch_tgt(te.panic_block); - ) - ) + }); //DEBUG("- " << bb.terminator); } @@ -1401,6 +1493,165 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn) } } +// -------------------------------------------------------------------- +// Propagate source values when a composite (tuple) is read +// -------------------------------------------------------------------- +bool MIR_Optimise_PropagateKnownValues(::MIR::TypeResolve& state, ::MIR::Function& fcn) +{ + TRACE_FUNCTION; + // 1. Determine reference counts for blocks (allows reversing up BB tree) + ::std::vector<size_t> block_origins( fcn.blocks.size(), SIZE_MAX ); + { + ::std::vector<unsigned int> block_uses( fcn.blocks.size() ); + ::std::vector<bool> visited( fcn.blocks.size() ); + ::std::vector< ::MIR::BasicBlockId> to_visit; + to_visit.push_back( 0 ); + block_uses[0] ++; + while( to_visit.size() > 0 ) + { + auto bb = to_visit.back(); to_visit.pop_back(); + if( visited[bb] ) + continue ; + visited[bb] = true; + const auto& block = fcn.blocks[bb]; + + visit_terminator_target(block.terminator, [&](const auto& idx) { + if( !visited[idx] ) + to_visit.push_back(idx); + if(block_uses[idx] == 0) + block_origins[idx] = bb; + else + block_origins[idx] = SIZE_MAX; + block_uses[idx] ++; + }); + } + } + + // 2. Find any assignments (or function uses?) of the form FIELD(LOCAL, _) + // > Restricted to simplify logic (and because that's the inefficient pattern observed) + // 3. Search backwards from that point until the referenced local is assigned + bool change_happend = false; + auto get_field = [&](const ::MIR::LValue& slot_lvalue, unsigned field, size_t start_bb_idx, size_t start_stmt_idx)->const ::MIR::LValue* { + TRACE_FUNCTION_F(slot_lvalue << "." << field << " BB" << start_bb_idx << "/" << start_stmt_idx); + // NOTE: An infinite loop is (theoretically) impossible. + auto bb_idx = start_bb_idx; + auto stmt_idx = start_stmt_idx; + for(;;) + { + const auto& bb = fcn.blocks[bb_idx]; + while(stmt_idx --) + { + if( stmt_idx == bb.statements.size() ) + { + DEBUG("BB" << bb_idx << "/TERM - " << bb.terminator); + if( terminator_invalidates_lvalue(bb.terminator, slot_lvalue) ) { + return nullptr; + } + continue ; + } + const auto& stmt = bb.statements[stmt_idx]; + DEBUG("BB" << bb_idx << "/" << stmt_idx << " - " << stmt); + if( const auto* se = stmt.opt_Assign() ) + { + if( se->dst == slot_lvalue ) + { + if( !se->src.is_Tuple() ) + return nullptr; + const auto& src_param = se->src.as_Tuple().vals.at(field); + DEBUG("> Found a source " << src_param); + // TODO: Support returning a Param + if( !src_param.is_LValue() ) + return nullptr; + const auto& src_lval = src_param.as_LValue(); + // Visit all statements between the start and here, checking for mutation of this value. + auto end_bb_idx = bb_idx; + auto end_stmt_idx = stmt_idx; + bb_idx = start_bb_idx; + stmt_idx = start_stmt_idx; + for(;;) + { + const auto& bb = fcn.blocks[bb_idx]; + while(stmt_idx--) + { + if(bb_idx == end_bb_idx && stmt_idx == end_stmt_idx) + return &src_lval; + if(stmt_idx == bb.statements.size()) + { + DEBUG("BB" << bb_idx << "/TERM - " << bb.terminator); + if( terminator_invalidates_lvalue(bb.terminator, src_lval) ) { + // Invalidated: Return. + return nullptr; + } + continue ; + } + if( statement_invalidates_lvalue(bb.statements[stmt_idx], src_lval) ) { + // Invalidated: Return. + return nullptr; + } + } + assert( block_origins[bb_idx] != SIZE_MAX ); + bb_idx = block_origins[bb_idx]; + stmt_idx = fcn.blocks[bb_idx].statements.size() + 1; + } + throw ""; + } + } + + // Check if the slot is invalidated (mutated) + if( statement_invalidates_lvalue(stmt, slot_lvalue) ) { + return nullptr; + } + } + if( block_origins[bb_idx] == SIZE_MAX ) + break; + bb_idx = block_origins[bb_idx]; + stmt_idx = fcn.blocks[bb_idx].statements.size() + 1; + } + return nullptr; + }; + for(auto& block : fcn.blocks) + { + size_t bb_idx = &block - &fcn.blocks.front(); + for(size_t i = 0; i < block.statements.size(); i++) + { + 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 && 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)) ) + { + DEBUG(state << "- not Copy, can't optimise"); + return false; + } + const auto* source_lvalue = get_field(*e->val, e->field_index, bb_idx, i); + if( source_lvalue ) + { + if( lv != *source_lvalue ) + { + DEBUG(state << "Source is " << *source_lvalue); + lv = source_lvalue->clone(); + change_happend = true; + } + else + { + DEBUG(state << "No change"); + } + return false; + } + } + } + return false; + }); + } + } + return change_happend; +} // -------------------------------------------------------------------- // Propagate constants and eliminate known paths @@ -1721,7 +1972,7 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn) // - Locate `temp = SOME_CONST` and record value if( const auto* e = stmt.opt_Assign() ) { - if( e->dst.is_Temporary() || e->dst.is_Variable() ) + if( e->dst.is_Local() ) { if( const auto* ce = e->src.opt_Constant() ) { @@ -1743,9 +1994,7 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn) const auto& te = bb.terminator.as_If(); // Restrict condition to being a temporary/variable - if( te.cond.is_Temporary() ) - ; - else if( te.cond.is_Argument() ) + if( te.cond.is_Local() ) ; else continue; @@ -1813,24 +2062,16 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F unsigned int borrow = 0; }; struct { - ::std::vector<ValUse> var_uses; - ::std::vector<ValUse> tmp_uses; + ::std::vector<ValUse> local_uses; void use_lvalue(const ::MIR::LValue& lv, ValUsage ut) { TU_MATCHA( (lv), (e), - (Variable, - auto& vu = var_uses[e]; - switch(ut) - { - case ValUsage::Read: vu.read += 1; break; - case ValUsage::Write: vu.write += 1; break; - case ValUsage::Borrow: vu.borrow += 1; break; - } + (Return, ), (Argument, ), - (Temporary, - auto& vu = tmp_uses[e.idx]; + (Local, + auto& vu = local_uses[e]; switch(ut) { case ValUsage::Read: vu.read += 1; break; @@ -1840,8 +2081,6 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F ), (Static, ), - (Return, - ), (Field, use_lvalue(*e.val, ut); ), @@ -1858,8 +2097,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F ) } } val_uses = { - ::std::vector<ValUse>(fcn.named_variables.size()), - ::std::vector<ValUse>(fcn.temporaries.size()) + ::std::vector<ValUse>(fcn.locals.size()) }; visit_mir_lvalues(state, fcn, [&](const auto& lv, auto ut){ val_uses.use_lvalue(lv, ut); return false; }); @@ -1886,19 +2124,10 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F continue ; const auto& e = stmt.as_Assign(); // > Of a temporary from with a RValue::Use - if( const auto* de = e.dst.opt_Temporary() ) + if( const auto* de = e.dst.opt_Local() ) { - const auto& vu = val_uses.tmp_uses[de->idx]; - DEBUG(e.dst << " - VU " << e.dst << " R:" << vu.read << " W:" << vu.write); - // TODO: Allow write many? - // > Where the temporary is written once and read once - if( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) - continue ; - } - else if( const auto* de = e.dst.opt_Variable() ) - { - const auto& vu = val_uses.var_uses[*de]; - DEBUG(e.dst << " - VU " << e.dst << " R:" << vu.read << " W:" << vu.write); + const auto& vu = val_uses.local_uses[*de]; + DEBUG(e.dst << " - 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 ) ) @@ -1915,7 +2144,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F const auto* srcp = &e.src.as_Use(); while( srcp->is_Field() ) srcp = &*srcp->as_Field().val; - if( !( srcp->is_Temporary() || srcp->is_Variable() || srcp->is_Argument() ) ) + if( !srcp->is_Local() ) continue ; if( replacements.find(*srcp) != replacements.end() ) @@ -2004,6 +2233,11 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F 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) ) @@ -2030,6 +2264,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F } // for(stmt : block.statements) } + // Apply replacements within replacements for(;;) { unsigned int inner_replaced_count = 0; @@ -2131,9 +2366,9 @@ 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_Temporary() ) { - const auto& vu = val_uses.tmp_uses[e->idx]; - if( !( vu.read == 1 && vu.write == 1 ) ) + if( const auto* e = to_replace_lval.opt_Local() ) { + const auto& vu = val_uses.local_uses[*e]; + if( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) continue ; } else { @@ -2155,6 +2390,19 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F const auto& new_dst_lval = it2->as_Assign().dst; // `... = 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( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) + break ; + } + else if( new_dst_lval.is_Return() ) { + // Return, can't be borrowed? + } + else { + break; + } + // Ensure that the target doesn't change in the intervening time. bool was_invalidated = false; for(auto it3 = it+1; it3 != it2; it3++) @@ -2201,10 +2449,9 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F { // TODO: What if the destination located here is a 1:1 and its usage is listed to be replaced by the return value. auto& e = block.terminator.as_Call(); - // TODO: Support variables too? - if( !e.ret_val.is_Temporary() ) + if( !e.ret_val.is_Local() ) continue ; - const auto& vu = val_uses.tmp_uses[e.ret_val.as_Temporary().idx]; + const auto& vu = val_uses.local_uses[e.ret_val.as_Local()]; if( !( vu.read == 1 && vu.write == 1 && vu.borrow == 0 ) ) continue ; @@ -2256,22 +2503,28 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F { for(auto it = block.statements.begin(); it != block.statements.end(); ++it) { + state.set_cur_stmt(&block - &fcn.blocks.front(), it - block.statements.begin()); if( const auto& se = it->opt_Assign() ) { - TU_MATCH_DEF( ::MIR::LValue, (se->dst), (de), - ( - ), - (Variable, - const auto& vu = val_uses.var_uses[de]; - if( vu.write == 1 && vu.read == 0 && vu.borrow == 0 ) { - DEBUG(se->dst << " only written, removing write"); + // Remove No-op assignments (assignment from a lvalue to itself) + if( const auto* src_e = se->src.opt_Use() ) + { + if( se->dst == *src_e ) + { + DEBUG(state << se->dst << " set to itself, removing write"); it = block.statements.erase(it)-1; + continue ; } + } + + // Remove assignments of locals that are never read + TU_MATCH_DEF( ::MIR::LValue, (se->dst), (de), + ( ), - (Temporary, - const auto& vu = val_uses.tmp_uses[de.idx]; + (Local, + const auto& vu = val_uses.local_uses[de]; if( vu.write == 1 && vu.read == 0 && vu.borrow == 0 ) { - DEBUG(se->dst << " only written, removing write with " << se->src); + DEBUG(state << se->dst << " only written, removing write"); it = block.statements.erase(it)-1; } ) @@ -2355,8 +2608,7 @@ bool MIR_Optimise_GarbageCollect_Partial(::MIR::TypeResolve& state, ::MIR::Funct // -------------------------------------------------------------------- bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn) { - ::std::vector<bool> used_temps( fcn.temporaries.size() ); - ::std::vector<bool> used_vars( fcn.named_variables.size() ); + ::std::vector<bool> used_locals( fcn.locals.size() ); ::std::vector<bool> used_dfs( fcn.drop_flags.size() ); ::std::vector<bool> visited( fcn.blocks.size() ); @@ -2364,10 +2616,8 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn visited[bb] = true; auto assigned_lval = [&](const ::MIR::LValue& lv) { - if(const auto* le = lv.opt_Temporary() ) - used_temps[le->idx] = true; - if(const auto* le = lv.opt_Variable() ) - used_vars[*le] = true; + if(const auto* le = lv.opt_Local() ) + used_locals[*le] = true; }; for(const auto& stmt : block.statements) @@ -2404,42 +2654,28 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn { block_rewrite_table.push_back( visited[i] ? j ++ : ~0u ); } - ::std::vector<unsigned int> temp_rewrite_table; - unsigned int n_temp = fcn.temporaries.size(); - for(unsigned int i = 0, j = 0; i < n_temp; i ++) + ::std::vector<unsigned int> local_rewrite_table; + unsigned int n_locals = fcn.locals.size(); + for(unsigned int i = 0, j = 0; i < n_locals; i ++) { - if( !used_temps[i] ) + if( !used_locals[i] ) { - fcn.temporaries.erase(fcn.temporaries.begin() + j); + fcn.locals.erase(fcn.locals.begin() + j); } else { - DEBUG("tmp$" << i << " => tmp$" << j); + DEBUG("_" << i << " => _" << j); } - temp_rewrite_table.push_back( used_temps[i] ? j ++ : ~0u ); + local_rewrite_table.push_back( used_locals[i] ? j ++ : ~0u ); } - DEBUG("Deleted Temporaries:" << FMT_CB(ss, - for(auto run : runs(used_temps)) - if( !used_temps[run.first] ) + DEBUG("Deleted Locals:" << FMT_CB(ss, + for(auto run : runs(used_locals)) + if( !used_locals[run.first] ) { ss << " " << run.first; if(run.second != run.first) ss << "-" << run.second; } )); - ::std::vector<unsigned int> var_rewrite_table; - unsigned int n_var = fcn.named_variables.size(); - for(unsigned int i = 0, j = 0; i < n_var; i ++) - { - if( !used_vars[i] ) - { - DEBUG("GC Variable(" << i << ")"); - fcn.named_variables.erase(fcn.named_variables.begin() + j); - } - else { - DEBUG("var$" << i << " => var$" << j); - } - var_rewrite_table.push_back( used_vars[i] ? j ++ : ~0u ); - } ::std::vector<unsigned int> df_rewrite_table; unsigned int n_df = fcn.drop_flags.size(); for(unsigned int i = 0, j = 0; i < n_df; i ++) @@ -2464,17 +2700,11 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn else { auto lvalue_cb = [&](auto& lv, auto ) { - if(auto* e = lv.opt_Temporary() ) { - MIR_ASSERT(state, e->idx < temp_rewrite_table.size(), "Temporary out of range - " << lv); - // If the table entry for this temporary is !0, it wasn't marked as used - MIR_ASSERT(state, temp_rewrite_table.at(e->idx) != ~0u, "LValue " << lv << " incorrectly marked as unused"); - e->idx = temp_rewrite_table.at(e->idx); - } - if(auto* e = lv.opt_Variable() ) { - MIR_ASSERT(state, *e < var_rewrite_table.size(), "Variable out of range - " << lv); + if(auto* e = lv.opt_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, var_rewrite_table.at(*e) != ~0u, "LValue " << lv << " incorrectly marked as unused"); - *e = var_rewrite_table.at(*e); + MIR_ASSERT(state, local_rewrite_table.at(*e) != ~0u, "LValue " << lv << " incorrectly marked as unused"); + *e = local_rewrite_table.at(*e); } return false; }; @@ -2520,28 +2750,18 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn } else if( auto* se = stmt.opt_ScopeEnd() ) { - for(auto it = se->vars.begin(); it != se->vars.end(); ) - { - if( var_rewrite_table.at(*it) == ~0u ) { - it = se->vars.erase(it); - } - else { - *it = var_rewrite_table.at(*it); - ++ it; - } - } - for(auto it = se->tmps.begin(); it != se->tmps.end(); ) + for(auto it = se->slots.begin(); it != se->slots.end(); ) { - if( temp_rewrite_table.at(*it) == ~0u ) { - it = se->tmps.erase(it); + if( local_rewrite_table.at(*it) == ~0u ) { + it = se->slots.erase(it); } else { - *it = temp_rewrite_table.at(*it); + *it = local_rewrite_table.at(*it); ++ it; } } - if( se->vars.empty() && se->tmps.empty() ) { + if( se->slots.empty() ) { DEBUG(state << "Delete ScopeEnd (now empty)"); to_remove_statements[stmt_idx] = true; continue ; @@ -2572,6 +2792,12 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn 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); @@ -2616,11 +2842,101 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn return false; } -void MIR_OptimiseCrate(::HIR::Crate& crate) + +/// Sort basic blocks to approximate program flow (helps when reading MIR) +void MIR_SortBlocks(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path, ::MIR::Function& fcn) { - ::MIR::OuterVisitor ov { crate, [](const auto& res, const auto& p, auto& expr, const auto& args, const auto& ty) + ::std::vector<bool> visited( fcn.blocks.size() ); + ::std::vector<::std::pair<unsigned,unsigned>> depths( fcn.blocks.size() ); + + struct Todo { + size_t bb_idx; + unsigned branch_count; + unsigned level; + }; + unsigned int branches = 0; + ::std::vector<Todo> todo; + todo.push_back( Todo { 0, 0, 0 } ); + + while(!todo.empty()) + { + auto info = todo.back(); + todo.pop_back(); + if( visited[info.bb_idx] ) + continue ; + + visited[info.bb_idx] = true; + depths[info.bb_idx] = ::std::make_pair( info.branch_count, info.level ); + const auto& bb = fcn.blocks[info.bb_idx]; + + TU_MATCHA( (bb.terminator), (te), + (Incomplete, + ), + (Return, + ), + (Diverge, + ), + (Goto, + todo.push_back(Todo { te, info.branch_count, info.level + 1 }); + ), + (Panic, + todo.push_back(Todo { te.dst, info.branch_count, info.level + 1 }); + ), + (If, + todo.push_back(Todo { te.bb0, ++branches, info.level + 1 }); + todo.push_back(Todo { te.bb1, ++branches, info.level + 1 }); + ), + (Switch, + for(auto dst : te.targets) + todo.push_back(Todo { dst, ++branches, info.level + 1 }); + ), + (SwitchValue, + for(auto dst : te.targets) + todo.push_back(Todo { dst, ++branches, info.level + 1 }); + todo.push_back(Todo { te.def_target, info.branch_count, info.level + 1 }); + ), + (Call, + todo.push_back(Todo { te.ret_block, info.branch_count, info.level + 1 }); + todo.push_back(Todo { te.panic_block, ++branches, info.level + 1 }); + ) + ) + } + + // Sort a list of block indexes by `depths` + ::std::vector<size_t> idxes; + idxes.reserve(fcn.blocks.size()); + for(size_t i = 0; i < fcn.blocks.size(); i++) + idxes.push_back(i); + ::std::sort( idxes.begin(), idxes.end(), [&](auto a, auto b){ + return depths.at(a) < depths.at(b); + }); + + DEBUG(idxes); + + decltype(fcn.blocks) new_block_list; + new_block_list.reserve( fcn.blocks.size() ); + for(auto idx : idxes) + { + auto fix_bb_idx = [&](auto idx){ return ::std::find(idxes.begin(), idxes.end(), idx) - idxes.begin(); }; + new_block_list.push_back( mv$(fcn.blocks[idx]) ); + visit_terminator_target_mut(new_block_list.back().terminator, [&](auto& te){ + te = fix_bb_idx(te); + }); + } + fcn.blocks = mv$(new_block_list); +} + + +void MIR_OptimiseCrate(::HIR::Crate& crate, bool do_minimal_optimisation) +{ + ::MIR::OuterVisitor ov { crate, [do_minimal_optimisation](const auto& res, const auto& p, auto& expr, const auto& args, const auto& ty) { - MIR_Optimise(res, p, *expr.m_mir, args, ty); + if( do_minimal_optimisation ) { + MIR_OptimiseMin(res, p, *expr.m_mir, args, ty); + } + else { + MIR_Optimise(res, p, *expr.m_mir, args, ty); + } } }; ov.visit_crate(crate); diff --git a/src/parse/expr.cpp b/src/parse/expr.cpp index 28729b93..547e38e0 100644 --- a/src/parse/expr.cpp +++ b/src/parse/expr.cpp @@ -20,7 +20,8 @@ using AST::ExprNode; using AST::ExprNodeP; -static inline ExprNodeP mk_exprnodep(const TokenStream& lex, AST::ExprNode* en){en->set_pos(lex.getPosition()); return ExprNodeP(en); } +// TODO: Use a ProtoSpan +static inline ExprNodeP mk_exprnodep(const TokenStream& lex, AST::ExprNode* en){en->set_span(lex.point_span()); return ExprNodeP(en); } #define NEWNODE(type, ...) mk_exprnodep(lex, new type(__VA_ARGS__)) //ExprNodeP Parse_ExprBlockNode(TokenStream& lex, bool is_unsafe=false); // common.hpp @@ -552,7 +553,7 @@ ExprNodeP Parse_Stmt_Let(TokenStream& lex) { Token tok; AST::Pattern pat = Parse_Pattern(lex, false); // irrefutable - TypeRef type { lex.getPosition() }; + TypeRef type { lex.point_span() }; if( GET_TOK(tok, lex) == TOK_COLON ) { type = Parse_Type(lex); GET_TOK(tok, lex); @@ -969,7 +970,7 @@ ExprNodeP Parse_ExprVal_StructLiteral(TokenStream& lex, AST::Path path) GET_CHECK_TOK(tok, lex, TOK_COLON); ExprNodeP val = Parse_Stmt(lex); if( ! nodes.insert( ::std::make_pair(ofs, mv$(val)) ).second ) { - ERROR(lex.getPosition(), E0000, "Duplicate index"); + ERROR(lex.point_span(), E0000, "Duplicate index"); } if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) @@ -983,7 +984,7 @@ ExprNodeP Parse_ExprVal_StructLiteral(TokenStream& lex, AST::Path path) for(auto& p : nodes) { if( p.first != i ) { - ERROR(lex.getPosition(), E0000, "Missing index " << i); + ERROR(lex.point_span(), E0000, "Missing index " << i); } items.push_back( mv$(p.second) ); i ++; @@ -1041,7 +1042,7 @@ ExprNodeP Parse_ExprVal_Closure(TokenStream& lex, bool is_move) // Irrefutable pattern AST::Pattern pat = Parse_Pattern(lex, false); - TypeRef type { lex.getPosition() }; + TypeRef type { lex.point_span() }; if( GET_TOK(tok, lex) == TOK_COLON ) type = Parse_Type(lex); else @@ -1054,7 +1055,7 @@ ExprNodeP Parse_ExprVal_Closure(TokenStream& lex, bool is_move) } CHECK_TOK(tok, TOK_PIPE); - auto rt = TypeRef(lex.getPosition()); + auto rt = TypeRef(lex.point_span()); if( GET_TOK(tok, lex) == TOK_THINARROW ) { if( GET_TOK(tok, lex) == TOK_EXCLAM ) { @@ -1264,8 +1265,9 @@ ExprNodeP Parse_ExprVal(TokenStream& lex) } ExprNodeP Parse_ExprMacro(TokenStream& lex, AST::Path path) { + ASSERT_BUG(lex.point_span(), path.is_trivial(), "TODO: Support path macros - " << path); + Token tok; - ASSERT_BUG(lex.getPosition(), path.is_trivial(), "TODO: Support path macros - " << path); ::std::string name = path.m_class.is_Local() ? path.m_class.as_Local().name : path.nodes()[0].name(); ::std::string ident; if( GET_TOK(tok, lex) == TOK_IDENT ) { diff --git a/src/parse/lex.cpp b/src/parse/lex.cpp index 0587e443..9388fca4 100644 --- a/src/parse/lex.cpp +++ b/src/parse/lex.cpp @@ -223,8 +223,8 @@ signed int Lexer::getSymbol() bool issym(Codepoint ch) { - if('0' <= ch.v && ch.v <= '9') - return true; + if('0' <= ch.v && ch.v <= '9') + return true; if( ::std::isalpha(ch.v) ) return true; if( ch == '_' ) @@ -668,7 +668,7 @@ Token Lexer::getTokenInt() { return Token(TOK_EOF); } - throw "Fell off the end of getTokenInt"; + throw "Fell off the end of getTokenInt"; } Token Lexer::getTokenInt_RawString(bool is_byte) @@ -983,7 +983,7 @@ bool Codepoint::isspace() const { case ' ': case 0xC: // ^L case 0x85: - case 0x200E: case 0x200F: // LTR / RTL markers + case 0x200E: case 0x200F: // LTR / RTL markers case 0x2028: // Line Separator case 0x2029: // Paragrah Separator return true; diff --git a/src/parse/parseerror.cpp b/src/parse/parseerror.cpp index 3c5d41fe..1bb30985 100644 --- a/src/parse/parseerror.cpp +++ b/src/parse/parseerror.cpp @@ -19,13 +19,13 @@ CompileError::Generic::Generic(::std::string message): } CompileError::Generic::Generic(const TokenStream& lex, ::std::string message) { - ::std::cout << lex.getPosition() << ": Generic(" << message << ")" << ::std::endl; + ::std::cout << lex.point_span() << ": Generic(" << message << ")" << ::std::endl; } CompileError::BugCheck::BugCheck(const TokenStream& lex, ::std::string message): m_message(message) { - ::std::cout << lex.getPosition() << "BugCheck(" << message << ")" << ::std::endl; + ::std::cout << lex.point_span() << "BugCheck(" << message << ")" << ::std::endl; } CompileError::BugCheck::BugCheck(::std::string message): m_message(message) @@ -41,7 +41,7 @@ CompileError::Todo::Todo(::std::string message): CompileError::Todo::Todo(const TokenStream& lex, ::std::string message): m_message(message) { - ::std::cout << lex.getPosition() << ": Todo(" << message << ")" << ::std::endl; + ::std::cout << lex.point_span() << ": Todo(" << message << ")" << ::std::endl; } CompileError::Todo::~Todo() throw() { @@ -49,7 +49,7 @@ CompileError::Todo::~Todo() throw() ParseError::BadChar::BadChar(const TokenStream& lex, char character) { - ::std::cout << lex.getPosition() << ": BadChar(" << character << ")" << ::std::endl; + ::std::cout << lex.point_span() << ": BadChar(" << character << ")" << ::std::endl; } ParseError::BadChar::~BadChar() throw() { @@ -58,24 +58,24 @@ ParseError::BadChar::~BadChar() throw() ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok)//: // m_tok( mv$(tok) ) { - auto pos = tok.get_pos(); + Span pos = tok.get_pos(); if(pos.filename == "") - pos = lex.getPosition(); + pos = lex.point_span(); ::std::cout << pos << ": Unexpected(" << tok << ")" << ::std::endl; } ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok, Token exp)//: // m_tok( mv$(tok) ) { - auto pos = tok.get_pos(); + Span pos = tok.get_pos(); if(pos.filename == "") - pos = lex.getPosition(); + pos = lex.point_span(); ::std::cout << pos << ": Unexpected(" << tok << ", " << exp << ")" << ::std::endl; } ParseError::Unexpected::Unexpected(const TokenStream& lex, const Token& tok, ::std::vector<eTokenType> exp) { - auto pos = tok.get_pos(); + Span pos = tok.get_pos(); if(pos.filename == "") - pos = lex.getPosition(); + pos = lex.point_span(); ::std::cout << pos << ": Unexpected " << tok << ", expected "; bool f = true; for(auto v: exp) { diff --git a/src/parse/pattern.cpp b/src/parse/pattern.cpp index e9d086f9..974ff5fb 100644 --- a/src/parse/pattern.cpp +++ b/src/parse/pattern.cpp @@ -258,7 +258,7 @@ AST::Pattern Parse_PatternReal1(TokenStream& lex, bool is_refutable) return AST::Pattern( AST::Pattern::TagValue(), AST::Pattern::Value::make_Float({n->m_datatype, n->m_value}) ); } else { - TODO(lex.getPosition(), "Convert :expr into a pattern value - " << *e); + TODO(lex.point_span(), "Convert :expr into a pattern value - " << *e); } } break; @@ -414,7 +414,7 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut GET_CHECK_TOK(tok, lex, TOK_COLON); auto val = Parse_Pattern(lex, is_refutable); if( ! pats.insert( ::std::make_pair(ofs, mv$(val)) ).second ) { - ERROR(lex.getPosition(), E0000, "Duplicate index"); + ERROR(lex.point_span(), E0000, "Duplicate index"); } if( GET_TOK(tok,lex) == TOK_BRACE_CLOSE ) @@ -435,7 +435,7 @@ AST::Pattern Parse_PatternStruct(TokenStream& lex, AST::Path path, bool is_refut { if( p.first != i ) { if( has_split || !split_allowed ) { - ERROR(lex.getPosition(), E0000, "Missing index " << i); + ERROR(lex.point_span(), E0000, "Missing index " << i); } has_split = true; i = p.first; diff --git a/src/parse/root.cpp b/src/parse/root.cpp index 5f8ed0a3..0790f05b 100644 --- a/src/parse/root.cpp +++ b/src/parse/root.cpp @@ -196,7 +196,7 @@ AST::GenericParams Parse_GenericParams(TokenStream& lex) ::std::string param_name = mv$(tok.str()); ret.add_ty_param( AST::TypeParam( param_name ) ); - auto param_ty = TypeRef(lex.getPosition(), param_name); + auto param_ty = TypeRef(lex.point_span(), param_name); if( GET_TOK(tok, lex) == TOK_COLON ) { Parse_TypeBound(lex, ret, mv$(param_ty)); @@ -382,7 +382,7 @@ AST::Function Parse_FunctionDef(TokenStream& lex, ::std::string abi, bool allow_ GET_TOK(tok, lex); if( allow_self == false ) throw ParseError::Generic(lex, "Self binding not expected"); - TypeRef ty = TypeRef( lex.getPosition(), "Self", 0xFFFF ); + TypeRef ty = TypeRef( lex.point_span(), "Self", 0xFFFF ); if( GET_TOK(tok, lex) == TOK_COLON ) { // Typed mut self ty = Parse_Type(lex); @@ -399,7 +399,7 @@ AST::Function Parse_FunctionDef(TokenStream& lex, ::std::string abi, bool allow_ // By-value method if( allow_self == false ) throw ParseError::Generic(lex, "Self binding not expected"); - TypeRef ty = TypeRef( lex.getPosition(), "Self", 0xFFFF ); + TypeRef ty = TypeRef( lex.point_span(), "Self", 0xFFFF ); if( GET_TOK(tok, lex) == TOK_COLON ) { // Typed mut self ty = Parse_Type(lex); @@ -723,14 +723,14 @@ AST::Trait Parse_TraitDef(TokenStream& lex, const AST::MetaItems& meta_items) if( GET_TOK(tok, lex) == TOK_COLON ) { // Bounded associated type - Parse_TypeBound(lex, atype_params, TypeRef(lex.getPosition(), "Self", 0xFFFF)); + Parse_TypeBound(lex, atype_params, TypeRef(lex.point_span(), "Self", 0xFFFF)); GET_TOK(tok, lex); } if( tok.type() == TOK_RWORD_WHERE ) { throw ParseError::Todo(lex, "Where clause on associated type"); } - TypeRef default_type = TypeRef( lex.getPosition() ); + TypeRef default_type = TypeRef( lex.point_span() ); if( tok.type() == TOK_EQUAL ) { default_type = Parse_Type(lex); GET_TOK(tok, lex); @@ -1049,7 +1049,7 @@ AST::MetaItem Parse_MetaItem(TokenStream& lex) if( GET_TOK(tok, lex) == TOK_DOUBLE_DOT ) { // Default impl - impl_type = TypeRef(TypeRef::TagInvalid(), lex.getPosition()); + impl_type = TypeRef(TypeRef::TagInvalid(), lex.point_span()); } else { @@ -1390,7 +1390,7 @@ void Parse_Use(TokenStream& lex, ::std::function<void(AST::UseStmt, ::std::strin else { PUTBACK(tok, lex); - ASSERT_BUG(lex.getPosition(), path.nodes().size() > 0, "`use` with no path"); + ASSERT_BUG(lex.point_span(), path.nodes().size() > 0, "`use` with no path"); name = path.nodes().back().name(); } @@ -1490,7 +1490,7 @@ bool Parse_MacroInvocation_Opt(TokenStream& lex, AST::MacroInvocation& out_inv) Parse_Use(lex, [&](AST::UseStmt p, std::string s) { DEBUG(mod_path << " - use " << p << " as '" << s << "'"); if( !item_data.is_None() ) - TODO(lex.getPosition(), "Encode multi-item use statements as a single Item"); + TODO(lex.point_span(), "Encode multi-item use statements as a single Item"); item_data = ::AST::Item(mv$(p)); item_name = mv$(s); }); @@ -1735,7 +1735,7 @@ bool Parse_MacroInvocation_Opt(TokenStream& lex, AST::MacroInvocation& out_inv) bool sub_file_controls_dir = true; if( mod_fileinfo.path == "-" ) { if( path_attr.size() ) { - ERROR(lex.getPosition(), E0000, "Cannot load module from file when reading stdin"); + ERROR(lex.point_span(), E0000, "Cannot load module from file when reading stdin"); } sub_path = "-"; } @@ -1779,11 +1779,11 @@ bool Parse_MacroInvocation_Opt(TokenStream& lex, AST::MacroInvocation& out_inv) break; case TOK_SEMICOLON: if( sub_path == "-" ) { - ERROR(lex.getPosition(), E0000, "Cannot load module from file when reading stdin"); + ERROR(lex.point_span(), E0000, "Cannot load module from file when reading stdin"); } else if( path_attr.size() == 0 && ! mod_fileinfo.controls_dir ) { - ERROR(lex.getPosition(), E0000, "Can't load from files outside of mod.rs or crate root"); + ERROR(lex.point_span(), E0000, "Can't load from files outside of mod.rs or crate root"); } else if( !H::check_item_cfg(meta_items) ) { // Ignore - emit Item::None @@ -1801,7 +1801,7 @@ bool Parse_MacroInvocation_Opt(TokenStream& lex, AST::MacroInvocation& out_inv) if( ifs_dir.is_open() && ifs_file.is_open() ) { // Collision - ERROR(lex.getPosition(), E0000, "Both modname.rs and modname/mod.rs exist"); + ERROR(lex.point_span(), E0000, "Both modname.rs and modname/mod.rs exist"); } else if( ifs_dir.is_open() ) { @@ -1815,7 +1815,7 @@ bool Parse_MacroInvocation_Opt(TokenStream& lex, AST::MacroInvocation& out_inv) else { // Can't find file - ERROR(lex.getPosition(), E0000, "Can't find file for '" << name << "' in '" << mod_fileinfo.path << "'"); + ERROR(lex.point_span(), E0000, "Can't find file for '" << name << "' in '" << mod_fileinfo.path << "'"); } DEBUG("- path = " << submod.m_file_info.path); Lexer sub_lex(submod.m_file_info.path); diff --git a/src/parse/tokenstream.cpp b/src/parse/tokenstream.cpp index 8cb9a910..2975a523 100644 --- a/src/parse/tokenstream.cpp +++ b/src/parse/tokenstream.cpp @@ -116,11 +116,15 @@ ProtoSpan TokenStream::start_span() const Span TokenStream::end_span(ProtoSpan ps) const { auto p = this->getPosition(); - return Span( - ps.filename, - ps.start_line, ps.start_ofs, - p.line, p.ofs - ); + auto rv = Span( ps.filename, ps.start_line, ps.start_ofs, p.line, p.ofs ); + rv.outer_span = this->outerSpan(); + return rv; +} +Span TokenStream::point_span() const +{ + Span rv = this->getPosition(); + rv.outer_span = this->outerSpan(); + return rv; } Ident TokenStream::get_ident(Token tok) const { diff --git a/src/parse/tokenstream.hpp b/src/parse/tokenstream.hpp index 85fc62e2..5f2e0733 100644 --- a/src/parse/tokenstream.hpp +++ b/src/parse/tokenstream.hpp @@ -60,17 +60,19 @@ public: void putback(Token tok); eTokenType lookahead(unsigned int count); - virtual Position getPosition() const = 0; Ident::Hygiene getHygiene() const; ParseState& parse_state() { return m_parse_state; } ProtoSpan start_span() const; Span end_span(ProtoSpan ps) const; + Span point_span() const; Ident get_ident(Token tok) const; protected: + virtual Position getPosition() const = 0; + virtual ::std::shared_ptr<Span> outerSpan() const { return ::std::shared_ptr<Span>(0); } virtual Token realGetToken() = 0; virtual Ident::Hygiene realGetHygiene() const = 0; private: diff --git a/src/parse/types.cpp b/src/parse/types.cpp index 905bd935..ff993693 100644 --- a/src/parse/types.cpp +++ b/src/parse/types.cpp @@ -218,7 +218,7 @@ TypeRef Parse_Type_Fn(TokenStream& lex, ::std::vector<::std::string> hrls) if( GET_TOK(tok, lex) == TOK_STRING ) { abi = tok.str(); if( abi == "" ) - ERROR(lex.getPosition(), E0000, "Empty ABI"); + ERROR(lex.point_span(), E0000, "Empty ABI"); GET_TOK(tok, lex); } else { diff --git a/src/resolve/absolute.cpp b/src/resolve/absolute.cpp index 26a45a1e..1e197769 100644 --- a/src/resolve/absolute.cpp +++ b/src/resolve/absolute.cpp @@ -1601,7 +1601,7 @@ void Resolve_Absolute_ExprNode(Context& context, ::AST::ExprNode& node) Resolve_Absolute_Pattern(this->context, true, node.m_pattern); break; case ::AST::ExprNode_Loop::FOR: - BUG(node.get_pos(), "`for` should be desugared"); + BUG(node.span(), "`for` should be desugared"); } node.m_code->visit( *this ); this->context.pop_block(); @@ -1629,22 +1629,22 @@ void Resolve_Absolute_ExprNode(Context& context, ::AST::ExprNode& node) } void visit(AST::ExprNode_StructLiteral& node) override { DEBUG("ExprNode_StructLiteral"); - Resolve_Absolute_Path(this->context, Span(node.get_pos()), Context::LookupMode::Type, node.m_path); + Resolve_Absolute_Path(this->context, node.span(), Context::LookupMode::Type, node.m_path); AST::NodeVisitorDef::visit(node); } void visit(AST::ExprNode_CallPath& node) override { DEBUG("ExprNode_CallPath"); - Resolve_Absolute_Path(this->context, Span(node.get_pos()), Context::LookupMode::Variable, node.m_path); + Resolve_Absolute_Path(this->context, node.span(), Context::LookupMode::Variable, node.m_path); AST::NodeVisitorDef::visit(node); } void visit(AST::ExprNode_CallMethod& node) override { DEBUG("ExprNode_CallMethod"); - Resolve_Absolute_PathParams(this->context, Span(node.get_pos()), node.m_method.args()); + Resolve_Absolute_PathParams(this->context, node.span(), node.m_method.args()); AST::NodeVisitorDef::visit(node); } void visit(AST::ExprNode_NamedValue& node) override { - DEBUG("(" << node.get_pos() << ") ExprNode_NamedValue - " << node.m_path); - Resolve_Absolute_Path(this->context, Span(node.get_pos()), Context::LookupMode::Variable, node.m_path); + DEBUG("(" << node.span() << ") ExprNode_NamedValue - " << node.m_path); + Resolve_Absolute_Path(this->context, node.span(), Context::LookupMode::Variable, node.m_path); } void visit(AST::ExprNode_Cast& node) override { DEBUG("ExprNode_Cast"); diff --git a/src/span.cpp b/src/span.cpp index 29d7201b..8d176671 100644 --- a/src/span.cpp +++ b/src/span.cpp @@ -12,6 +12,7 @@ #include <common.hpp> Span::Span(const Span& x): + outer_span(x.outer_span), filename(x.filename), start_line(x.start_line), start_ofs(x.start_ofs), @@ -20,6 +21,7 @@ Span::Span(const Span& x): { } Span::Span(const Position& pos): + outer_span(), filename(pos.filename), start_line(pos.line), start_ofs(pos.ofs), @@ -28,6 +30,7 @@ Span::Span(const Position& pos): { } Span::Span(): + outer_span(), filename("")/*, start_line(0), start_ofs(0), end_line(0), end_ofs(0) // */ diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp index 17bb5ab0..f5c1a61d 100644 --- a/src/trans/codegen_c.cpp +++ b/src/trans/codegen_c.cpp @@ -14,6 +14,7 @@ #include <mir/mir.hpp> #include <hir_typeck/static.hpp> #include <mir/helpers.hpp> +#include "codegen_c.hpp" namespace { @@ -31,6 +32,11 @@ namespace { Msvc, // Use MSVC extensions }; + enum class Compiler { + Gcc, + Msvc + }; + static Span sp; const ::HIR::Crate& m_crate; @@ -42,6 +48,8 @@ namespace { ::std::ofstream m_of; const ::MIR::TypeResolve* m_mir_res; + Compiler m_compiler = Compiler::Gcc; + ::std::map<::HIR::GenericPath, ::std::vector<unsigned>> m_enum_repr_cache; ::std::vector< ::std::pair< ::HIR::GenericPath, const ::HIR::Struct*> > m_box_glue_todo; @@ -60,10 +68,14 @@ namespace { << "#include <stddef.h>\n" << "#include <stdint.h>\n" << "#include <stdbool.h>\n" - << "#include <stdatomic.h>\n" // atomic_* << "#include <stdlib.h>\n" // abort << "#include <string.h>\n" // mem* << "#include <math.h>\n" // round, ... + ; + m_of + << "#include <stdatomic.h>\n" // atomic_* + ; + m_of << "typedef uint32_t CHAR;\n" << "typedef struct { } tUNIT;\n" << "typedef struct { } tBANG;\n" @@ -87,13 +99,21 @@ namespace { << "static inline TRAITOBJ_PTR make_traitobjptr(void* ptr, void* vt) { TRAITOBJ_PTR rv = { ptr, vt }; return rv; }\n" << "\n" << "static inline size_t max(size_t a, size_t b) { return a < b ? b : a; }\n" + << "static inline void noop_drop(void *p) {}\n" + << "\n" + ; + // 64-bit bit ops + m_of << "static inline uint64_t __builtin_clz64(uint64_t v) {\n" << "\treturn (v >> 32 != 0 ? __builtin_clz(v>>32) : 32 + __builtin_clz(v));\n" << "}\n" << "static inline uint64_t __builtin_ctz64(uint64_t v) {\n" << "\treturn ((v&0xFFFFFFFF) == 0 ? __builtin_ctz(v>>32) + 32 : __builtin_ctz(v));\n" << "}\n" - << "static inline uint128_t __builtin_bswap128(uint128_t v) {\n" + ; + // u128/i128 ops + m_of + << "static inline unsigned __int128 __builtin_bswap128(unsigned __int128 v) {\n" << "\tuint64_t lo = __builtin_bswap64((uint64_t)v);\n" << "\tuint64_t hi = __builtin_bswap64((uint64_t)(v>>64));\n" << "\treturn ((uint128_t)lo << 64) | (uint128_t)hi;\n" @@ -105,8 +125,6 @@ namespace { << "\treturn ((v&0xFFFFFFFFFFFFFFFF) == 0 ? __builtin_ctz64(v>>64) + 64 : __builtin_ctz64(v));\n" << "}\n" << "\n" - << "static inline void noop_drop(void *p) {}\n" - << "\n" ; } @@ -141,59 +159,113 @@ namespace { // Execute $CC with the required libraries ::std::vector<::std::string> tmp; + auto cache_str = [&](::std::string s){ tmp.push_back(::std::move(s)); return tmp.back().c_str(); }; ::std::vector<const char*> args; - args.push_back( getenv("CC") ? getenv("CC") : "gcc" ); - args.push_back("-ffunction-sections"); - args.push_back("-pthread"); - switch(opt.opt_level) - { - case 0: break; - case 1: - args.push_back("-O1"); - break; - case 2: - args.push_back("-O2"); - break; - } - if( opt.emit_debug_info ) - { - args.push_back("-g"); - } - args.push_back("-o"); - args.push_back(m_outfile_path.c_str()); - args.push_back(m_outfile_path_c.c_str()); - if( is_executable ) + switch( m_compiler ) { - for( const auto& crate : m_crate.m_ext_crates ) + case Compiler::Gcc: + args.push_back( getenv("CC") ? getenv("CC") : "gcc" ); + args.push_back("-ffunction-sections"); + args.push_back("-pthread"); + switch(opt.opt_level) { - tmp.push_back(crate.second.m_filename + ".o"); - args.push_back(tmp.back().c_str()); + case 0: break; + case 1: + args.push_back("-O1"); + break; + case 2: + args.push_back("-O2"); + break; } - for(const auto& path : opt.library_search_dirs ) + if( opt.emit_debug_info ) { - args.push_back("-L"); args.push_back(path.c_str()); + args.push_back("-g"); } - for(const auto& lib : m_crate.m_ext_libs) { - ASSERT_BUG(Span(), lib.name != "", ""); - args.push_back("-l"); args.push_back(lib.name.c_str()); - } - for( const auto& crate : m_crate.m_ext_crates ) + args.push_back("-o"); + args.push_back(m_outfile_path.c_str()); + args.push_back(m_outfile_path_c.c_str()); + if( is_executable ) { - for(const auto& lib : crate.second.m_data->m_ext_libs) { - ASSERT_BUG(Span(), lib.name != "", "Empty lib from " << crate.first); + for( const auto& crate : m_crate.m_ext_crates ) + { + args.push_back(cache_str( crate.second.m_filename + ".o" )); + } + for(const auto& path : opt.library_search_dirs ) + { + args.push_back("-L"); args.push_back(path.c_str()); + } + for(const auto& lib : m_crate.m_ext_libs) { + ASSERT_BUG(Span(), lib.name != "", ""); args.push_back("-l"); args.push_back(lib.name.c_str()); } + for( const auto& crate : m_crate.m_ext_crates ) + { + for(const auto& lib : crate.second.m_data->m_ext_libs) { + ASSERT_BUG(Span(), lib.name != "", "Empty lib from " << crate.first); + args.push_back("-l"); args.push_back(lib.name.c_str()); + } + } + for(const auto& path : opt.libraries ) + { + args.push_back("-l"); args.push_back(path.c_str()); + } + args.push_back("-z"); args.push_back("muldefs"); + args.push_back("-Wl,--gc-sections"); } - for(const auto& path : opt.libraries ) + else { - args.push_back("-l"); args.push_back(path.c_str()); + args.push_back("-c"); } - args.push_back("-z"); args.push_back("muldefs"); - args.push_back("-Wl,--gc-sections"); - } - else - { - args.push_back("-c"); + break; + case Compiler::Msvc: + args.push_back( "cl.exe" ); + args.push_back(m_outfile_path_c.c_str()); + switch(opt.opt_level) + { + case 0: break; + case 1: + args.push_back("/O1"); + break; + case 2: + args.push_back("/O2"); + break; + } + if(is_executable) + { + for( const auto& crate : m_crate.m_ext_crates ) + { + args.push_back(cache_str( crate.second.m_filename + ".o" )); + } + // Command-line specified linker search directories + for(const auto& path : opt.library_search_dirs ) + { + args.push_back("/link"); + args.push_back("/LIBPATH"); + args.push_back(path.c_str()); + } + // Crate-specified libraries + for(const auto& lib : m_crate.m_ext_libs) { + ASSERT_BUG(Span(), lib.name != "", ""); + args.push_back(lib.name.c_str()); + } + for( const auto& crate : m_crate.m_ext_crates ) + { + for(const auto& lib : crate.second.m_data->m_ext_libs) { + ASSERT_BUG(Span(), lib.name != "", "Empty lib from " << crate.first); + args.push_back(lib.name.c_str()); + } + } + for(const auto& path : opt.libraries ) + { + args.push_back(path.c_str()); + } + } + else + { + args.push_back("/c"); + args.push_back(cache_str( FMT("/Fo" + m_outfile_path) )); + } + break; } ::std::stringstream cmd_ss; @@ -226,13 +298,13 @@ namespace { ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), struct_ty_ptr, args, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; - m_of << "void " << Trans_Mangle(drop_glue_path) << "(struct s_" << Trans_Mangle(p) << "* rv) {\n"; + m_of << "static void " << Trans_Mangle(drop_glue_path) << "(struct s_" << Trans_Mangle(p) << "* rv) {\n"; // Obtain inner pointer // TODO: This is very specific to the structure of the official liballoc's Box. m_of << "\t"; emit_ctype(args[0].second, FMT_CB(ss, ss << "arg0"; )); m_of << " = rv->_0._0._0;\n"; // Call destructor of inner data - emit_destructor_call( ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({0})) }), *ity, true); + emit_destructor_call( ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Argument({0})) }), *ity, true, 1); // Emit a call to box_free for the type m_of << "\t" << Trans_Mangle(box_free) << "(arg0);\n"; @@ -328,12 +400,12 @@ namespace { auto ty_ptr = ::HIR::TypeRef::new_pointer(::HIR::BorrowType::Owned, ty.clone()); ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), ty_ptr, args, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; - m_of << "void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(ty); m_of << "* rv) {"; + m_of << "static void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(ty); m_of << "* rv) {"; auto self = ::MIR::LValue::make_Deref({ box$(::MIR::LValue::make_Return({})) }); auto fld_lv = ::MIR::LValue::make_Field({ box$(self), 0 }); for(const auto& ity : te) { - emit_destructor_call(fld_lv, ity, /*unsized_valid=*/false); + emit_destructor_call(fld_lv, ity, /*unsized_valid=*/false, 1); fld_lv.as_Field().field_index ++; } m_of << "}\n"; @@ -440,13 +512,13 @@ namespace { else if( m_resolve.is_type_owned_box(struct_ty) ) { m_box_glue_todo.push_back( ::std::make_pair( mv$(struct_ty.m_data.as_Path().path.m_data.as_Generic()), &item ) ); - m_of << "void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ");\n"; + m_of << "static void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ");\n"; return ; } ::MIR::TypeResolve mir_res { sp, m_resolve, FMT_CB(ss, ss << drop_glue_path;), struct_ty_ptr, args, *(::MIR::Function*)nullptr }; m_mir_res = &mir_res; - m_of << "void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ") {\n"; + m_of << "static void " << Trans_Mangle(drop_glue_path) << "("; emit_ctype(struct_ty_ptr, FMT_CB(ss, ss << "rv";)); m_of << ") {\n"; // If this type has an impl of Drop, call that impl if( item.m_markings.has_drop_impl ) { @@ -464,7 +536,7 @@ namespace { const auto& fld = e[i]; fld_lv.as_Field().field_index = i; - emit_destructor_call(fld_lv, monomorph(fld.ent), true); + emit_destructor_call(fld_lv, monomorph(fld.ent), true, 1); } ), (Named, @@ -473,7 +545,7 @@ namespace { const auto& fld = e[i].second; fld_lv.as_Field().field_index = i; - emit_destructor_call(fld_lv, monomorph(fld.ent), true); + emit_destructor_call(fld_lv, monomorph(fld.ent), true, 1); } ) ) @@ -518,7 +590,7 @@ namespace { m_of << "tUNIT " << Trans_Mangle(drop_impl_path) << "(union u_" << Trans_Mangle(p) << "*rv);\n"; } - m_of << "void " << Trans_Mangle(drop_glue_path) << "(union u_" << Trans_Mangle(p) << "* rv) {\n"; + m_of << "static void " << Trans_Mangle(drop_glue_path) << "(union u_" << Trans_Mangle(p) << "* rv) {\n"; if( item.m_markings.has_drop_impl ) { m_of << "\t" << Trans_Mangle(drop_impl_path) << "(rv);\n"; @@ -601,6 +673,7 @@ namespace { } } + m_of << "// enum " << p << "\n"; if( nonzero_path.size() > 0 ) { MIR_ASSERT(*m_mir_res, nonzero_path[0] == 0, ""); @@ -692,7 +765,7 @@ namespace { m_of << "tUNIT " << Trans_Mangle(drop_impl_path) << "(struct e_" << Trans_Mangle(p) << "*rv);\n"; } - m_of << "void " << Trans_Mangle(drop_glue_path) << "(struct e_" << Trans_Mangle(p) << "* rv) {\n"; + m_of << "static void " << Trans_Mangle(drop_glue_path) << "(struct e_" << Trans_Mangle(p) << "* rv) {\n"; // If this type has an impl of Drop, call that impl if( item.m_markings.has_drop_impl ) @@ -708,7 +781,7 @@ namespace { m_of << "\tif( ! (*rv)"; emit_nonzero_path(nonzero_path); m_of << " ) {\n"; for(const auto& fld : item.m_variants[1].second.as_Tuple()) { - emit_destructor_call(fld_lv, monomorph(fld.ent), false); + emit_destructor_call(fld_lv, monomorph(fld.ent), false, 2); fld_lv.as_Field().field_index ++; } m_of << "\t}\n"; @@ -739,7 +812,7 @@ namespace { fld_lv.as_Field().field_index = i; const auto& fld = e[i]; - emit_destructor_call(fld_lv, monomorph(fld.ent), false); + emit_destructor_call(fld_lv, monomorph(fld.ent), false, 2); } m_of << "\tbreak;\n"; ), @@ -749,7 +822,7 @@ namespace { { fld_lv.as_Field().field_index = i; const auto& fld = e[i]; - emit_destructor_call(fld_lv, monomorph(fld.second.ent), false); + emit_destructor_call(fld_lv, monomorph(fld.second.ent), false, 2); } m_of << "\tbreak;\n"; ) @@ -901,6 +974,18 @@ namespace { m_mir_res = nullptr; } + void emit_float(double v) { + if( ::std::isnan(v) ) { + m_of << "NAN"; + } + else if( ::std::isinf(v) ) { + m_of << (v < 0 ? "-" : "") << "INFINITY"; + } + else { + m_of.precision(::std::numeric_limits<double>::max_digits10 + 1); + m_of << ::std::scientific << v; + } + } void emit_literal(const ::HIR::TypeRef& ty, const ::HIR::Literal& lit, const Trans_Params& params) { TRACE_FUNCTION_F("ty=" << ty << ", lit=" << lit); ::HIR::TypeRef tmp; @@ -1072,15 +1157,7 @@ namespace { } ), (Float, - if( ::std::isnan(e) ) { - m_of << "NAN"; - } - else if( ::std::isinf(e) ) { - m_of << "INFINITY"; - } - else { - m_of << e; - } + this->emit_float(e); ), (BorrowOf, TU_MATCHA( (e.m_data), (pe), @@ -1306,25 +1383,32 @@ namespace { m_of << "{\n"; // Variables m_of << "\t"; emit_ctype(ret_type, FMT_CB(ss, ss << "rv";)); m_of << ";\n"; - for(unsigned int i = 0; i < code->named_variables.size(); i ++) { - DEBUG("var" << i << " : " << code->named_variables[i]); - m_of << "\t"; emit_ctype(code->named_variables[i], FMT_CB(ss, ss << "var" << i;)); m_of << ";"; - m_of << "\t// " << code->named_variables[i]; - m_of << "\n"; - } - for(unsigned int i = 0; i < code->temporaries.size(); i ++) { - DEBUG("tmp" << i << " : " << code->temporaries[i]); - m_of << "\t"; emit_ctype(code->temporaries[i], FMT_CB(ss, ss << " tmp" << i;)); m_of << ";"; - m_of << "\t// " << code->temporaries[i]; + for(unsigned int i = 0; i < code->locals.size(); i ++) { + DEBUG("var" << i << " : " << code->locals[i]); + m_of << "\t"; emit_ctype(code->locals[i], FMT_CB(ss, ss << "var" << i;)); m_of << ";"; + m_of << "\t// " << code->locals[i]; m_of << "\n"; } for(unsigned int i = 0; i < code->drop_flags.size(); i ++) { m_of << "\tbool df" << i << " = " << code->drop_flags[i] << ";\n"; } + + if( false ) + { + m_of << "#if 0\n"; + auto nodes = MIR_To_Structured(*code); + for(const auto& node : nodes) + { + emit_fcn_node(mir_res, node, 1); + } + m_of << "#endif\n"; + } + for(unsigned int i = 0; i < code->blocks.size(); i ++) { TRACE_FUNCTION_F(p << " bb" << i); + // HACK: Ignore any blocks that only contain `diverge;` if( code->blocks[i].statements.size() == 0 && code->blocks[i].terminator.is_Diverge() ) { DEBUG("- Diverge only, omitting"); m_of << "bb" << i << ": _Unwind_Resume(); // Diverge\n"; @@ -1336,454 +1420,7 @@ namespace { for(const auto& stmt : code->blocks[i].statements) { mir_res.set_cur_stmt(i, (&stmt - &code->blocks[i].statements.front())); - switch( stmt.tag() ) - { - case ::MIR::Statement::TAGDEAD: throw ""; - case ::MIR::Statement::TAG_ScopeEnd: - m_of << "// " << stmt << "\n"; - break; - case ::MIR::Statement::TAG_SetDropFlag: { - const auto& e = stmt.as_SetDropFlag(); - m_of << "\tdf" << e.idx << " = "; - if( e.other == ~0u ) - m_of << e.new_val; - else - m_of << (e.new_val ? "!" : "") << "df" << e.other; - m_of << ";\n"; - break; } - case ::MIR::Statement::TAG_Drop: { - const auto& e = stmt.as_Drop(); - ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, e.slot); - - if( e.flag_idx != ~0u ) - m_of << "\tif( df" << e.flag_idx << " ) {\n"; - - switch( e.kind ) - { - case ::MIR::eDropKind::SHALLOW: - // Shallow drops are only valid on owned_box - if( const auto* ity = m_resolve.is_type_owned_box(ty) ) - { - // Emit a call to box_free for the type - ::HIR::GenericPath box_free { m_crate.get_lang_item_path(sp, "box_free"), { ity->clone() } }; - // TODO: This is specific to the official liballoc's owned_box - m_of << "\t" << Trans_Mangle(box_free) << "("; emit_lvalue(e.slot); m_of << "._0._0._0);\n"; - } - else - { - MIR_BUG(mir_res, "Shallow drop on non-Box - " << ty); - } - break; - case ::MIR::eDropKind::DEEP: - emit_destructor_call(e.slot, ty, false); - break; - } - if( e.flag_idx != ~0u ) - m_of << "\t}\n"; - break; } - case ::MIR::Statement::TAG_Asm: { - const auto& e = stmt.as_Asm(); - - struct H { - static bool has_flag(const ::std::vector<::std::string>& flags, const char* des) { - return ::std::find_if(flags.begin(), flags.end(), [des](const auto&x){return x==des;}) != flags.end(); - } - static const char* convert_reg(const char* r) { - if( ::std::strcmp(r, "{eax}") == 0 || ::std::strcmp(r, "{rax}") == 0 ) { - return "a"; - } - else { - return r; - } - } - }; - bool is_volatile = H::has_flag(e.flags, "volatile"); - bool is_intel = H::has_flag(e.flags, "intel"); - - m_of << "\t__asm__ "; - if(is_volatile) m_of << "__volatile__"; - // TODO: Convert format string? - // TODO: Use a C-specific escaper here. - m_of << "(\"" << (is_intel ? ".syntax intel; " : "") << FmtEscaped(e.tpl) << (is_intel ? ".syntax att; " : "") << "\""; - m_of << ": "; - for(unsigned int i = 0; i < e.outputs.size(); i ++ ) - { - const auto& v = e.outputs[i]; - if( i != 0 ) m_of << ", "; - m_of << "\""; - switch(v.first[0]) - { - case '=': m_of << "="; break; - case '+': m_of << "+"; break; - default: MIR_TODO(mir_res, "Handle asm! output leader '" << v.first[0] << "'"); - } - m_of << H::convert_reg(v.first.c_str()+1); - m_of << "\"("; emit_lvalue(v.second); m_of << ")"; - } - m_of << ": "; - for(unsigned int i = 0; i < e.inputs.size(); i ++ ) - { - const auto& v = e.inputs[i]; - if( i != 0 ) m_of << ", "; - m_of << "\"" << v.first << "\"("; emit_lvalue(v.second); m_of << ")"; - } - m_of << ": "; - for(unsigned int i = 0; i < e.clobbers.size(); i ++ ) - { - if( i != 0 ) m_of << ", "; - m_of << "\"" << e.clobbers[i] << "\""; - } - m_of << ");\n"; - break; } - case ::MIR::Statement::TAG_Assign: { - const auto& e = stmt.as_Assign(); - DEBUG("- " << e.dst << " = " << e.src); - m_of << "\t"; - TU_MATCHA( (e.src), (ve), - (Use, - ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, ve); - if( ty == ::HIR::TypeRef::new_diverge() ) { - m_of << "abort()"; - break; - } - emit_lvalue(e.dst); - m_of << " = "; - emit_lvalue(ve); - ), - (Constant, - emit_lvalue(e.dst); - m_of << " = "; - emit_constant(ve, &e.dst); - ), - (SizedArray, - if( ve.count == 0 ) { - } - else if( ve.count == 1 ) { - emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_param(ve.val); - } - else if( ve.count == 2 ) { - emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_param(ve.val); m_of << ";\n\t"; - emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_param(ve.val); - } - else if( ve.count == 3 ) { - emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_param(ve.val); m_of << ";\n\t"; - emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_param(ve.val); m_of << ";\n\t"; - emit_lvalue(e.dst); m_of << ".DATA[2] = "; emit_param(ve.val); - } - else { - m_of << "for(unsigned int i = 0; i < " << ve.count << "; i ++)\n"; - m_of << "\t\t"; emit_lvalue(e.dst); m_of << ".DATA[i] = "; emit_param(ve.val); - } - ), - (Borrow, - ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); - bool special = false; - // If the inner value has type [T] or str, create DST based on inner pointer and existing metadata - TU_IFLET(::MIR::LValue, ve.val, Deref, le, - if( metadata_type(ty) != MetadataType::None ) { - emit_lvalue(e.dst); - m_of << " = "; - emit_lvalue(*le.val); - 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, - 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; - - // Construct the new DST - emit_lvalue(e.dst); m_of << ".META = "; emit_lvalue(base_ptr); m_of << ".META;\n\t"; - emit_lvalue(e.dst); m_of << ".PTR = &"; emit_lvalue(ve.val); - special = true; - } - ) - if( !special ) - { - emit_lvalue(e.dst); - m_of << " = "; - m_of << "& "; emit_lvalue(ve.val); - } - ), - (Cast, - if( m_resolve.is_type_phantom_data(ve.type) ) { - m_of << "/* PhandomData cast */\n"; - continue ; - } - - emit_lvalue(e.dst); - m_of << " = "; - m_of << "("; emit_ctype(ve.type); m_of << ")"; - // TODO: If the source is an unsized borrow, then extract the pointer - bool special = false; - ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); - // If the destination is a thin pointer - if( ve.type.m_data.is_Pointer() && !is_dst( *ve.type.m_data.as_Pointer().inner ) ) - { - // NOTE: Checks the result of the deref - if( (ty.m_data.is_Borrow() && is_dst(*ty.m_data.as_Borrow().inner)) - || (ty.m_data.is_Pointer() && is_dst(*ty.m_data.as_Pointer().inner)) - ) - { - emit_lvalue(ve.val); - m_of << ".PTR"; - special = true; - } - } - if( ve.type.m_data.is_Primitive() && ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum() ) - { - emit_lvalue(ve.val); - m_of << ".TAG"; - special = true; - } - if( !special ) - { - emit_lvalue(ve.val); - } - ), - (BinOp, - emit_lvalue(e.dst); - m_of << " = "; - ::HIR::TypeRef tmp; - const auto& ty = ve.val_l.is_LValue() ? mir_res.get_lvalue_type(tmp, ve.val_l.as_LValue()) : tmp = mir_res.get_const_type(ve.val_l.as_Constant()); - if( ty.m_data.is_Borrow() ) { - m_of << "(slice_cmp("; emit_param(ve.val_l); m_of << ", "; emit_param(ve.val_r); m_of << ")"; - switch(ve.op) - { - case ::MIR::eBinOp::EQ: m_of << " == 0"; break; - case ::MIR::eBinOp::NE: m_of << " != 0"; break; - case ::MIR::eBinOp::GT: m_of << " > 0"; break; - case ::MIR::eBinOp::GE: m_of << " >= 0"; break; - case ::MIR::eBinOp::LT: m_of << " < 0"; break; - case ::MIR::eBinOp::LE: m_of << " <= 0"; break; - default: - MIR_BUG(mir_res, "Unknown comparison of a &-ptr - " << e.src << " with " << ty); - } - m_of << ")"; - break; - } - else if( const auto* te = ty.m_data.opt_Pointer() ) { - if( metadata_type(*te->inner) != MetadataType::None ) - { - switch(ve.op) - { - case ::MIR::eBinOp::EQ: - emit_param(ve.val_l); m_of << ".PTR == "; emit_param(ve.val_r); m_of << ".PTR && "; - emit_param(ve.val_l); m_of << ".META == "; emit_param(ve.val_r); m_of << ".META"; - break; - case ::MIR::eBinOp::NE: - emit_param(ve.val_l); m_of << ".PTR != "; emit_param(ve.val_r); m_of << ".PTR || "; - emit_param(ve.val_l); m_of << ".META != "; emit_param(ve.val_r); m_of << ".META"; - break; - default: - MIR_BUG(mir_res, "Unknown comparison of a *-ptr - " << e.src << " with " << ty); - } - } - else - { - emit_param(ve.val_l); - switch(ve.op) - { - case ::MIR::eBinOp::EQ: m_of << " == "; break; - case ::MIR::eBinOp::NE: m_of << " != "; break; - case ::MIR::eBinOp::GT: m_of << " > " ; break; - case ::MIR::eBinOp::GE: m_of << " >= "; break; - case ::MIR::eBinOp::LT: m_of << " < " ; break; - case ::MIR::eBinOp::LE: m_of << " <= "; break; - default: - MIR_BUG(mir_res, "Unknown comparison of a *-ptr - " << e.src << " with " << ty); - } - emit_param(ve.val_r); - } - break; - } - else if( ve.op == ::MIR::eBinOp::MOD && (ty == ::HIR::CoreType::F32 || ty == ::HIR::CoreType::F64) ) { - if( ty == ::HIR::CoreType::F32 ) - m_of << "remainderf"; - else - m_of << "remainder"; - m_of << "("; emit_param(ve.val_l); m_of << ", "; emit_param(ve.val_r); m_of << ")"; - break; - } - else { - } - - emit_param(ve.val_l); - switch(ve.op) - { - case ::MIR::eBinOp::ADD: m_of << " + "; break; - case ::MIR::eBinOp::SUB: m_of << " - "; break; - case ::MIR::eBinOp::MUL: m_of << " * "; break; - case ::MIR::eBinOp::DIV: m_of << " / "; break; - case ::MIR::eBinOp::MOD: m_of << " % "; break; - - case ::MIR::eBinOp::BIT_OR: m_of << " | "; break; - case ::MIR::eBinOp::BIT_AND: m_of << " & "; break; - case ::MIR::eBinOp::BIT_XOR: m_of << " ^ "; break; - case ::MIR::eBinOp::BIT_SHR: m_of << " >> "; break; - case ::MIR::eBinOp::BIT_SHL: m_of << " << "; break; - case ::MIR::eBinOp::EQ: m_of << " == "; break; - case ::MIR::eBinOp::NE: m_of << " != "; break; - case ::MIR::eBinOp::GT: m_of << " > " ; break; - case ::MIR::eBinOp::GE: m_of << " >= "; break; - case ::MIR::eBinOp::LT: m_of << " < " ; break; - case ::MIR::eBinOp::LE: m_of << " <= "; break; - - case ::MIR::eBinOp::ADD_OV: - case ::MIR::eBinOp::SUB_OV: - case ::MIR::eBinOp::MUL_OV: - case ::MIR::eBinOp::DIV_OV: - MIR_TODO(mir_res, "Overflow"); - break; - } - emit_param(ve.val_r); - ), - (UniOp, - ::HIR::TypeRef tmp; - emit_lvalue(e.dst); - m_of << " = "; - switch(ve.op) - { - case ::MIR::eUniOp::NEG: m_of << "-"; break; - case ::MIR::eUniOp::INV: - if( mir_res.get_lvalue_type(tmp, e.dst) == ::HIR::CoreType::Bool ) - m_of << "!"; - else - m_of << "~"; - break; - } - emit_lvalue(ve.val); - ), - (DstMeta, - emit_lvalue(e.dst); - m_of << " = "; - emit_lvalue(ve.val); - m_of << ".META"; - ), - (DstPtr, - emit_lvalue(e.dst); - m_of << " = "; - emit_lvalue(ve.val); - m_of << ".PTR"; - ), - (MakeDst, - emit_lvalue(e.dst); - m_of << ".PTR = "; - emit_param(ve.ptr_val); - m_of << ";\n\t"; - emit_lvalue(e.dst); - m_of << ".META = "; - emit_param(ve.meta_val); - ), - (Tuple, - for(unsigned int j = 0; j < ve.vals.size(); j ++) { - if( j != 0 ) m_of << ";\n\t"; - emit_lvalue(e.dst); - m_of << "._" << j << " = "; - emit_param(ve.vals[j]); - } - ), - (Array, - for(unsigned int j = 0; j < ve.vals.size(); j ++) { - if( j != 0 ) m_of << ";\n\t"; - emit_lvalue(e.dst); m_of << ".DATA[" << j << "] = "; - emit_param(ve.vals[j]); - } - ), - (Variant, - const auto& tyi = m_crate.get_typeitem_by_path(sp, ve.path.m_path); - if( tyi.is_Union() ) - { - emit_lvalue(e.dst); - m_of << ".var_" << ve.index << " = "; emit_param(ve.val); - } - else if( const auto* enm_p = tyi.opt_Enum() ) - { - MIR_TODO(mir_res, "Construct enum with RValue::Variant"); - if( enm_p->is_value() ) - { - emit_lvalue(e.dst); m_of << ".TAG = " << enm_p->get_value(ve.index) << ""; - } - else - { - emit_lvalue(e.dst); m_of << ".TAG = " << ve.index << ";\n\t"; - emit_lvalue(e.dst); m_of << ".DATA"; - m_of << ".var_" << ve.index << " = "; emit_param(ve.val); - } - } - else - { - BUG(mir_res.sp, "Unexpected type in Variant"); - } - ), - (Struct, - if(ve.variant_idx != ~0u) - { - ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); - const auto* enm_p = ty.m_data.as_Path().binding.as_Enum(); - - auto it = m_enum_repr_cache.find(ty.m_data.as_Path().path.m_data.as_Generic()); - if( it != m_enum_repr_cache.end() ) - { - if( ve.variant_idx == 0 ) { - // TODO: Use nonzero_path - m_of << "memset(&"; emit_lvalue(e.dst); m_of << ", 0, sizeof("; emit_ctype(ty); m_of << "))"; - } - else if( ve.variant_idx == 1 ) { - emit_lvalue(e.dst); - m_of << "._0 = "; - emit_param(ve.vals[0]); - } - else { - } - break; - } - else if( enm_p->is_value() ) - { - emit_lvalue(e.dst); - m_of << ".TAG = " << enm_p->get_value(ve.variant_idx); - assert(ve.vals.size() == 0); - } - else - { - emit_lvalue(e.dst); - m_of << ".TAG = " << ve.variant_idx; - } - if(ve.vals.size() > 0) - m_of << ";\n\t"; - } - - for(unsigned int j = 0; j < ve.vals.size(); j ++) - { - // HACK: Don't emit assignment of PhantomData - ::HIR::TypeRef tmp; - if( ve.vals[j].is_LValue() && m_resolve.is_type_phantom_data( mir_res.get_lvalue_type(tmp, ve.vals[j].as_LValue())) ) - continue ; - - if( j != 0 ) m_of << ";\n\t"; - emit_lvalue(e.dst); - if(ve.variant_idx != ~0u) - m_of << ".DATA.var_" << ve.variant_idx; - m_of << "._" << j << " = "; - emit_param(ve.vals[j]); - } - ) - ) - m_of << ";"; - m_of << "\t// " << e.dst << " = " << e.src; - m_of << "\n"; - break; } - } + emit_statement(mir_res, stmt); } mir_res.set_cur_stmt_term(i); @@ -1816,7 +1453,7 @@ namespace { auto it = m_enum_repr_cache.find( ty.m_data.as_Path().path.m_data.as_Generic() ); if( it != m_enum_repr_cache.end() ) { - MIR_ASSERT(mir_res, e.targets.size() == 2, ""); + MIR_ASSERT(mir_res, e.targets.size() == 2, "Non-zero optimised type a variant count that isn't 2"); m_of << "\tif("; emit_lvalue(e.val); emit_nonzero_path(it->second); m_of << ")\n"; m_of << "\t\tgoto bb" << e.targets[1] << ";\n"; m_of << "\telse\n"; @@ -1841,85 +1478,683 @@ namespace { m_of << "\t}\n"; } ), + (SwitchValue, + MIR_TODO(mir_res, "SwitchValue in C codegen"); + ), (Call, - m_of << "\t"; - if( e.fcn.is_Intrinsic() ) + emit_term_call(mir_res, e, 1); + m_of << "\tgoto bb" << e.ret_block << ";\n"; + ) + ) + m_of << "\t// ^ " << code->blocks[i].terminator << "\n"; + } + m_of << "}\n"; + m_of.flush(); + m_mir_res = nullptr; + } + + void emit_fcn_node(::MIR::TypeResolve& mir_res, const Node& node, unsigned indent_level) + { + auto indent = RepeatLitStr { "\t", static_cast<int>(indent_level) }; + TU_MATCHA( (node), (e), + (Block, + for(size_t i = 0; i < e.nodes.size(); i ++) + { + const auto& snr = e.nodes[i]; + if( snr.node ) { + emit_fcn_node(mir_res, *snr.node, indent_level); + } + else { + DEBUG(mir_res << "BB" << snr.bb_idx); + m_of << indent << "bb" << snr.bb_idx << ":\n"; + const auto& bb = mir_res.m_fcn.blocks.at(snr.bb_idx); + for(const auto& stmt : bb.statements) + { + mir_res.set_cur_stmt(snr.bb_idx, (&stmt - &bb.statements.front())); + this->emit_statement(mir_res, stmt, indent_level); + } + + TU_MATCHA( (bb.terminator), (te), + (Incomplete, ), + (Return, + assert(i == e.nodes.size()-1 && "Return"); + m_of << indent << "return;\n"; + ), + (Goto, + // Ignore (handled by caller) + ), + (Diverge, + ), + (Panic, + ), + (If, + //assert(i == e.nodes.size()-1 && "If"); + ), + (Call, + // TODO: Emit call + emit_term_call(mir_res, te, indent_level); + ), + (Switch, + //assert(i == e.nodes.size()-1 && "Switch"); + ), + (SwitchValue, + //assert(i == e.nodes.size()-1 && "Switch"); + ) + ) + } + } + ), + (If, + m_of << indent << "if("; emit_lvalue(*e.val); m_of << ") {\n"; + if( e.arm_true.node ) { + emit_fcn_node(mir_res, *e.arm_true.node, indent_level+1); + } + else { + m_of << indent << "\tgoto bb" << e.arm_true.bb_idx << ";\n"; + } + m_of << indent << "}\n"; + m_of << indent << "else {\n"; + if( e.arm_false.node ) { + emit_fcn_node(mir_res, *e.arm_false.node, indent_level+1); + } + else { + m_of << indent << "\tgoto bb" << e.arm_false.bb_idx << ";\n"; + } + m_of << indent << "}\n"; + ), + (Switch, + this->emit_term_switch(mir_res, *e.val, e.arms.size(), indent_level, [&](auto idx) { + const auto& arm = e.arms.at(idx); + if( arm.node ) { + m_of << "{\n"; + this->emit_fcn_node(mir_res, *arm.node, indent_level+1); + m_of << indent << "\t} "; + if( arm.has_target() && arm.target() != e.next_bb ) { + m_of << "goto bb" << arm.target() << ";"; + } + else { + m_of << "break;"; + } + } + else { + m_of << "goto bb" << arm.bb_idx << ";"; + } + }); + ), + (Loop, + m_of << indent << "for(;;) {\n"; + assert(e.code.node); + assert(e.code.node->is_Block()); + this->emit_fcn_node(mir_res, *e.code.node, indent_level+1); + m_of << indent << "}\n"; + ) + ) + } + + void emit_statement(const ::MIR::TypeResolve& mir_res, const ::MIR::Statement& stmt, unsigned indent_level=1) + { + auto indent = RepeatLitStr { "\t", static_cast<int>(indent_level) }; + switch( stmt.tag() ) + { + case ::MIR::Statement::TAGDEAD: throw ""; + case ::MIR::Statement::TAG_ScopeEnd: + m_of << indent << "// " << stmt << "\n"; + break; + case ::MIR::Statement::TAG_SetDropFlag: { + const auto& e = stmt.as_SetDropFlag(); + m_of << indent << "df" << e.idx << " = "; + if( e.other == ~0u ) + m_of << e.new_val; + else + m_of << (e.new_val ? "!" : "") << "df" << e.other; + m_of << ";\n"; + break; } + case ::MIR::Statement::TAG_Drop: { + const auto& e = stmt.as_Drop(); + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, e.slot); + + if( e.flag_idx != ~0u ) + m_of << indent << "if( df" << e.flag_idx << " ) {\n"; + + switch( e.kind ) + { + case ::MIR::eDropKind::SHALLOW: + // Shallow drops are only valid on owned_box + if( const auto* ity = m_resolve.is_type_owned_box(ty) ) { - const auto& name = e.fcn.as_Intrinsic().name; - const auto& params = e.fcn.as_Intrinsic().params; - emit_intrinsic_call(name, params, e); - m_of << "\tgoto bb" << e.ret_block << ";\n"; - break ; + // Emit a call to box_free for the type + ::HIR::GenericPath box_free { m_crate.get_lang_item_path(sp, "box_free"), { ity->clone() } }; + // TODO: This is specific to the official liballoc's owned_box + m_of << indent << Trans_Mangle(box_free) << "("; emit_lvalue(e.slot); m_of << "._0._0._0);\n"; + } + else + { + MIR_BUG(mir_res, "Shallow drop on non-Box - " << ty); } + break; + case ::MIR::eDropKind::DEEP: + emit_destructor_call(e.slot, ty, false, indent_level + (e.flag_idx != ~0u ? 1 : 0)); + break; + } + if( e.flag_idx != ~0u ) + m_of << indent << "}\n"; + break; } + case ::MIR::Statement::TAG_Asm: { + const auto& e = stmt.as_Asm(); - TU_MATCHA( (e.fcn), (e2), - (Value, + struct H { + static bool has_flag(const ::std::vector<::std::string>& flags, const char* des) { + return ::std::find_if(flags.begin(), flags.end(), [des](const auto&x){return x==des;}) != flags.end(); + } + static const char* convert_reg(const char* r) { + if( ::std::strcmp(r, "{eax}") == 0 || ::std::strcmp(r, "{rax}") == 0 ) { + return "a"; + } + else { + return r; + } + } + }; + bool is_volatile = H::has_flag(e.flags, "volatile"); + bool is_intel = H::has_flag(e.flags, "intel"); + + m_of << indent << "__asm__ "; + if(is_volatile) m_of << "__volatile__"; + // TODO: Convert format string? + // TODO: Use a C-specific escaper here. + m_of << "(\"" << (is_intel ? ".syntax intel; " : "") << FmtEscaped(e.tpl) << (is_intel ? ".syntax att; " : "") << "\""; + m_of << ": "; + for(unsigned int i = 0; i < e.outputs.size(); i ++ ) + { + const auto& v = e.outputs[i]; + if( i != 0 ) m_of << ", "; + m_of << "\""; + switch(v.first[0]) + { + case '=': m_of << "="; break; + case '+': m_of << "+"; break; + default: MIR_TODO(mir_res, "Handle asm! output leader '" << v.first[0] << "'"); + } + m_of << H::convert_reg(v.first.c_str()+1); + m_of << "\"("; emit_lvalue(v.second); m_of << ")"; + } + m_of << ": "; + for(unsigned int i = 0; i < e.inputs.size(); i ++ ) + { + const auto& v = e.inputs[i]; + if( i != 0 ) m_of << ", "; + m_of << "\"" << v.first << "\"("; emit_lvalue(v.second); m_of << ")"; + } + m_of << ": "; + for(unsigned int i = 0; i < e.clobbers.size(); i ++ ) + { + if( i != 0 ) m_of << ", "; + m_of << "\"" << e.clobbers[i] << "\""; + } + m_of << ");\n"; + break; } + case ::MIR::Statement::TAG_Assign: { + const auto& e = stmt.as_Assign(); + DEBUG("- " << e.dst << " = " << e.src); + m_of << indent; + TU_MATCHA( (e.src), (ve), + (Use, + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, ve); + if( ty == ::HIR::TypeRef::new_diverge() ) { + m_of << "abort()"; + break; + } + emit_lvalue(e.dst); + m_of << " = "; + emit_lvalue(ve); + ), + (Constant, + emit_lvalue(e.dst); + m_of << " = "; + emit_constant(ve, &e.dst); + ), + (SizedArray, + if( ve.count == 0 ) { + } + else if( ve.count == 1 ) { + emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_param(ve.val); + } + else if( ve.count == 2 ) { + emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_param(ve.val); m_of << ";\n" << indent; + emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_param(ve.val); + } + else if( ve.count == 3 ) { + emit_lvalue(e.dst); m_of << ".DATA[0] = "; emit_param(ve.val); m_of << ";\n" << indent; + emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_param(ve.val); m_of << ";\n" << indent; + emit_lvalue(e.dst); m_of << ".DATA[2] = "; emit_param(ve.val); + } + else { + 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, + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); + bool special = false; + // If the inner value has type [T] or str, create DST based on inner pointer and existing metadata + TU_IFLET(::MIR::LValue, ve.val, Deref, le, + if( metadata_type(ty) != MetadataType::None ) { + emit_lvalue(e.dst); + m_of << " = "; + emit_lvalue(*le.val); + 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, + 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; + + // 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; + } + ) + if( !special ) + { + emit_lvalue(e.dst); + m_of << " = "; + m_of << "& "; emit_lvalue(ve.val); + } + ), + (Cast, + if( m_resolve.is_type_phantom_data(ve.type) ) { + m_of << "/* PhandomData cast */\n"; + return ; + } + + emit_lvalue(e.dst); + m_of << " = "; + m_of << "("; emit_ctype(ve.type); m_of << ")"; + // TODO: If the source is an unsized borrow, then extract the pointer + bool special = false; + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, ve.val); + // If the destination is a thin pointer + if( ve.type.m_data.is_Pointer() && !is_dst( *ve.type.m_data.as_Pointer().inner ) ) + { + // NOTE: Checks the result of the deref + if( (ty.m_data.is_Borrow() && is_dst(*ty.m_data.as_Borrow().inner)) + || (ty.m_data.is_Pointer() && is_dst(*ty.m_data.as_Pointer().inner)) + ) { - ::HIR::TypeRef tmp; - const auto& ty = mir_res.get_lvalue_type(tmp, e2); - MIR_ASSERT(mir_res, ty.m_data.is_Function(), "Call::Value on non-function - " << ty); - if( !ty.m_data.as_Function().m_rettype->m_data.is_Diverge() ) + emit_lvalue(ve.val); + m_of << ".PTR"; + special = true; + } + } + if( ve.type.m_data.is_Primitive() && ty.m_data.is_Path() && ty.m_data.as_Path().binding.is_Enum() ) + { + emit_lvalue(ve.val); + m_of << ".TAG"; + special = true; + } + if( !special ) + { + emit_lvalue(ve.val); + } + ), + (BinOp, + emit_lvalue(e.dst); + m_of << " = "; + ::HIR::TypeRef tmp; + const auto& ty = ve.val_l.is_LValue() ? mir_res.get_lvalue_type(tmp, ve.val_l.as_LValue()) : tmp = mir_res.get_const_type(ve.val_l.as_Constant()); + if( ty.m_data.is_Borrow() ) { + m_of << "(slice_cmp("; emit_param(ve.val_l); m_of << ", "; emit_param(ve.val_r); m_of << ")"; + switch(ve.op) + { + case ::MIR::eBinOp::EQ: m_of << " == 0"; break; + case ::MIR::eBinOp::NE: m_of << " != 0"; break; + case ::MIR::eBinOp::GT: m_of << " > 0"; break; + case ::MIR::eBinOp::GE: m_of << " >= 0"; break; + case ::MIR::eBinOp::LT: m_of << " < 0"; break; + case ::MIR::eBinOp::LE: m_of << " <= 0"; break; + default: + MIR_BUG(mir_res, "Unknown comparison of a &-ptr - " << e.src << " with " << ty); + } + m_of << ")"; + break; + } + else if( const auto* te = ty.m_data.opt_Pointer() ) { + if( metadata_type(*te->inner) != MetadataType::None ) + { + switch(ve.op) { - emit_lvalue(e.ret_val); m_of << " = "; + case ::MIR::eBinOp::EQ: + emit_param(ve.val_l); m_of << ".PTR == "; emit_param(ve.val_r); m_of << ".PTR && "; + emit_param(ve.val_l); m_of << ".META == "; emit_param(ve.val_r); m_of << ".META"; + break; + case ::MIR::eBinOp::NE: + emit_param(ve.val_l); m_of << ".PTR != "; emit_param(ve.val_r); m_of << ".PTR || "; + emit_param(ve.val_l); m_of << ".META != "; emit_param(ve.val_r); m_of << ".META"; + break; + default: + MIR_BUG(mir_res, "Unknown comparison of a *-ptr - " << e.src << " with " << ty); } } - m_of << "("; emit_lvalue(e2); m_of << ")"; - ), - (Path, + else { - bool is_diverge = false; - TU_MATCHA( (e2.m_data), (pe), - (Generic, - const auto& fcn = m_crate.get_function_by_path(sp, pe.m_path); - is_diverge |= fcn.m_return.m_data.is_Diverge(); - // TODO: Monomorph. - ), - (UfcsUnknown, - ), - (UfcsInherent, - // TODO: Check if the return type is ! - is_diverge |= m_resolve.m_crate.find_type_impls(*pe.type, [&](const auto& ty)->const auto& { return ty; }, - [&](const auto& impl) { - // Associated functions - { - auto it = impl.m_methods.find(pe.item); - if( it != impl.m_methods.end() ) { - return it->second.data.m_return.m_data.is_Diverge(); - } - } - // Associated static (undef) - return false; - }); - ), - (UfcsKnown, - // TODO: Check if the return type is ! - ) - ) - if(!is_diverge) + emit_param(ve.val_l); + switch(ve.op) { - emit_lvalue(e.ret_val); m_of << " = "; + case ::MIR::eBinOp::EQ: m_of << " == "; break; + case ::MIR::eBinOp::NE: m_of << " != "; break; + case ::MIR::eBinOp::GT: m_of << " > " ; break; + case ::MIR::eBinOp::GE: m_of << " >= "; break; + case ::MIR::eBinOp::LT: m_of << " < " ; break; + case ::MIR::eBinOp::LE: m_of << " <= "; break; + default: + MIR_BUG(mir_res, "Unknown comparison of a *-ptr - " << e.src << " with " << ty); } + emit_param(ve.val_r); } - m_of << Trans_Mangle(e2); + break; + } + else if( ve.op == ::MIR::eBinOp::MOD && (ty == ::HIR::CoreType::F32 || ty == ::HIR::CoreType::F64) ) { + if( ty == ::HIR::CoreType::F32 ) + m_of << "remainderf"; + else + m_of << "remainder"; + m_of << "("; emit_param(ve.val_l); m_of << ", "; emit_param(ve.val_r); m_of << ")"; + break; + } + else { + } + + emit_param(ve.val_l); + switch(ve.op) + { + case ::MIR::eBinOp::ADD: m_of << " + "; break; + case ::MIR::eBinOp::SUB: m_of << " - "; break; + case ::MIR::eBinOp::MUL: m_of << " * "; break; + case ::MIR::eBinOp::DIV: m_of << " / "; break; + case ::MIR::eBinOp::MOD: m_of << " % "; break; + + case ::MIR::eBinOp::BIT_OR: m_of << " | "; break; + case ::MIR::eBinOp::BIT_AND: m_of << " & "; break; + case ::MIR::eBinOp::BIT_XOR: m_of << " ^ "; break; + case ::MIR::eBinOp::BIT_SHR: m_of << " >> "; break; + case ::MIR::eBinOp::BIT_SHL: m_of << " << "; break; + case ::MIR::eBinOp::EQ: m_of << " == "; break; + case ::MIR::eBinOp::NE: m_of << " != "; break; + case ::MIR::eBinOp::GT: m_of << " > " ; break; + case ::MIR::eBinOp::GE: m_of << " >= "; break; + case ::MIR::eBinOp::LT: m_of << " < " ; break; + case ::MIR::eBinOp::LE: m_of << " <= "; break; + + case ::MIR::eBinOp::ADD_OV: + case ::MIR::eBinOp::SUB_OV: + case ::MIR::eBinOp::MUL_OV: + case ::MIR::eBinOp::DIV_OV: + MIR_TODO(mir_res, "Overflow"); + break; + } + emit_param(ve.val_r); + ), + (UniOp, + ::HIR::TypeRef tmp; + emit_lvalue(e.dst); + m_of << " = "; + switch(ve.op) + { + case ::MIR::eUniOp::NEG: m_of << "-"; break; + case ::MIR::eUniOp::INV: + if( mir_res.get_lvalue_type(tmp, e.dst) == ::HIR::CoreType::Bool ) + m_of << "!"; + else + m_of << "~"; + break; + } + emit_lvalue(ve.val); + ), + (DstMeta, + emit_lvalue(e.dst); + m_of << " = "; + emit_lvalue(ve.val); + m_of << ".META"; + ), + (DstPtr, + emit_lvalue(e.dst); + m_of << " = "; + emit_lvalue(ve.val); + m_of << ".PTR"; + ), + (MakeDst, + 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, + for(unsigned int j = 0; j < ve.vals.size(); j ++) { + if( j != 0 ) m_of << ";\n" << indent; + emit_lvalue(e.dst); + m_of << "._" << j << " = "; + emit_param(ve.vals[j]); + } + ), + (Array, + 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, + const auto& tyi = m_crate.get_typeitem_by_path(sp, ve.path.m_path); + if( tyi.is_Union() ) + { + emit_lvalue(e.dst); + m_of << ".var_" << ve.index << " = "; emit_param(ve.val); + } + else if( const auto* enm_p = tyi.opt_Enum() ) + { + MIR_TODO(mir_res, "Construct enum with RValue::Variant"); + if( enm_p->is_value() ) + { + emit_lvalue(e.dst); m_of << ".TAG = " << enm_p->get_value(ve.index) << ""; + } + else + { + emit_lvalue(e.dst); m_of << ".TAG = " << ve.index << ";\n\t"; + emit_lvalue(e.dst); m_of << ".DATA"; + m_of << ".var_" << ve.index << " = "; emit_param(ve.val); + } + } + else + { + BUG(mir_res.sp, "Unexpected type in Variant"); + } + ), + (Struct, + if(ve.variant_idx != ~0u) + { + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, e.dst); + const auto* enm_p = ty.m_data.as_Path().binding.as_Enum(); + + auto it = m_enum_repr_cache.find(ty.m_data.as_Path().path.m_data.as_Generic()); + if( it != m_enum_repr_cache.end() ) + { + if( ve.variant_idx == 0 ) { + // TODO: Use nonzero_path + m_of << "memset(&"; emit_lvalue(e.dst); m_of << ", 0, sizeof("; emit_ctype(ty); m_of << "))"; + } + else if( ve.variant_idx == 1 ) { + emit_lvalue(e.dst); + m_of << "._0 = "; + emit_param(ve.vals[0]); + } + else { + } + break; + } + else if( enm_p->is_value() ) + { + emit_lvalue(e.dst); + m_of << ".TAG = " << enm_p->get_value(ve.variant_idx); + assert(ve.vals.size() == 0); + } + else + { + emit_lvalue(e.dst); + m_of << ".TAG = " << ve.variant_idx; + } + if(ve.vals.size() > 0) + m_of << ";\n" << indent; + } + + for(unsigned int j = 0; j < ve.vals.size(); j ++) + { + // HACK: Don't emit assignment of PhantomData + ::HIR::TypeRef tmp; + if( ve.vals[j].is_LValue() && m_resolve.is_type_phantom_data( mir_res.get_lvalue_type(tmp, ve.vals[j].as_LValue())) ) + continue ; + + if( j != 0 ) m_of << ";\n" << indent; + emit_lvalue(e.dst); + if(ve.variant_idx != ~0u) + m_of << ".DATA.var_" << ve.variant_idx; + m_of << "._" << j << " = "; + emit_param(ve.vals[j]); + } + ) + ) + m_of << ";"; + m_of << "\t// " << e.dst << " = " << e.src; + m_of << "\n"; + break; } + } + } + void emit_term_switch(const ::MIR::TypeResolve& mir_res, const ::MIR::LValue& val, size_t n_arms, unsigned indent_level, ::std::function<void(size_t)> cb) + { + auto indent = RepeatLitStr { "\t", static_cast<int>(indent_level) }; + + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, val); + MIR_ASSERT(mir_res, ty.m_data.is_Path(), "Switch over non-Path type"); + MIR_ASSERT(mir_res, ty.m_data.as_Path().binding.is_Enum(), "Switch over non-enum"); + const auto* enm = ty.m_data.as_Path().binding.as_Enum(); + + auto it = m_enum_repr_cache.find( ty.m_data.as_Path().path.m_data.as_Generic() ); + if( it != m_enum_repr_cache.end() ) + { + //MIR_ASSERT(mir_res, e.targets.size() == 2, "NonZero optimised representation for an enum without two variants"); + MIR_ASSERT(mir_res, n_arms == 2, "NonZero optimised switch without two arms"); + m_of << indent << "if("; emit_lvalue(val); emit_nonzero_path(it->second); m_of << ")\n"; + m_of << indent; + cb(1); + m_of << "\n"; + m_of << indent << "else\n"; + m_of << indent; + cb(0); + m_of << "\n"; + } + else if( enm->is_value() ) + { + m_of << indent << "switch("; emit_lvalue(val); m_of << ".TAG) {\n"; + for(size_t j = 0; j < n_arms; j ++) + { + m_of << indent << "case " << enm->get_value(j) << ": "; + cb(j); + m_of << "\n"; + } + m_of << indent << "default: abort();\n"; + m_of << indent << "}\n"; + } + else + { + m_of << indent << "switch("; emit_lvalue(val); m_of << ".TAG) {\n"; + for(size_t j = 0; j < n_arms; j ++) + { + m_of << indent << "case " << j << ": "; + cb(j); + m_of << "\n"; + } + m_of << indent << "default: abort();\n"; + m_of << indent << "}\n"; + } + } + void emit_term_call(const ::MIR::TypeResolve& mir_res, const ::MIR::Terminator::Data_Call& e, unsigned indent_level) + { + auto indent = RepeatLitStr { "\t", static_cast<int>(indent_level) }; + m_of << indent; + + TU_MATCHA( (e.fcn), (e2), + (Value, + { + ::HIR::TypeRef tmp; + const auto& ty = mir_res.get_lvalue_type(tmp, e2); + MIR_ASSERT(mir_res, ty.m_data.is_Function(), "Call::Value on non-function - " << ty); + if( !ty.m_data.as_Function().m_rettype->m_data.is_Diverge() ) + { + emit_lvalue(e.ret_val); m_of << " = "; + } + } + m_of << "("; emit_lvalue(e2); m_of << ")"; + ), + (Path, + { + bool is_diverge = false; + TU_MATCHA( (e2.m_data), (pe), + (Generic, + const auto& fcn = m_crate.get_function_by_path(sp, pe.m_path); + is_diverge |= fcn.m_return.m_data.is_Diverge(); + // TODO: Monomorph. + ), + (UfcsUnknown, ), - (Intrinsic, - MIR_BUG(mir_res, "Intrinsic not expected, should be handled above"); + (UfcsInherent, + // TODO: Check if the return type is ! + is_diverge |= m_resolve.m_crate.find_type_impls(*pe.type, [&](const auto& ty)->const auto& { return ty; }, + [&](const auto& impl) { + // Associated functions + { + auto it = impl.m_methods.find(pe.item); + if( it != impl.m_methods.end() ) { + return it->second.data.m_return.m_data.is_Diverge(); + } + } + // Associated static (undef) + return false; + }); + ), + (UfcsKnown, + // TODO: Check if the return type is ! ) ) - m_of << "("; - for(unsigned int j = 0; j < e.args.size(); j ++) { - if(j != 0) m_of << ","; - m_of << " "; emit_param(e.args[j]); + if(!is_diverge) + { + emit_lvalue(e.ret_val); m_of << " = "; } - m_of << " );\n"; - m_of << "\tgoto bb" << e.ret_block << ";\n"; - ) + } + m_of << Trans_Mangle(e2); + ), + (Intrinsic, + const auto& name = e.fcn.as_Intrinsic().name; + const auto& params = e.fcn.as_Intrinsic().params; + emit_intrinsic_call(name, params, e); + return ; ) - m_of << "\t// ^ " << code->blocks[i].terminator << "\n"; + ) + m_of << "("; + for(unsigned int j = 0; j < e.args.size(); j ++) { + if(j != 0) m_of << ","; + m_of << " "; emit_param(e.args[j]); } - m_of << "}\n"; - m_of.flush(); - m_mir_res = nullptr; + m_of << " );\n"; } private: const ::HIR::TypeRef& monomorphise_fcn_return(::HIR::TypeRef& tmp, const ::HIR::Function& item, const Trans_Params& params) @@ -2168,7 +2403,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 ); + 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 */ ); } else if( name == "needs_drop" ) { // Returns `true` if the actual type given as `T` requires drop glue; @@ -2508,8 +2743,9 @@ namespace { m_of << ";\n"; } - void emit_destructor_call(const ::MIR::LValue& slot, const ::HIR::TypeRef& ty, bool unsized_valid) + void emit_destructor_call(const ::MIR::LValue& slot, const ::HIR::TypeRef& ty, bool unsized_valid, unsigned indent_level) { + auto indent = RepeatLitStr { "\t", static_cast<int>(indent_level) }; TU_MATCHA( (ty.m_data), (te), // Impossible (Diverge, ), @@ -2530,7 +2766,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 ); + emit_destructor_call( ::MIR::LValue::make_Deref({ box$(slot.clone()) }), *te.inner, true, indent_level ); } ), (Path, @@ -2541,13 +2777,13 @@ namespace { switch( metadata_type(ty) ) { case MetadataType::None: - m_of << "\t" << Trans_Mangle(p) << "(&"; emit_lvalue(slot); m_of << ");\n"; + m_of << indent << Trans_Mangle(p) << "(&"; emit_lvalue(slot); m_of << ");\n"; break; case MetadataType::Slice: make_fcn = "make_sliceptr"; if(0) case MetadataType::TraitObject: make_fcn = "make_traitobjptr"; - m_of << "\t" << Trans_Mangle(p) << "( " << make_fcn << "("; + m_of << indent << Trans_Mangle(p) << "( " << make_fcn << "("; if( slot.is_Deref() ) { emit_lvalue(*slot.as_Deref().val); @@ -2570,12 +2806,9 @@ namespace { // Emit destructors for all entries if( te.size_val > 0 ) { - ::MIR::LValue lv = ::MIR::LValue::make_Field({ box$(slot.clone()), 0 }); - for(unsigned int i = 0; i < te.size_val; i ++) - { - lv.as_Field().field_index = i; - emit_destructor_call(lv, *te.inner, false); - } + 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); + m_of << "\n" << indent << "}"; } ), (Tuple, @@ -2586,7 +2819,7 @@ namespace { 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)); + emit_destructor_call(lv, te[i], unsized_valid && (i == te.size()-1), indent_level); } } ), @@ -2596,7 +2829,7 @@ namespace { 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 << "((VTABLE_HDR*)"; emit_lvalue(*lvp->as_Deref().val); m_of << ".META)->drop("; + m_of << indent << "((VTABLE_HDR*)"; emit_lvalue(*lvp->as_Deref().val); m_of << ".META)->drop("; if( const auto* ve = slot.opt_Deref() ) { emit_lvalue(*ve->val); m_of << ".PTR"; @@ -2613,10 +2846,9 @@ namespace { 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 << ")"); // Call destructor on all entries - m_of << "for(unsigned i = 0; i < "; emit_lvalue(*lvp->as_Deref().val); m_of << ".META; i++) {"; - m_of << "\t\t"; - emit_destructor_call(::MIR::LValue::make_Index({ box$(slot.clone()), box$(::MIR::LValue::make_Temporary({~0u})) }), *te.inner, false); - m_of << "\n\t}"; + 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 << "\n" << indent << "}"; ) ) } @@ -2817,20 +3049,17 @@ namespace { void emit_lvalue(const ::MIR::LValue& val) { TU_MATCHA( (val), (e), - (Variable, - m_of << "var" << e; - ), - (Temporary, - if( e.idx == ~0u ) - m_of << "i"; - else - m_of << "tmp" << e.idx; + (Return, + m_of << "rv"; ), (Argument, m_of << "arg" << e.idx; ), - (Return, - m_of << "rv"; + (Local, + if( e == ~0u ) + m_of << "i"; + else + m_of << "var" << e; ), (Static, m_of << Trans_Mangle(e); @@ -2991,15 +3220,7 @@ namespace { } ), (Float, - if( ::std::isnan(c.v) ) { - m_of << "NAN"; - } - else if( ::std::isinf(c.v) ) { - m_of << "INFINITY"; - } - else { - m_of << c.v; - } + this->emit_float(c.v); ), (Bool, m_of << (c.v ? "true" : "false"); diff --git a/src/trans/codegen_c.hpp b/src/trans/codegen_c.hpp new file mode 100644 index 00000000..72d0c796 --- /dev/null +++ b/src/trans/codegen_c.hpp @@ -0,0 +1,47 @@ +/* + */ +#pragma once +#include <vector> +#include <memory> + +class Node; + +struct NodeRef +{ + ::std::unique_ptr<Node> node; + size_t bb_idx; + + NodeRef(size_t idx): bb_idx(idx) {} + NodeRef(Node node); + + bool has_target() const; + size_t target() const; + + bool operator==(size_t idx) const { + return !node && bb_idx == idx; + } +}; + +TAGGED_UNION(Node, Block, +(Block, struct { + size_t next_bb; + ::std::vector<NodeRef> nodes; + }), +(If, struct { + size_t next_bb; + const ::MIR::LValue* val; + NodeRef arm_false; + NodeRef arm_true; + }), +(Switch, struct { + size_t next_bb; + const ::MIR::LValue* val; + ::std::vector<NodeRef> arms; + }), +(Loop, struct { + size_t next_bb; + NodeRef code; + }) +); + +extern ::std::vector<Node> MIR_To_Structured(const ::MIR::Function& fcn); diff --git a/src/trans/codegen_c_structured.cpp b/src/trans/codegen_c_structured.cpp new file mode 100644 index 00000000..888f9a26 --- /dev/null +++ b/src/trans/codegen_c_structured.cpp @@ -0,0 +1,298 @@ +/* + * MRustC - Rust Compiler + * - By John Hodge (Mutabah/thePowersGang) + * + * trans/codegen_c_structured.cpp + * - Converts MIR into a semi-structured form + */ +#include <common.hpp> +#include <mir/mir.hpp> +#include <algorithm> +#include "codegen_c.hpp" + +NodeRef::NodeRef(Node node_data): + node(new Node(mv$(node_data))), + bb_idx(SIZE_MAX) +{ +} +bool NodeRef::has_target() const +{ + if( node ) { + TU_MATCHA( (*this->node), (e), + (Block, + return e.next_bb != SIZE_MAX; + ), + (If, + return e.next_bb != SIZE_MAX; + ), + (Switch, + return e.next_bb != SIZE_MAX; + ), + (Loop, + return e.next_bb != SIZE_MAX; + ) + ) + throw ""; + } + else { + return true; + } +} +size_t NodeRef::target() const +{ + if( node ) { + TU_MATCHA( (*this->node), (e), + (Block, + return e.next_bb; + ), + (If, + return e.next_bb; + ), + (Switch, + return e.next_bb; + ), + (Loop, + return e.next_bb; + ) + ) + throw ""; + } + else { + return bb_idx; + } +} + +class Converter +{ + const ::MIR::Function& m_fcn; +public: + ::std::vector<unsigned> m_block_ref_count; + ::std::vector<bool> m_blocks_used; + + Converter(const ::MIR::Function& fcn): + m_fcn(fcn) + { + + } + + // Returns true if the passed block is the start of a self-contained sequence of blocks + bool bb_is_opening(size_t bb_idx) + { + if( m_blocks_used[bb_idx] ) { + return false; + } + else if( m_block_ref_count[bb_idx] > 1 ) { + // TODO: Determine if these multiple references are from the block looping back on itself + return false; + } + else { + return true; + } + } + NodeRef process_node_ref(size_t bb_idx) + { + if( bb_is_opening(bb_idx) ) { + return NodeRef( process_node(bb_idx) ); + } + else { + return NodeRef(bb_idx); + } + } + + Node process_node(size_t bb_idx) + { + TRACE_FUNCTION_F(bb_idx); + ::std::vector<NodeRef> refs; + for(;;) + { + DEBUG("bb_idx = " << bb_idx); + bool stop = false; + assert( !m_blocks_used[bb_idx] ); + m_blocks_used[bb_idx] = true; + + refs.push_back( NodeRef(bb_idx) ); + + const auto& blk = m_fcn.blocks.at(bb_idx); + DEBUG("> " << blk.terminator); + TU_MATCHA( (blk.terminator), (te), + (Incomplete, + stop = true; + ), + (Goto, + bb_idx = te; + ), + (Panic, + TODO(Span(), "Panic"); + ), + (Diverge, + stop = true; + ), + (Return, + stop = true; + ), + (If, + auto arm0 = process_node_ref(te.bb0); + auto arm1 = process_node_ref(te.bb1); + if( arm0.has_target() && arm1.has_target() ) { + if( arm0.target() == arm1.target() ) { + bb_idx = arm0.target(); + } + else { + stop = true; + } + } + else if( arm0.has_target() ) { + bb_idx = arm0.target(); + } + else if( arm1.has_target() ) { + bb_idx = arm1.target(); + } + else { + // No target from either arm + stop = false; + } + refs.push_back(Node::make_If({ bb_idx, &te.cond, mv$(arm0), mv$(arm1) })); + ), + (Switch, + ::std::vector<NodeRef> arms; + ::std::vector<size_t> next_blocks; + for(auto& tgt : te.targets) + { + arms.push_back( process_node_ref(tgt) ); + if( arms.back().has_target() ) + { + next_blocks.push_back( arms.back().target() ); + } + } + ::std::sort(next_blocks.begin(), next_blocks.end()); + size_t exit_bb = SIZE_MAX; + if(!next_blocks.empty()) + { + size_t cur = next_blocks[0]; + size_t cur_count = 0; + size_t max_count = 0; + for(auto b : next_blocks) + { + if(cur == b) { + cur_count ++; + } + else { + if( cur_count > max_count ) { + exit_bb = cur; + } + cur = b; + cur_count = 1; + } + } + if( cur_count > max_count ) { + exit_bb = cur; + } + } + refs.push_back(Node::make_Switch({ exit_bb, &te.val, mv$(arms) })); + stop = true; + ), + (SwitchValue, + TODO(Span(), "SwitchValue"); + ), + (Call, + // NOTE: Let the panic arm just be a goto + bb_idx = te.ret_block; + ) + ) + + if( stop ) + { + break; + } + + // If `bb_idx` is in `refs` as a NodeRef + auto it = ::std::find(refs.begin(), refs.end(), bb_idx); + if( it != refs.end() ) + { + // Wrap ibb_idxms from `it` to `refs.end()` in a `loop` block + ::std::vector<NodeRef> loop_blocks; + loop_blocks.reserve(refs.end() - it); + for(auto it2 = it; it2 != refs.end(); ++it2) + loop_blocks.push_back( mv$(*it2) ); + auto loop_node = NodeRef( Node::make_Block({ SIZE_MAX, mv$(loop_blocks) }) ); + + refs.push_back( Node::make_Loop({ SIZE_MAX, mv$(loop_node) }) ); + // TODO: If there is only one `goto` in the above loop, assume it's the target + DEBUG("Loop"); + break; + } + else if( bb_is_opening(bb_idx) ) + { + DEBUG("Destination " << bb_idx << " is unreferenced+unvisited"); + } + else + { + break; + } + } + + return Node::make_Block({ bb_idx, mv$(refs) }); + } +}; + +::std::vector<Node> MIR_To_Structured(const ::MIR::Function& fcn) +{ + Converter conv(fcn); + conv.m_block_ref_count.resize( fcn.blocks.size() ); + conv.m_block_ref_count[0] += 1; + for(const auto& blk : fcn.blocks) + { + TU_MATCHA( (blk.terminator), (te), + (Incomplete, + ), + (Goto, + conv.m_block_ref_count[te] += 1; + ), + (Panic, + conv.m_block_ref_count[te.dst] += 1; + ), + (Diverge, + ), + (Return, + ), + (If, + conv.m_block_ref_count[te.bb0] += 1; + conv.m_block_ref_count[te.bb1] += 1; + ), + (Switch, + for(auto tgt : te.targets) + conv.m_block_ref_count[tgt] += 1; + ), + (SwitchValue, + for(auto tgt : te.targets) + conv.m_block_ref_count[tgt] += 1; + conv.m_block_ref_count[te.def_target] += 1; + ), + (Call, + conv.m_block_ref_count[te.ret_block] += 1; + conv.m_block_ref_count[te.panic_block] += 1; + ) + ) + } + + // First Block: Becomes a block in structured output + // - Terminator selects what the next block will be + // - + + // Find next unvisited block + conv.m_blocks_used.resize( fcn.blocks.size() ); + ::std::vector<Node> nodes; + for(size_t bb_idx = 0; bb_idx < fcn.blocks.size(); bb_idx ++) + { + if( conv.m_blocks_used[bb_idx] ) + continue; + + nodes.push_back( conv.process_node(bb_idx) ); + } + + + // Return. + return nodes; +} + + diff --git a/src/trans/enumerate.cpp b/src/trans/enumerate.cpp index 02116d18..41489e2b 100644 --- a/src/trans/enumerate.cpp +++ b/src/trans/enumerate.cpp @@ -623,9 +623,7 @@ void Trans_Enumerate_Types(EnumState& state) if( fcn.m_code.m_mir ) { const auto& mir = *fcn.m_code.m_mir; - for(const auto& ty : mir.named_variables) - tv.visit_type(monomorph(ty)); - for(const auto& ty : mir.temporaries) + for(const auto& ty : mir.locals) tv.visit_type(monomorph(ty)); // TODO: Find all LValue::Deref instances and get the result type @@ -646,19 +644,19 @@ void Trans_Enumerate_Types(EnumState& state) }; // Recurse, if Deref get the type and add it to the visitor TU_MATCHA( (lv), (e), - (Variable, + (Return, if( tmp_ty_ptr ) { - return monomorph_outer(fcn.m_code.m_mir->named_variables[e]); + TODO(Span(), "Get return type for MIR type enumeration"); } ), - (Temporary, + (Argument, if( tmp_ty_ptr ) { - return monomorph_outer(fcn.m_code.m_mir->temporaries[e.idx]); + return monomorph_outer(fcn.m_args[e.idx].second); } ), - (Argument, + (Local, if( tmp_ty_ptr ) { - return monomorph_outer(fcn.m_args[e.idx].second); + return monomorph_outer(fcn.m_code.m_mir->locals[e]); } ), (Static, @@ -682,11 +680,6 @@ void Trans_Enumerate_Types(EnumState& state) ) } ), - (Return, - if( tmp_ty_ptr ) { - TODO(Span(), "Get return type for MIR type enumeration"); - } - ), (Field, const auto& ity = visit_lvalue(tv,pp,fcn, *e.val, tmp_ty_ptr); if( tmp_ty_ptr ) @@ -942,6 +935,9 @@ void Trans_Enumerate_Types(EnumState& state) (Switch, H::visit_lvalue(tv,pp,fcn, te.val); ), + (SwitchValue, + H::visit_lvalue(tv,pp,fcn, te.val); + ), (Call, if( te.fcn.is_Value() ) H::visit_lvalue(tv,pp,fcn, te.fcn.as_Value()); @@ -1344,17 +1340,15 @@ void Trans_Enumerate_FillFrom_Path(EnumState& state, const ::HIR::Path& path, co void Trans_Enumerate_FillFrom_MIR_LValue(EnumState& state, const ::MIR::LValue& lv, const Trans_Params& pp) { TU_MATCHA( (lv), (e), - (Variable, - ), - (Temporary, + (Return, ), (Argument, ), + (Local, + ), (Static, Trans_Enumerate_FillFrom_Path(state, e, pp); ), - (Return, - ), (Field, Trans_Enumerate_FillFrom_MIR_LValue(state, *e.val, pp); ), @@ -1485,6 +1479,9 @@ void Trans_Enumerate_FillFrom_MIR(EnumState& state, const ::MIR::Function& code, (Switch, Trans_Enumerate_FillFrom_MIR_LValue(state, e.val, pp); ), + (SwitchValue, + Trans_Enumerate_FillFrom_MIR_LValue(state, e.val, pp); + ), (Call, Trans_Enumerate_FillFrom_MIR_LValue(state, e.ret_val, pp); TU_MATCHA( (e.fcn), (e2), diff --git a/src/trans/monomorphise.cpp b/src/trans/monomorphise.cpp index 3ac2ee09..b752a5bc 100644 --- a/src/trans/monomorphise.cpp +++ b/src/trans/monomorphise.cpp @@ -13,10 +13,9 @@ namespace { ::MIR::LValue monomorph_LValue(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::LValue& tpl) { TU_MATCHA( (tpl), (e), - (Variable, return e; ), - (Temporary, return e; ), - (Argument, return e; ), (Return, return e; ), + (Argument, return e; ), + (Local, return e; ), (Static, return params.monomorph(resolve, e); ), @@ -120,17 +119,11 @@ namespace { ::MIR::Function output; // 1. Monomorphise locals and temporaries - output.named_variables.reserve( tpl->named_variables.size() ); - for(const auto& var : tpl->named_variables) - { - DEBUG("- var" << output.named_variables.size()); - output.named_variables.push_back( params.monomorph(resolve, var) ); - } - output.temporaries.reserve( tpl->temporaries.size() ); - for(const auto& ty : tpl->temporaries) + output.locals.reserve( tpl->locals.size() ); + for(const auto& var : tpl->locals) { - DEBUG("- tmp" << output.temporaries.size()); - output.temporaries.push_back( params.monomorph(resolve, ty) ); + DEBUG("- _" << output.locals.size()); + output.locals.push_back( params.monomorph(resolve, var) ); } output.drop_flags = tpl->drop_flags; @@ -302,6 +295,14 @@ namespace { e.targets }); ), + (SwitchValue, + terminator = ::MIR::Terminator::make_SwitchValue({ + monomorph_LValue(resolve, params, e.val), + e.def_target, + e.targets, + e.values.clone() + }); + ), (Call, struct H { static ::MIR::CallTarget monomorph_calltarget(const ::StaticTraitResolve& resolve, const Trans_Params& params, const ::MIR::CallTarget& ct) { diff --git a/src/trans/target.cpp b/src/trans/target.cpp index a85ab12a..d9b3486e 100644 --- a/src/trans/target.cpp +++ b/src/trans/target.cpp @@ -7,10 +7,53 @@ */ #include "target.hpp" #include <algorithm> +#include "../expand/cfg.hpp" // TODO: Replace with target selection #define POINTER_SIZE_BYTES 8 +TargetSpec g_target = { + "unix", + "linux", + "gnu", + CodegenMode::Gnu11, + TargetArch { + "x86_64", + 64, false, + { true, false, true, true, true } + } + }; + +void Target_SetCfg() +{ + if(g_target.m_family == "unix") { + Cfg_SetFlag("unix"); + } + else if( g_target.m_family == "windows") { + Cfg_SetFlag("windows"); + } + Cfg_SetValue("target_family", g_target.m_family); + + if( g_target.m_os_name == "linux" ) + { + Cfg_SetFlag("linux"); + } + Cfg_SetValue("target_env", g_target.m_env_name); + + Cfg_SetValue("target_os", g_target.m_os_name); + Cfg_SetValue("target_pointer_width", FMT(g_target.m_arch.m_pointer_bits)); + Cfg_SetValue("target_endian", g_target.m_arch.m_big_endian ? "big" : "little"); + Cfg_SetValue("target_arch", g_target.m_arch.m_name); + Cfg_SetValueCb("target_has_atomic", [&](const ::std::string& s) { + if(s == "8") return g_target.m_arch.m_atomics.u8; // Has an atomic byte + if(s == "ptr") return g_target.m_arch.m_atomics.ptr; // Has an atomic pointer-sized value + return false; + }); + Cfg_SetValueCb("target_feature", [](const ::std::string& s) { + return false; + }); +} + bool Target_GetSizeAndAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_size, size_t& out_align) { TU_MATCHA( (ty.m_data), (te), @@ -55,8 +98,8 @@ bool Target_GetSizeAndAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& return true; case ::HIR::CoreType::Usize: case ::HIR::CoreType::Isize: - out_size = POINTER_SIZE_BYTES; - out_align = POINTER_SIZE_BYTES; + out_size = g_target.m_arch.m_pointer_bits / 8; + out_align = g_target.m_arch.m_pointer_bits / 8; return true; case ::HIR::CoreType::F32: out_size = 4; @@ -104,6 +147,11 @@ bool Target_GetSizeAndAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& size_t size, align; if( !Target_GetSizeAndAlignOf(sp, t, size,align) ) return false; + if( out_size % align != 0 ) + { + out_size += align; + out_size %= align; + } out_size += size; out_align = ::std::max(out_align, align); } @@ -116,6 +164,9 @@ bool Target_GetSizeAndAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& ), (Function, // Pointer size + out_size = g_target.m_arch.m_pointer_bits / 8; + out_align = g_target.m_arch.m_pointer_bits / 8; + return true; ), (Closure, // TODO. diff --git a/src/trans/target.hpp b/src/trans/target.hpp index 1c081b54..80ba0bf2 100644 --- a/src/trans/target.hpp +++ b/src/trans/target.hpp @@ -10,6 +10,38 @@ #include <cstddef> #include <hir/type.hpp> +enum class CodegenMode +{ + Gnu11, + Msvc, +}; + +struct TargetArch +{ + ::std::string m_name; + unsigned m_pointer_bits; + bool m_big_endian; + + struct { + bool u8; + bool u16; + bool u32; + bool u64; + bool ptr; + } m_atomics; +}; +struct TargetSpec +{ + ::std::string m_family; + ::std::string m_os_name; + ::std::string m_env_name; + + CodegenMode m_codegen_mode; + TargetArch m_arch; +}; + + +extern void Target_SetCfg(); extern bool Target_GetSizeOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_size); extern bool Target_GetAlignOf(const Span& sp, const ::HIR::TypeRef& ty, size_t& out_align); |