summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Hodge <tpg@ucc.asn.au>2017-06-22 11:43:41 +0800
committerJohn Hodge <tpg@ucc.asn.au>2017-06-22 11:43:41 +0800
commitc7f4248191dce493cd43fecd808cf15015271408 (patch)
tree8f457818bc3fdf1fcc051736029c1ba04dac6f0e
parent60f74bdb41ae552caf734a799713591a6eea9fa1 (diff)
downloadmrust-c7f4248191dce493cd43fecd808cf15015271408.tar.gz
Codegen C - First pass at structured C output (generated, but not compiled)
-rw-r--r--Makefile3
-rw-r--r--src/trans/codegen_c.cpp341
-rw-r--r--src/trans/codegen_c.hpp47
-rw-r--r--src/trans/codegen_c_structured.cpp290
4 files changed, 586 insertions, 95 deletions
diff --git a/Makefile b/Makefile
index 778c59a8..e63b0d0d 100644
--- a/Makefile
+++ b/Makefile
@@ -110,7 +110,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
diff --git a/src/trans/codegen_c.cpp b/src/trans/codegen_c.cpp
index 7e0405d1..2612f577 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 {
@@ -1383,6 +1384,17 @@ namespace {
for(unsigned int i = 0; i < code->drop_flags.size(); i ++) {
m_of << "\tbool df" << i << " = " << code->drop_flags[i] << ";\n";
}
+
+ {
+ 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);
@@ -1458,76 +1470,7 @@ namespace {
}
),
(Call,
- m_of << "\t";
- if( e.fcn.is_Intrinsic() )
- {
- 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 ;
- }
-
- 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,
- ),
- (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_lvalue(e.ret_val); m_of << " = ";
- }
- }
- m_of << Trans_Mangle(e2);
- ),
- (Intrinsic,
- MIR_BUG(mir_res, "Intrinsic not expected, should be handled above");
- )
- )
- 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";
+ emit_term_call(mir_res, e, 1);
m_of << "\tgoto bb" << e.ret_block << ";\n";
)
)
@@ -1537,17 +1480,114 @@ namespace {
m_of.flush();
m_mir_res = nullptr;
}
- void emit_statement(const ::MIR::TypeResolve& mir_res, const ::MIR::Statement& stmt)
+
+ 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");
+ )
+ )
+ }
+ }
+ ),
+ (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 << "// " << stmt << "\n";
+ m_of << indent << "// " << stmt << "\n";
break;
case ::MIR::Statement::TAG_SetDropFlag: {
const auto& e = stmt.as_SetDropFlag();
- m_of << "\tdf" << e.idx << " = ";
+ m_of << indent << "df" << e.idx << " = ";
if( e.other == ~0u )
m_of << e.new_val;
else
@@ -1560,7 +1600,7 @@ namespace {
const auto& ty = mir_res.get_lvalue_type(tmp, e.slot);
if( e.flag_idx != ~0u )
- m_of << "\tif( df" << e.flag_idx << " ) {\n";
+ m_of << indent << "if( df" << e.flag_idx << " ) {\n";
switch( e.kind )
{
@@ -1571,7 +1611,7 @@ namespace {
// 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";
+ m_of << indent << Trans_Mangle(box_free) << "("; emit_lvalue(e.slot); m_of << "._0._0._0);\n";
}
else
{
@@ -1583,7 +1623,7 @@ namespace {
break;
}
if( e.flag_idx != ~0u )
- m_of << "\t}\n";
+ m_of << indent << "}\n";
break; }
case ::MIR::Statement::TAG_Asm: {
const auto& e = stmt.as_Asm();
@@ -1604,7 +1644,7 @@ namespace {
bool is_volatile = H::has_flag(e.flags, "volatile");
bool is_intel = H::has_flag(e.flags, "intel");
- m_of << "\t__asm__ ";
+ m_of << indent << "__asm__ ";
if(is_volatile) m_of << "__volatile__";
// TODO: Convert format string?
// TODO: Use a C-specific escaper here.
@@ -1642,7 +1682,7 @@ namespace {
case ::MIR::Statement::TAG_Assign: {
const auto& e = stmt.as_Assign();
DEBUG("- " << e.dst << " = " << e.src);
- m_of << "\t";
+ m_of << indent;
TU_MATCHA( (e.src), (ve),
(Use,
::HIR::TypeRef tmp;
@@ -1667,17 +1707,17 @@ namespace {
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[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\t";
- emit_lvalue(e.dst); m_of << ".DATA[1] = "; emit_param(ve.val); m_of << ";\n\t";
+ 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 << "\t\t"; emit_lvalue(e.dst); m_of << ".DATA[i] = "; emit_param(ve.val);
+ m_of << indent << "\t"; emit_lvalue(e.dst); m_of << ".DATA[i] = "; emit_param(ve.val);
}
),
(Borrow,
@@ -1704,7 +1744,7 @@ namespace {
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 << ".META = "; emit_lvalue(base_ptr); m_of << ".META;\n" << indent;
emit_lvalue(e.dst); m_of << ".PTR = &"; emit_lvalue(ve.val);
special = true;
}
@@ -1879,17 +1919,12 @@ namespace {
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);
+ 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\t";
+ if( j != 0 ) m_of << ";\n" << indent;
emit_lvalue(e.dst);
m_of << "._" << j << " = ";
emit_param(ve.vals[j]);
@@ -1897,7 +1932,7 @@ namespace {
),
(Array,
for(unsigned int j = 0; j < ve.vals.size(); j ++) {
- if( j != 0 ) m_of << ";\n\t";
+ if( j != 0 ) m_of << ";\n" << indent;
emit_lvalue(e.dst); m_of << ".DATA[" << j << "] = ";
emit_param(ve.vals[j]);
}
@@ -1963,7 +1998,7 @@ namespace {
m_of << ".TAG = " << ve.variant_idx;
}
if(ve.vals.size() > 0)
- m_of << ";\n\t";
+ m_of << ";\n" << indent;
}
for(unsigned int j = 0; j < ve.vals.size(); j ++)
@@ -1973,7 +2008,7 @@ namespace {
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";
+ if( j != 0 ) m_of << ";\n" << indent;
emit_lvalue(e.dst);
if(ve.variant_idx != ~0u)
m_of << ".DATA.var_" << ve.variant_idx;
@@ -1988,6 +2023,124 @@ namespace {
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,
+ ),
+ (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_lvalue(e.ret_val); m_of << " = ";
+ }
+ }
+ 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 << "(";
+ 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";
+ }
private:
const ::HIR::TypeRef& monomorphise_fcn_return(::HIR::TypeRef& tmp, const ::HIR::Function& item, const Trans_Params& params)
{
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..e89d7589
--- /dev/null
+++ b/src/trans/codegen_c_structured.cpp
@@ -0,0 +1,290 @@
+/*
+ * 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;
+ ),
+ (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;
+ ),
+ (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;
+}
+
+