From 08abcbb9211cc1452a8b6420d4e160aaad061e01 Mon Sep 17 00:00:00 2001 From: Alon Zakai Date: Thu, 19 May 2016 15:12:48 -0700 Subject: vacuum away everything not tied down --- src/passes/SimplifyLocals.cpp | 3 +- src/passes/Vacuum.cpp | 119 +++++++++++++++++++++++++++++++++++++++--- 2 files changed, 112 insertions(+), 10 deletions(-) (limited to 'src') diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp index fbd1e7e57..dac6cfa65 100644 --- a/src/passes/SimplifyLocals.cpp +++ b/src/passes/SimplifyLocals.cpp @@ -26,8 +26,7 @@ // removing multiple set_locals and replacing them with one that the // block returns to. Further optimization rounds then have the opportunity // to remove that set_local as well. TODO: support partial traces; right -// now, whenever control flow splits, we invalidate everything. This is -// enough for SSA form, but not otherwise. +// now, whenever control flow splits, we invalidate everything. // // After this pass, some locals may be completely unused. reorder-locals // can get rid of those (the operation is trivial there after it sorts by use diff --git a/src/passes/Vacuum.cpp b/src/passes/Vacuum.cpp index 458ce63ca..6d6f1490a 100644 --- a/src/passes/Vacuum.cpp +++ b/src/passes/Vacuum.cpp @@ -30,25 +30,119 @@ struct Vacuum : public WalkerPass>> { std::vector expressionStack; - bool isDead(Expression* curr, bool resultMayBeUsed) { - if (curr->is()) return true; - // dead get_locals may be generated from coalesce-locals - if (curr->is() && (!resultMayBeUsed || !ExpressionAnalyzer::isResultUsed(expressionStack, getFunction()))) return true; - // TODO: more dead code - return false; + // returns nullptr if curr is dead, curr if it must stay as is, or another node if it can be replaced + Expression* optimize(Expression* curr, bool resultUsed) { + while (1) { + switch (curr->_id) { + case Expression::Id::NopId: return nullptr; // never needed + + case Expression::Id::BlockId: return curr; // not always needed, but handled in visitBlock() + case Expression::Id::IfId: return curr; // not always needed, but handled in visitIf() + case Expression::Id::LoopId: return curr; // not always needed, but handled in visitLoop() + + case Expression::Id::BreakId: + case Expression::Id::SwitchId: + case Expression::Id::CallId: + case Expression::Id::CallImportId: + case Expression::Id::CallIndirectId: + case Expression::Id::SetLocalId: + case Expression::Id::LoadId: + case Expression::Id::StoreId: + case Expression::Id::ReturnId: + case Expression::Id::HostId: + case Expression::Id::UnreachableId: return curr; // always needed + + case Expression::Id::ConstId: + case Expression::Id::GetLocalId: + case Expression::Id::UnaryId: + case Expression::Id::BinaryId: + case Expression::Id::SelectId: { + if (resultUsed) { + return curr; // used, keep it + } + // result is not used, perhaps it is dead + if (curr->is() || curr->is()) { + return nullptr; + } + // for unary, binary, and select, we need to check their arguments for side effects + if (auto* unary = curr->dynCast()) { + if (EffectAnalyzer(unary->value).hasSideEffects()) { + curr = unary->value; + continue; + } else { + return nullptr; + } + } else if (auto* binary = curr->dynCast()) { + if (EffectAnalyzer(binary->left).hasSideEffects()) { + if (EffectAnalyzer(binary->right).hasSideEffects()) { + return curr; // leave them + } else { + curr = binary->left; + continue; + } + } else { + if (EffectAnalyzer(binary->right).hasSideEffects()) { + curr = binary->right; + continue; + } else { + return nullptr; + } + } + } else { + // TODO: if two have side effects, we could replace the select with say an add? + auto* select = curr->cast