summaryrefslogtreecommitdiff
path: root/src/mir/optimise.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mir/optimise.cpp')
-rw-r--r--src/mir/optimise.cpp663
1 files changed, 188 insertions, 475 deletions
diff --git a/src/mir/optimise.cpp b/src/mir/optimise.cpp
index 2e823821..81574afd 100644
--- a/src/mir/optimise.cpp
+++ b/src/mir/optimise.cpp
@@ -17,6 +17,13 @@
#include <iomanip>
#include <trans/target.hpp>
+#define DUMP_BEFORE_ALL 0
+#define DUMP_BEFORE_CONSTPROPAGATE 0
+#define CHECK_AFTER_PASS 0
+
+#define DUMP_AFTER_DONE 0
+#define CHECK_AFTER_DONE 1
+
namespace {
::MIR::BasicBlockId get_new_target(const ::MIR::TypeResolve& state, ::MIR::BasicBlockId bb)
{
@@ -185,6 +192,8 @@ namespace {
(Drop,
// Well, it mutates...
rv |= visit_mir_lvalue_mut(e.slot, ValUsage::Write, cb);
+ ),
+ (ScopeEnd,
)
)
return rv;
@@ -436,26 +445,32 @@ void MIR_Optimise(const StaticTraitResolve& resolve, const ::HIR::ItemPath& path
// >> Combine Duplicate Blocks
change_happened |= MIR_Optimise_UnifyBlocks(state, fcn);
- #if 0
if( change_happened )
{
- //MIR_Dump_Fcn(::std::cout, fcn);
+ #if DUMP_AFTER_PASS
+ if( debug_enabled() ) {
+ MIR_Dump_Fcn(::std::cout, fcn);
+ }
+ #endif
+ #if CHECK_AFTER_PASS
MIR_Validate(resolve, path, fcn, args, ret_type);
+ #endif
}
- #endif
MIR_Optimise_GarbageCollect_Partial(state, fcn);
pass_num += 1;
} while( change_happened );
- #if 1
+ #if DUMP_AFTER_DONE
if( debug_enabled() ) {
MIR_Dump_Fcn(::std::cout, fcn);
}
#endif
+ #if CHECK_AFTER_DONE
// DEFENCE: Run validation _before_ GC (so validation errors refer to the pre-gc numbers)
MIR_Validate(resolve, path, fcn, args, ret_type);
+ #endif
// GC pass on blocks and variables
// - Find unused blocks, then delete and rewrite all references.
MIR_Optimise_GarbageCollect(state, fcn);
@@ -469,6 +484,31 @@ bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn)
// >> Replace targets that point to a block that is just a goto
for(auto& block : fcn.blocks)
{
+ // Unify sequential ScopeEnd statements
+ if( block.statements.size() > 1 )
+ {
+ for(auto it = block.statements.begin() + 1; it != block.statements.end(); )
+ {
+ if( (it-1)->is_ScopeEnd() && it->is_ScopeEnd() )
+ {
+ 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());
+ it = block.statements.erase(it);
+ }
+ else
+ {
+ ++ it;
+ }
+ }
+ }
+
TU_MATCHA( (block.terminator), (e),
(Incomplete,
),
@@ -589,7 +629,7 @@ bool MIR_Optimise_BlockSimplify(::MIR::TypeResolve& state, ::MIR::Function& fcn)
bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn)
{
TRACE_FUNCTION;
-
+
struct H
{
static bool can_inline(const ::HIR::Path& path, const ::MIR::Function& fcn)
@@ -686,22 +726,21 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn)
return rv;
}
- ::MIR::BasicBlock clone_bb(const ::MIR::BasicBlock& src) const
+ ::MIR::BasicBlock clone_bb(const ::MIR::BasicBlock& src, unsigned src_idx, unsigned new_idx) const
{
::MIR::BasicBlock rv;
rv.statements.reserve( src.statements.size() );
for(const auto& stmt : src.statements)
{
+ DEBUG("BB" << src_idx << "->BB" << new_idx << "/" << rv.statements.size() << ": " << stmt);
TU_MATCHA( (stmt), (se),
(Assign,
- DEBUG(se.dst << " = " << se.src);
rv.statements.push_back( ::MIR::Statement::make_Assign({
this->clone_lval(se.dst),
this->clone_rval(se.src)
}) );
),
(Asm,
- DEBUG("asm!");
rv.statements.push_back( ::MIR::Statement::make_Asm({
se.tpl,
this->clone_name_lval_vec(se.outputs),
@@ -711,7 +750,6 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn)
}) );
),
(SetDropFlag,
- DEBUG("df" << se.idx << " = ");
rv.statements.push_back( ::MIR::Statement::make_SetDropFlag({
this->df_base + se.idx,
se.new_val,
@@ -719,17 +757,27 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn)
}) );
),
(Drop,
- DEBUG("drop " << se.slot);
rv.statements.push_back( ::MIR::Statement::make_Drop({
se.kind,
this->clone_lval(se.slot),
se.flag_idx == ~0u ? ~0u : this->df_base + se.flag_idx
}) );
+ ),
+ (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);
+ rv.statements.push_back(::MIR::Statement( mv$(new_se) ));
)
)
}
- DEBUG(src.terminator);
+ DEBUG("BB" << src_idx << "->BB" << new_idx << "/" << rv.statements.size() << ": " << src.terminator);
rv.terminator = this->clone_term(src.terminator);
+ DEBUG("-> " << rv.terminator);
return rv;
}
::MIR::Terminator clone_term(const ::MIR::Terminator& src) const
@@ -981,7 +1029,7 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn)
new_blocks.reserve( called_mir->blocks.size() );
for(const auto& bb : called_mir->blocks)
{
- new_blocks.push_back( cloner.clone_bb(bb) );
+ 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)
@@ -1006,6 +1054,7 @@ bool MIR_Optimise_Inlining(::MIR::TypeResolve& state, ::MIR::Function& fcn)
return inline_happened;
}
+
// --------------------------------------------------------------------
// If two temporaries don't overlap in lifetime (blocks in which they're valid), unify the two
// --------------------------------------------------------------------
@@ -1036,444 +1085,9 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f
return false;
}
- class VarLifetime
- {
- ::std::vector<bool> statements;
-
- public:
- VarLifetime(::std::vector<bool> stmts):
- statements( mv$(stmts) )
- {}
-
- // true if this value is used at any point
- bool is_used() const {
- for(auto v : statements)
- if( v )
- return true;
- return false;
- }
- bool overlaps(const VarLifetime& x) const {
- assert(statements.size() == x.statements.size());
- for(unsigned int i = 0; i < statements.size(); i ++)
- {
- if( statements[i] && x.statements[i] )
- return true;
- }
- return false;
- }
- void unify(const VarLifetime& x) {
- assert(statements.size() == x.statements.size());
- for(unsigned int i = 0; i < statements.size(); i ++)
- {
- if( x.statements[i] )
- statements[i] = true;
- }
- }
- };
- //::std::vector<VarLifetime> var_lifetimes;
- ::std::vector<VarLifetime> tmp_lifetimes;
-
-
- // New algorithm notes:
- // ---
- // The lifetime of a value starts when it is written, and ends the last time it is read
- // - When a variable is read, end any existing lifetime and start a new one.
- // - When the value is read, update the end of its lifetime.
- // ---
- // A lifetime is a range in the call graph (with a start and end, including list of blocks)
- // - Representation: Bitmap with a bit per statement.
- // - Record the current block path in general state, along with known active lifetimes
-
- // TODO: Move all this code to a helper to other passes can use it.
- {
- // Scan through all possible paths in the graph (with loopback detection using a memory of the path)
- // - If a loop is detected, determine if there were changes to the lifetime set during that pass
- // > Changes are noticed by recording in the state structure when it triggers a change in the lifetime
- // map.
- struct Position
- {
- size_t path_index = 0; // index into the block path.
- unsigned int stmt_idx = 0;
-
- bool operator==(const Position& x) const {
- return path_index == x.path_index && stmt_idx == x.stmt_idx;
- }
- };
- struct ProtoLifetime
- {
- Position start;
- Position end;
-
- bool is_empty() const {
- return start == end;
- }
- };
- static unsigned NEXT_INDEX = 0;
- struct State
- {
- unsigned int index = 0;
- ::std::vector<unsigned int> block_path;
- ::std::vector<unsigned int> block_change_idx;
- unsigned int cur_change_idx = 0;
-
- // if read, update. If set, save and update
- ::std::vector<ProtoLifetime> tmp_ends;
- //::std::vector<Position> var_ends;
-
- State clone() const {
- auto rv = *this;
- rv.index = ++NEXT_INDEX;
- return rv;
- }
- };
- NEXT_INDEX = 0;
-
- struct ValueLifetime
- {
- ::std::vector<bool> stmt_bitmap;
- ValueLifetime(size_t stmt_count):
- stmt_bitmap(stmt_count)
- {}
- };
-
- size_t statement_count = 0;
- ::std::vector<size_t> block_offsets;
- block_offsets.reserve( fcn.blocks.size() );
- for(const auto& bb : fcn.blocks)
- {
- block_offsets.push_back(statement_count);
- statement_count += bb.statements.size() + 1; // +1 for the terminator
- }
-
- ::std::vector<ValueLifetime> temporary_lifetimes( fcn.temporaries.size(), ValueLifetime(statement_count) );
-
- struct BlockSeenLifetimes {
- const ::std::vector<size_t>& block_offsets;
- ::std::vector< ::std::vector<unsigned int> > tmp;
-
- BlockSeenLifetimes(const ::std::vector<size_t>& block_offsets, const ::MIR::Function& fcn):
- block_offsets( block_offsets ),
- tmp( fcn.temporaries.size() )
- {}
-
- bool has_state() const
- {
- return ::std::any_of(tmp.begin(), tmp.end(), [&](const auto& x){ return !x.empty(); });
- }
-
- bool try_merge(const State& val_state) const
- {
- for(size_t i = 0; i < val_state.tmp_ends.size(); i++)
- {
- const auto& lft = val_state.tmp_ends[i];
- const auto& seen = this->tmp[i];
- if(lft.is_empty()) continue ;
- auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx;
-
- auto it = ::std::find(seen.begin(), seen.end(), end_idx);
- if( it == seen.end() )
- {
- return true;
- }
- }
- return false;
- }
- bool merge(const State& val_state)
- {
- bool rv = false;
- for(size_t i = 0; i < val_state.tmp_ends.size(); i++)
- {
- const auto& lft = val_state.tmp_ends[i];
- auto& seen = this->tmp[i];
- if(lft.is_empty()) continue ;
- auto end_idx = block_offsets.at( val_state.block_path.at(lft.end.path_index) ) + lft.end.stmt_idx;
-
- auto it = ::std::find(seen.begin(), seen.end(), end_idx);
- if( it == seen.end() )
- {
- rv = true;
- seen.push_back( end_idx );
- }
- }
- return rv;
- }
- };
- ::std::vector<BlockSeenLifetimes> block_seen_lifetimes( fcn.blocks.size(), BlockSeenLifetimes(block_offsets, fcn) );
-
- State init_state;
- init_state.tmp_ends.resize( fcn.temporaries.size(), ProtoLifetime() );
-
- ::std::vector<::std::pair<unsigned int, State>> todo_queue;
- todo_queue.push_back(::std::make_pair( 0, mv$(init_state) ));
-
- while(!todo_queue.empty())
- {
- auto bb_idx = todo_queue.back().first;
- auto val_state = mv$(todo_queue.back().second);
- todo_queue.pop_back();
- state.set_cur_stmt(bb_idx, 0);
-
- // Fill alive time in the bitmap
- // TODO: Maybe also store the range (as a sequence of {block,start,end})
- auto add_lifetime_s = [&](State& val_state, const ::MIR::LValue& lv, const Position& start, const Position& end) {
- assert(start.path_index <= end.path_index);
- assert(start.path_index < end.path_index || start.stmt_idx <= end.stmt_idx);
- if(start.path_index == end.path_index && start.stmt_idx == end.stmt_idx)
- return;
- //DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ")");
- ValueLifetime* lft;
- if(const auto* e = lv.opt_Temporary())
- {
- lft = &temporary_lifetimes[e->idx];
- }
- else
- {
- MIR_TODO(state, "[add_lifetime] " << lv);
- return;
- }
-
- // Fill lifetime map for this temporary in the indicated range
- bool did_set = false;
- unsigned int j = start.stmt_idx;
- unsigned int i = start.path_index;
- while( i <= end.path_index )
- {
- auto bb_idx = val_state.block_path.at(i);
- const auto& bb = fcn.blocks[bb_idx];
- MIR_ASSERT(state, j <= bb.statements.size(), "");
- MIR_ASSERT(state, bb_idx < block_offsets.size(), "");
-
- auto block_base = block_offsets.at(bb_idx);
- auto idx = block_base + j;
- if( !lft->stmt_bitmap.at(idx) )
- {
- lft->stmt_bitmap[idx] = true;
- did_set = true;
- }
-
- if( i == end.path_index && j == end.stmt_idx )
- break;
-
- // If the current index is the terminator (one after the size)
- if(j == bb.statements.size())
- {
- j = 0;
- i++;
- }
- else
- {
- j ++;
- }
- }
-
- // - If the above set a new bit, increment `val_state.cur_change_idx`
- if( did_set )
- {
- DEBUG("[add_lifetime] " << lv << " (" << start.path_index << "," << start.stmt_idx << ") -- (" << end.path_index << "," << end.stmt_idx << ") - New information");
- val_state.cur_change_idx += 1;
- }
- };
- auto add_lifetime = [&](const ::MIR::LValue& lv, const Position& start, const Position& end) {
- add_lifetime_s(val_state, lv, start, end);
- };
-
- auto apply_state = [&](State& state) {
- // Apply all changes in this state, just in case there was new information
- for(unsigned i = 0; i < fcn.temporaries.size(); i++)
- add_lifetime_s( state, ::MIR::LValue::make_Temporary({i}), state.tmp_ends[i].start, state.tmp_ends[i].end );
- };
- auto add_to_visit = [&](unsigned int new_bb_idx, State new_state) {
- auto& bb_memory_ent = block_seen_lifetimes[new_bb_idx];
- /*if( !bb_memory_ent.has_state() )
- {
- // No recorded state, needs to be visited
- DEBUG(state << " " << new_state.index << " -> bb" << new_bb_idx << " (no existing state)");
- }
- else*/ if( bb_memory_ent.try_merge(new_state) )
- {
- // This state has new information, needs to be visited
- DEBUG(state << " " << new_state.index << " -> bb" << new_bb_idx << " (new info)");
- }
- else
- {
- // Skip
- DEBUG(state << " " << new_state.index << " No new state before push (to bb" << new_bb_idx << "), applying");
- apply_state(new_state);
- return ;
- }
- todo_queue.push_back(::std::make_pair( new_bb_idx, mv$(new_state) ));
- };
-
- // Compare this state to a composite list of lifetimes seen in this block
- // - Just compares the end of each proto lifetime
- {
- auto& bb_memory_ent = block_seen_lifetimes[bb_idx];
- bool has_new = bb_memory_ent.merge(val_state);
-
- if( !has_new && bb_memory_ent.has_state() )
- {
- DEBUG(state << " " << val_state.index << " No new entry state");
- apply_state(val_state);
-
- continue ;
- }
- }
-
- // Check if this state has visited this block before, and if anything changed since last time
- {
- auto it = ::std::find(val_state.block_path.rbegin(), val_state.block_path.rend(), bb_idx);
- if( it != val_state.block_path.rend() )
- {
- auto idx = &*it - &val_state.block_path.front();
- if( val_state.block_change_idx[idx] == val_state.cur_change_idx )
- {
- DEBUG(state << " " << val_state.index << " Loop and no change");
- continue ;
- }
- else
- {
- assert( val_state.block_change_idx[idx] < val_state.cur_change_idx );
- DEBUG(state << " " << val_state.index << " --- Loop, " << val_state.cur_change_idx - val_state.block_change_idx[idx] << " changes");
- }
- }
- else
- {
- DEBUG(state << " " << val_state.index << " ---");
- }
- val_state.block_path.push_back(bb_idx);
- val_state.block_change_idx.push_back( val_state.cur_change_idx );
- }
-
- Position cur_pos;
- cur_pos.path_index = val_state.block_path.size() - 1;
- cur_pos.stmt_idx = 0;
- auto lvalue_read = [&](const ::MIR::LValue& lv) {
- if(const auto* e = lv.opt_Temporary())
- {
- // Update the last read location
- val_state.tmp_ends.at(e->idx).end = cur_pos;
- }
- };
- auto lvalue_set = [&](const ::MIR::LValue& lv) {
- if(const auto* e = lv.opt_Temporary())
- {
- // End whatever value was originally there, and insert this new one
- val_state.tmp_ends.at(e->idx).end = cur_pos;
- add_lifetime(lv, val_state.tmp_ends.at(e->idx).start, val_state.tmp_ends.at(e->idx).end);
- val_state.tmp_ends.at(e->idx).start = cur_pos;
- }
- };
-
- // Run statements
- for(const auto& stmt : fcn.blocks[bb_idx].statements)
- {
- auto stmt_idx = &stmt - &fcn.blocks[bb_idx].statements.front();
- cur_pos.stmt_idx = stmt_idx;
- state.set_cur_stmt(bb_idx, stmt_idx);
- DEBUG(state << " " << stmt);
-
- if( const auto* e = stmt.opt_Drop() )
- {
- visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{
- if(vu == ValUsage::Read)
- lvalue_read(lv);
- return false;
- });
- lvalue_read(e->slot);
- lvalue_set(e->slot);
- }
- else
- {
- visit_mir_lvalues(stmt, [&](const auto& lv, ValUsage vu)->bool{
- if(vu == ValUsage::Read)
- lvalue_read(lv);
- if(vu == ValUsage::Write)
- lvalue_set(lv);
- return false;
- });
- }
- }
- cur_pos.stmt_idx = fcn.blocks[bb_idx].statements.size();
-
- state.set_cur_stmt_term(bb_idx);
- DEBUG(state << "TERM " << fcn.blocks[bb_idx].terminator);
- TU_MATCH(::MIR::Terminator, (fcn.blocks[bb_idx].terminator), (e),
- (Incomplete,
- // Should be impossible here.
- ),
- (Return,
- // End all active lifetimes at their previous location.
- apply_state(val_state);
- ),
- (Diverge,
- apply_state(val_state);
- ),
- (Goto,
- add_to_visit(e, mv$(val_state));
- ),
- (Panic,
- // What should be done here?
- ),
- (If,
- lvalue_read(e.cond);
-
- // Push blocks
- add_to_visit(e.bb0, val_state.clone());
- add_to_visit(e.bb1, mv$(val_state));
- ),
- (Switch,
- lvalue_read(e.val);
- ::std::set<unsigned int> tgts;
- for(const auto& tgt : e.targets)
- tgts.insert(tgt);
-
- for(const auto& tgt : tgts)
- {
- auto vs = (tgt == *tgts.rbegin() ? mv$(val_state) : val_state.clone());
- add_to_visit(tgt, mv$(vs));
- }
- ),
- (Call,
- if( const auto* f = e.fcn.opt_Value() )
- lvalue_read(*f);
- for(const auto& arg : e.args)
- if( const auto* e = arg.opt_LValue() )
- lvalue_read(*e);
-
- // Push blocks (with return valid only in one)
- add_to_visit(e.panic_block, val_state.clone());
-
- // TODO: If the function returns !, don't follow the ret_block
- lvalue_set(e.ret_val);
- add_to_visit(e.ret_block, mv$(val_state));
- )
- )
- }
-
- // Dump out variable lifetimes.
-#if 0
- for(unsigned int i = 0; i < temporary_lifetimes.size(); i ++)
- {
- const auto& lft = temporary_lifetimes[i];
- auto name = FMT("tmp$" << i);
- while(name.size() < 3+1+3)
- name += " ";
- DEBUG(name << " : " << FMT_CB(os,
- for(unsigned int j = 0; j < lft.stmt_bitmap.size(); j++)
- {
- if(j != 0 && ::std::find(block_offsets.begin(), block_offsets.end(), j) != block_offsets.end())
- os << "|";
- os << (lft.stmt_bitmap[j] ? "X" : " ");
- }
- ));
- }
-#endif
-
- // Move lifetime bitmaps into the variable for the below code
- tmp_lifetimes.reserve( temporary_lifetimes.size() );
- for(auto& lft : temporary_lifetimes)
- tmp_lifetimes.push_back( VarLifetime(mv$(lft.stmt_bitmap)) );
- }
+ 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);
// 2. Unify variables of the same type with distinct non-overlapping lifetimes
::std::map<unsigned int, unsigned int> replacements;
@@ -1520,6 +1134,8 @@ bool MIR_Optimise_UnifyTemporaries(::MIR::TypeResolve& state, ::MIR::Function& f
}
return false;
});
+
+ // TODO: Replace in ScopeEnd too?
}
return replacement_needed;
@@ -1572,6 +1188,12 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn)
return false;
if( ae.slot != be.slot )
return false;
+ ),
+ (ScopeEnd,
+ if( ae.vars != be.vars )
+ return false;
+ if( ae.tmps == be.tmps )
+ return false;
)
)
}
@@ -1664,7 +1286,7 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn)
if( ! replacements.empty() )
{
//MIR_TODO(state, "Unify blocks - " << replacements);
- DEBUG("Unify blocks - " << replacements);
+ DEBUG("Unify blocks (old: new) - " << replacements);
auto patch_tgt = [&replacements](::MIR::BasicBlockId& tgt) {
auto it = replacements.find(tgt);
if( it != replacements.end() )
@@ -1726,6 +1348,9 @@ bool MIR_Optimise_UnifyBlocks(::MIR::TypeResolve& state, ::MIR::Function& fcn)
// --------------------------------------------------------------------
bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
{
+#if DUMP_BEFORE_ALL || DUMP_BEFORE_CONSTPROPAGATE
+ if( debug_enabled() ) MIR_Dump_Fcn(::std::cout, fcn);
+#endif
bool changed = false;
TRACE_FUNCTION_FR("", changed);
@@ -1760,8 +1385,8 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
changed = true;
}
}
- // NOTE: Quick special-case for bswap<u8> (a no-op)
- else if( tef.name == "bswap" && tef.params.m_types.at(0) == ::HIR::CoreType::U8 )
+ // NOTE: Quick special-case for bswap<u8/i8> (a no-op)
+ else if( tef.name == "bswap" && (tef.params.m_types.at(0) == ::HIR::CoreType::U8 || tef.params.m_types.at(0) == ::HIR::CoreType::I8) )
{
DEBUG("bswap<u8> is a no-op");
if( auto* e = te.args.at(0).opt_LValue() )
@@ -1771,12 +1396,14 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
bb.terminator = ::MIR::Terminator::make_Goto(te.ret_block);
changed = true;
}
- //else if( tef.name == "get_dst_meta_slice" )
- //{
- // MIR_ASSERT(state, te.args.at(0).is_LValue(), "Argument to `get_dst_meta` must be a lvalue");
- // auto& e = te.args.at(0).as_LValue();
- // bb.statements.push_back(::MIR::Statement::make_Assign({ mv$(te.ret_val), ::MIR::RValue::make_DstMeta({ mv$(*e) }) }));
- //}
+ else if( tef.name == "mrustc_slice_len" )
+ {
+ MIR_ASSERT(state, te.args.at(0).is_LValue(), "Argument to `get_dst_meta` must be a lvalue");
+ auto& e = te.args.at(0).as_LValue();
+ bb.statements.push_back(::MIR::Statement::make_Assign({ mv$(te.ret_val), ::MIR::RValue::make_DstMeta({ mv$(e) }) }));
+ bb.terminator = ::MIR::Terminator::make_Goto(te.ret_block);
+ changed = true;
+ }
else
{
// Ignore any other intrinsics
@@ -1801,6 +1428,7 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
auto it = known_values.find(*pe);
if( it != known_values.end() )
{
+ DEBUG(state << "Value " << *pe << " known to be " << it->second);
p = it->second.clone();
}
}
@@ -1821,6 +1449,7 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
auto it = known_values.find(se);
if( it != known_values.end() )
{
+ DEBUG(state << "Value " << se << " known to be" << it->second);
e->src = it->second.clone();
}
),
@@ -1840,7 +1469,41 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
if( se.val_l.is_Constant() && se.val_r.is_Constant() )
{
- // TODO: Evaluate BinOp
+ const auto& val_l = se.val_l.as_Constant();
+ const auto& val_r = se.val_r.as_Constant();
+
+ ::MIR::Constant new_value;
+ bool replace = false;
+ switch(se.op)
+ {
+ case ::MIR::eBinOp::EQ:
+ if( val_l.is_Const() )
+ ;
+ else
+ {
+ replace = true;
+ new_value = ::MIR::Constant::make_Bool({val_l == val_r});
+ }
+ break;
+ case ::MIR::eBinOp::NE:
+ if( val_l.is_Const() )
+ ;
+ else
+ {
+ replace = true;
+ new_value = ::MIR::Constant::make_Bool({val_l != val_r});
+ }
+ break;
+ // TODO: Other binary operations
+ default:
+ break;
+ }
+
+ if( replace )
+ {
+ DEBUG(state << " " << e->src << " = " << new_value);
+ e->src = mv$(new_value);
+ }
}
),
(UniOp,
@@ -1848,6 +1511,8 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
if( it != known_values.end() )
{
const auto& val = it->second;
+ ::MIR::Constant new_value;
+ bool replace = false;
// TODO: Evaluate UniOp
switch( se.op )
{
@@ -1874,7 +1539,8 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
// Invalid type for Uint literal
break;
}
- e->src = ::MIR::Constant::make_Uint({ val, ve.t });
+ new_value = ::MIR::Constant::make_Uint({ val, ve.t });
+ replace = true;
),
(Int,
// Is ! valid on Int?
@@ -1883,7 +1549,8 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
// Not valid?
),
(Bool,
- e->src = ::MIR::Constant::make_Bool({ !ve.v });
+ new_value = ::MIR::Constant::make_Bool({ !ve.v });
+ replace = true;
),
(Bytes, ),
(StaticString, ),
@@ -1900,10 +1567,12 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
// Not valid?
),
(Int,
- e->src = ::MIR::Constant::make_Int({ -ve.v, ve.t });
+ new_value = ::MIR::Constant::make_Int({ -ve.v, ve.t });
+ replace = true;
),
(Float,
- e->src = ::MIR::Constant::make_Float({ -ve.v, ve.t });
+ new_value = ::MIR::Constant::make_Float({ -ve.v, ve.t });
+ replace = true;
),
(Bool,
// Not valid?
@@ -1918,6 +1587,11 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
)
break;
}
+ if( replace )
+ {
+ DEBUG(state << " " << e->src << " = " << new_value);
+ e->src = mv$(new_value);
+ }
}
),
(DstMeta,
@@ -1948,18 +1622,19 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
// - If a known temporary is borrowed mutably or mutated somehow, clear its knowledge
visit_mir_lvalues(stmt, [&known_values](const ::MIR::LValue& lv, ValUsage vu)->bool {
if( vu == ValUsage::Write ) {
- auto it = known_values.find(lv);
- if(it != known_values.end())
- known_values.erase(it);
+ known_values.erase(lv);
}
return false;
});
// - Locate `temp = SOME_CONST` and record value
if( const auto* e = stmt.opt_Assign() )
{
- if( e->dst.is_Temporary() && e->src.is_Constant() )
+ if( e->dst.is_Temporary() || e->dst.is_Variable() )
{
- known_values.insert(::std::make_pair( e->dst.clone(), e->src.as_Constant().clone() ));
+ if( const auto* ce = e->src.opt_Constant() )
+ {
+ known_values.insert(::std::make_pair( e->dst.clone(), ce->clone() ));
+ }
}
}
}
@@ -2000,12 +1675,15 @@ bool MIR_Optimise_ConstPropagte(::MIR::TypeResolve& state, ::MIR::Function& fcn)
if( se.dst != te.cond )
continue;
- if( !se.src.is_Constant() )
- continue;
- if( !se.src.as_Constant().is_Bool() )
- continue;
- val_known = true;
- known_val = se.src.as_Constant().as_Bool().v;
+ if( se.src.is_Constant() && se.src.as_Constant().is_Bool() )
+ {
+ val_known = true;
+ known_val = se.src.as_Constant().as_Bool().v;
+ }
+ else
+ {
+ val_known = false;
+ }
break;
}
else
@@ -2673,6 +2351,9 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn
DEBUG("GC Temporary(" << i << ")");
fcn.temporaries.erase(fcn.temporaries.begin() + j);
}
+ else {
+ DEBUG("tmp$" << i << " => tmp$" << j);
+ }
temp_rewrite_table.push_back( used_temps[i] ? j ++ : ~0u );
}
::std::vector<unsigned int> var_rewrite_table;
@@ -2684,6 +2365,9 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn
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;
@@ -2745,6 +2429,35 @@ bool MIR_Optimise_GarbageCollect(::MIR::TypeResolve& state, ::MIR::Function& fcn
if( se->other != ~0u )
se->other = df_rewrite_table[se->other];
}
+ else if( auto* se = stmt_it->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(); )
+ {
+ if( temp_rewrite_table.at(*it) == ~0u ) {
+ it = se->tmps.erase(it);
+ }
+ else {
+ *it = temp_rewrite_table.at(*it);
+ ++ it;
+ }
+ }
+
+ if( se->vars.empty() && se->tmps.empty() ) {
+ DEBUG("- Delete ScopeEnd (now empty)");
+ stmt_it = it->statements.erase(stmt_it);
+ -- stmt_it;
+ }
+ }
}
state.set_cur_stmt_term(i);
// Rewrite and advance