summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mir/mir.cpp33
-rw-r--r--src/mir/mir.hpp4
-rw-r--r--src/mir/optimise.cpp70
3 files changed, 107 insertions, 0 deletions
diff --git a/src/mir/mir.cpp b/src/mir/mir.cpp
index 18557304..fb34357e 100644
--- a/src/mir/mir.cpp
+++ b/src/mir/mir.cpp
@@ -500,6 +500,39 @@ namespace MIR {
)
return os;
}
+ bool operator==(const Statement& a, const Statement& b) {
+ if( a.tag() != b.tag() )
+ return false;
+
+ TU_MATCHA( (a,b), (ae,be),
+ (Assign,
+ return ae.dst == be.dst && ae.src == be.src;
+ ),
+ (Asm,
+ return ae.outputs == be.outputs
+ && ae.inputs == be.inputs
+ && ae.clobbers == be.clobbers
+ && ae.flags == be.flags
+ ;
+ ),
+ (SetDropFlag,
+ return ae.idx == be.idx
+ && ae.other == be.other
+ && ae.new_val == be.new_val
+ ;
+ ),
+ (Drop,
+ return ae.slot == be.slot
+ && ae.kind == be.kind
+ && ae.flag_idx == be.flag_idx
+ ;
+ ),
+ (ScopeEnd,
+ return ae.slots == be.slots;
+ )
+ )
+ throw "";
+ }
}
::MIR::LValue MIR::LValue::clone() const
diff --git a/src/mir/mir.hpp b/src/mir/mir.hpp
index 3ce115b8..2a623d3c 100644
--- a/src/mir/mir.hpp
+++ b/src/mir/mir.hpp
@@ -285,6 +285,10 @@ TAGGED_UNION(Statement, Assign,
})
);
extern ::std::ostream& operator<<(::std::ostream& os, const Statement& x);
+extern bool operator==(const Statement& a, const Statement& b);
+static inline bool operator!=(const Statement& a, const Statement& b) {
+ return !(a == b);
+}
struct BasicBlock
{
diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp
index 9935454e..4c2da555 100644
--- a/src/mir/optimise.cpp
+++ b/src/mir/optimise.cpp
@@ -498,6 +498,7 @@ bool MIR_Optimise_PropagateSingleAssignments(::MIR::TypeResolve& state, ::MIR::F
bool MIR_Optimise_PropagateKnownValues(::MIR::TypeResolve& state, ::MIR::Function& fcn);
bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn); // Eliminate useless temporaries
bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& fcn);
+bool MIR_Optimise_CommonStatements(::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);
bool MIR_Optimise_DeadDropFlags(::MIR::TypeResolve& state, ::MIR::Function& fcn);
@@ -600,6 +601,9 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path
}
#endif
+ // >> Move common statements (assignments) across gotos.
+ change_happened |= MIR_Optimise_CommonStatements(state, fcn);
+
// >> Combine Duplicate Blocks
change_happened |= MIR_Optimise_UnifyBlocks(state, fcn);
// >> Remove assignments of unsed drop flags
@@ -1431,6 +1435,72 @@ bool MIR_Optimise_DeTemporary(::MIR::TypeResolve& state, ::MIR::Function& fcn)
return changed;
}
+
+// --------------------------------------------------------------------
+// Detect common statements between all source arms of a block
+// --------------------------------------------------------------------
+bool MIR_Optimise_CommonStatements(::MIR::TypeResolve& state, ::MIR::Function& fcn)
+{
+ bool changed = false;
+ TRACE_FUNCTION_FR("", changed);
+
+ for(size_t bb_idx = 0; bb_idx < fcn.blocks.size(); bb_idx ++)
+ {
+ state.set_cur_stmt(bb_idx, 0);
+
+ bool skip = false;
+ ::std::vector<size_t> sources;
+ // Find source blocks
+ for(size_t bb2_idx = 0; bb2_idx < fcn.blocks.size(); bb2_idx ++)
+ {
+ const auto& blk = fcn.blocks[bb2_idx];
+ // TODO: Handle non-Goto branches? (e.g. calls)
+ if( blk.terminator.is_Goto() && blk.terminator.as_Goto() == bb_idx )
+ {
+ if( blk.statements.empty() )
+ {
+ DEBUG(state << " BB" << bb2_idx << " empty");
+ skip = true;
+ break ;
+ }
+ if( !sources.empty() )
+ {
+ if( blk.statements.back() != fcn.blocks[sources.front()].statements.back() )
+ {
+ DEBUG(state << " BB" << bb2_idx << " doesn't end with " << fcn.blocks[sources.front()].statements.back() << " instead " << blk.statements.back());
+ skip = true;
+ break;
+ }
+ }
+ sources.push_back(bb2_idx);
+ }
+ else
+ {
+ visit_terminator_target(blk.terminator, [&](const auto& dst_idx) {
+ if( dst_idx == bb_idx ) {
+ DEBUG(state << " BB" << bb2_idx << " doesn't end Goto - instead " << blk.terminator);
+ skip = true;
+ }
+ });
+ }
+ }
+
+ if( !skip && sources.size() > 1 )
+ {
+ // Found a common assignment, add to the start and remove from sources.
+ auto stmt = ::std::move(fcn.blocks[sources.front()].statements.back());
+ MIR_DEBUG(state, "Move common final statements from " << sources << " to " << bb_idx << " - " << stmt);
+ for(auto idx : sources)
+ {
+ fcn.blocks[idx].statements.pop_back();
+ }
+ fcn.blocks[bb_idx].statements.insert(fcn.blocks[bb_idx].statements.begin(), ::std::move(stmt));
+ }
+ }
+ return changed;
+}
+
+
// --------------------------------------------------------------------
// If two temporaries don't overlap in lifetime (blocks in which they're valid), unify the two
// --------------------------------------------------------------------