Allow msize optimization only if it is not present.

This commit is contained in:
chriseth 2019-05-27 13:42:50 +02:00
parent afe887adc1
commit d7b5ea6761
10 changed files with 100 additions and 17 deletions

View File

@ -55,7 +55,7 @@ void ExpressionInliner::visit(Expression& _expression)
bool movable = boost::algorithm::all_of( bool movable = boost::algorithm::all_of(
funCall.arguments, funCall.arguments,
[=](Expression const& _arg) { return MovableChecker(m_dialect, _arg).movable(); } [=](Expression const& _arg) { return SideEffectsCollector(m_dialect, _arg).movable(); }
); );
if (m_inlinableFunctions.count(funCall.functionName.name) && movable) if (m_inlinableFunctions.count(funCall.functionName.name) && movable)
{ {

View File

@ -43,7 +43,7 @@ void ExpressionSimplifier::visit(Expression& _expression)
// so if the value of the variable is not movable, the expression that references // so if the value of the variable is not movable, the expression that references
// the variable still is. // the variable still is.
if (match->removesNonConstants && !MovableChecker(m_dialect, _expression).movable()) if (match->removesNonConstants && !SideEffectsCollector(m_dialect, _expression).movable())
return; return;
_expression = match->action().toExpression(locationOf(_expression)); _expression = match->action().toExpression(locationOf(_expression));
} }

View File

@ -284,7 +284,7 @@ void RedundantAssignEliminator::finalize(
{ {
State const state = assignment.second == State::Undecided ? _finalState : assignment.second; State const state = assignment.second == State::Undecided ? _finalState : assignment.second;
if (state == State::Unused && MovableChecker{*m_dialect, *assignment.first->value}.movable()) if (state == State::Unused && SideEffectsCollector{*m_dialect, *assignment.first->value}.movable())
// TODO the only point where we actually need this // TODO the only point where we actually need this
// to be a set is for the for loop // to be a set is for the for loop
m_pendingRemovals.insert(assignment.first); m_pendingRemovals.insert(assignment.first);

View File

@ -45,7 +45,13 @@ public:
void operator()(FunctionCall const& _functionCall) override; void operator()(FunctionCall const& _functionCall) override;
bool movable() const { return m_movable; } bool movable() const { return m_movable; }
bool sideEffectFree() const { return m_sideEffectFree; } bool sideEffectFree(bool _allowMSizeModification = false) const
{
if (_allowMSizeModification)
return sideEffectFreeIfNoMSize();
else
return m_sideEffectFree;
}
bool sideEffectFreeIfNoMSize() const { return m_sideEffectFreeIfNoMSize; } bool sideEffectFreeIfNoMSize() const { return m_sideEffectFreeIfNoMSize; }
bool containsMSize() const { return m_containsMSize; } bool containsMSize() const { return m_containsMSize; }

View File

@ -187,7 +187,7 @@ bool Pattern::matches(
assertThrow(firstMatch, OptimizerException, "Match set but to null."); assertThrow(firstMatch, OptimizerException, "Match set but to null.");
return return
SyntacticallyEqual{}(*firstMatch, _expr) && SyntacticallyEqual{}(*firstMatch, _expr) &&
MovableChecker(_dialect, _expr).movable(); SideEffectsCollector(_dialect, _expr).movable();
} }
else if (m_kind == PatternKind::Any) else if (m_kind == PatternKind::Any)
(*m_matchGroups)[m_matchGroup] = &_expr; (*m_matchGroups)[m_matchGroup] = &_expr;

View File

@ -113,7 +113,12 @@ public:
}; };
template <typename ASTNode> template <typename ASTNode>
void eliminateVariables(Dialect const& _dialect, ASTNode& _node, size_t _numVariables) void eliminateVariables(
Dialect const& _dialect,
ASTNode& _node,
size_t _numVariables,
bool _allowMSizeOptimization
)
{ {
RematCandidateSelector selector{_dialect}; RematCandidateSelector selector{_dialect};
selector(_node); selector(_node);
@ -143,7 +148,7 @@ void eliminateVariables(Dialect const& _dialect, ASTNode& _node, size_t _numVari
} }
Rematerialiser::run(_dialect, _node, std::move(varsToEliminate)); Rematerialiser::run(_dialect, _node, std::move(varsToEliminate));
UnusedPruner::runUntilStabilised(_dialect, _node); UnusedPruner::runUntilStabilised(_dialect, _node, _allowMSizeOptimization);
} }
} }
@ -159,6 +164,7 @@ bool StackCompressor::run(
_ast.statements.size() > 0 && _ast.statements.at(0).type() == typeid(Block), _ast.statements.size() > 0 && _ast.statements.at(0).type() == typeid(Block),
"Need to run the function grouper before the stack compressor." "Need to run the function grouper before the stack compressor."
); );
bool allowMSizeOptimzation = !SideEffectsCollector(_dialect, _ast).containsMSize();
for (size_t iterations = 0; iterations < _maxIterations; iterations++) for (size_t iterations = 0; iterations < _maxIterations; iterations++)
{ {
map<YulString, int> stackSurplus = CompilabilityChecker::run(_dialect, _ast, _optimizeStackAllocation); map<YulString, int> stackSurplus = CompilabilityChecker::run(_dialect, _ast, _optimizeStackAllocation);
@ -168,7 +174,12 @@ bool StackCompressor::run(
if (stackSurplus.count(YulString{})) if (stackSurplus.count(YulString{}))
{ {
yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value."); yulAssert(stackSurplus.at({}) > 0, "Invalid surplus value.");
eliminateVariables(_dialect, boost::get<Block>(_ast.statements.at(0)), stackSurplus.at({})); eliminateVariables(
_dialect,
boost::get<Block>(_ast.statements.at(0)),
stackSurplus.at({}),
allowMSizeOptimzation
);
} }
for (size_t i = 1; i < _ast.statements.size(); ++i) for (size_t i = 1; i < _ast.statements.size(); ++i)
@ -178,7 +189,12 @@ bool StackCompressor::run(
continue; continue;
yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value."); yulAssert(stackSurplus.at(fun.name) > 0, "Invalid surplus value.");
eliminateVariables(_dialect, fun, stackSurplus.at(fun.name)); eliminateVariables(
_dialect,
fun,
stackSurplus.at(fun.name),
allowMSizeOptimzation
);
} }
} }
return false; return false;

View File

@ -32,16 +32,28 @@ using namespace std;
using namespace dev; using namespace dev;
using namespace yul; using namespace yul;
UnusedPruner::UnusedPruner(Dialect const& _dialect, Block& _ast, set<YulString> const& _externallyUsedFunctions): UnusedPruner::UnusedPruner(
m_dialect(_dialect) Dialect const& _dialect,
Block& _ast,
bool _allowMSizeOptimization,
set<YulString> const& _externallyUsedFunctions
):
m_dialect(_dialect),
m_allowMSizeOptimization(_allowMSizeOptimization)
{ {
m_references = ReferencesCounter::countReferences(_ast); m_references = ReferencesCounter::countReferences(_ast);
for (auto const& f: _externallyUsedFunctions) for (auto const& f: _externallyUsedFunctions)
++m_references[f]; ++m_references[f];
} }
UnusedPruner::UnusedPruner(Dialect const& _dialect, FunctionDefinition& _function, set<YulString> const& _externallyUsedFunctions): UnusedPruner::UnusedPruner(
m_dialect(_dialect) Dialect const& _dialect,
FunctionDefinition& _function,
bool _allowMSizeOptimization,
set<YulString> const& _externallyUsedFunctions
):
m_dialect(_dialect),
m_allowMSizeOptimization(_allowMSizeOptimization)
{ {
m_references = ReferencesCounter::countReferences(_function); m_references = ReferencesCounter::countReferences(_function);
for (auto const& f: _externallyUsedFunctions) for (auto const& f: _externallyUsedFunctions)
@ -75,7 +87,7 @@ void UnusedPruner::operator()(Block& _block)
{ {
if (!varDecl.value) if (!varDecl.value)
statement = Block{std::move(varDecl.location), {}}; statement = Block{std::move(varDecl.location), {}};
else if (MovableChecker(m_dialect, *varDecl.value).sideEffectFree()) else if (SideEffectsCollector(m_dialect, *varDecl.value).sideEffectFree(m_allowMSizeOptimization))
{ {
subtractReferences(ReferencesCounter::countReferences(*varDecl.value)); subtractReferences(ReferencesCounter::countReferences(*varDecl.value));
statement = Block{std::move(varDecl.location), {}}; statement = Block{std::move(varDecl.location), {}};
@ -93,7 +105,7 @@ void UnusedPruner::operator()(Block& _block)
else if (statement.type() == typeid(ExpressionStatement)) else if (statement.type() == typeid(ExpressionStatement))
{ {
ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement); ExpressionStatement& exprStmt = boost::get<ExpressionStatement>(statement);
if (MovableChecker(m_dialect, exprStmt.expression).sideEffectFree()) if (SideEffectsCollector(m_dialect, exprStmt.expression).sideEffectFree(m_allowMSizeOptimization))
{ {
subtractReferences(ReferencesCounter::countReferences(exprStmt.expression)); subtractReferences(ReferencesCounter::countReferences(exprStmt.expression));
statement = Block{std::move(exprStmt.location), {}}; statement = Block{std::move(exprStmt.location), {}};
@ -108,27 +120,41 @@ void UnusedPruner::operator()(Block& _block)
void UnusedPruner::runUntilStabilised( void UnusedPruner::runUntilStabilised(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
bool _allowMSizeOptization,
set<YulString> const& _externallyUsedFunctions set<YulString> const& _externallyUsedFunctions
) )
{ {
_allowMSizeOptization = !SideEffectsCollector(_dialect, _ast).containsMSize();
while (true) while (true)
{ {
UnusedPruner pruner(_dialect, _ast, _externallyUsedFunctions); UnusedPruner pruner(_dialect, _ast, _allowMSizeOptization, _externallyUsedFunctions);
pruner(_ast); pruner(_ast);
if (!pruner.shouldRunAgain()) if (!pruner.shouldRunAgain())
return; return;
} }
} }
void UnusedPruner::runUntilStabilised(
Dialect const& _dialect,
Block& _ast,
set<YulString> const& _externallyUsedFunctions
)
{
bool allowMSizeOptimization = !SideEffectsCollector(_dialect, _ast).containsMSize();
runUntilStabilised(_dialect, _ast, allowMSizeOptimization, _externallyUsedFunctions);
}
void UnusedPruner::runUntilStabilised( void UnusedPruner::runUntilStabilised(
Dialect const& _dialect, Dialect const& _dialect,
FunctionDefinition& _function, FunctionDefinition& _function,
bool _allowMSizeOptimization,
set<YulString> const& _externallyUsedFunctions set<YulString> const& _externallyUsedFunctions
) )
{ {
while (true) while (true)
{ {
UnusedPruner pruner(_dialect, _function, _externallyUsedFunctions); UnusedPruner pruner(_dialect, _function, _allowMSizeOptimization, _externallyUsedFunctions);
pruner(_function); pruner(_function);
if (!pruner.shouldRunAgain()) if (!pruner.shouldRunAgain())
return; return;

View File

@ -44,11 +44,13 @@ public:
UnusedPruner( UnusedPruner(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {} std::set<YulString> const& _externallyUsedFunctions = {}
); );
UnusedPruner( UnusedPruner(
Dialect const& _dialect, Dialect const& _dialect,
FunctionDefinition& _function, FunctionDefinition& _function,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {} std::set<YulString> const& _externallyUsedFunctions = {}
); );
@ -59,6 +61,13 @@ public:
bool shouldRunAgain() const { return m_shouldRunAgain; } bool shouldRunAgain() const { return m_shouldRunAgain; }
// Run the pruner until the code does not change anymore. // Run the pruner until the code does not change anymore.
static void runUntilStabilised(
Dialect const& _dialect,
Block& _ast,
bool _allowMSizeOptization,
std::set<YulString> const& _externallyUsedFunctions = {}
);
static void runUntilStabilised( static void runUntilStabilised(
Dialect const& _dialect, Dialect const& _dialect,
Block& _ast, Block& _ast,
@ -67,9 +76,13 @@ public:
// Run the pruner until the code does not change anymore. // Run the pruner until the code does not change anymore.
// Only run on the given function. // Only run on the given function.
// @param _allowMSizeOptimization if true, allows to remove instructions
// whose only side-effect is a potential change of the return value of
// the msize instruction.
static void runUntilStabilised( static void runUntilStabilised(
Dialect const& _dialect, Dialect const& _dialect,
FunctionDefinition& _functionDefinition, FunctionDefinition& _functionDefinition,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {} std::set<YulString> const& _externallyUsedFunctions = {}
); );
@ -78,6 +91,7 @@ private:
void subtractReferences(std::map<YulString, size_t> const& _subtrahend); void subtractReferences(std::map<YulString, size_t> const& _subtrahend);
Dialect const& m_dialect; Dialect const& m_dialect;
bool m_allowMSizeOptimization = false;
bool m_shouldRunAgain = false; bool m_shouldRunAgain = false;
std::map<YulString, size_t> m_references; std::map<YulString, size_t> m_references;
}; };

View File

@ -0,0 +1,12 @@
{
let a := 1
let b := mload(10)
sstore(0, msize())
}
// ====
// step: unusedPruner
// ----
// {
// pop(mload(10))
// sstore(0, msize())
// }

View File

@ -0,0 +1,9 @@
{
let a := 1
let b := mload(10)
sstore(0, 5)
}
// ====
// step: unusedPruner
// ----
// { sstore(0, 5) }