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(
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)
{

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
// the variable still is.
if (match->removesNonConstants && !MovableChecker(m_dialect, _expression).movable())
if (match->removesNonConstants && !SideEffectsCollector(m_dialect, _expression).movable())
return;
_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;
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
// to be a set is for the for loop
m_pendingRemovals.insert(assignment.first);

View File

@ -45,7 +45,13 @@ public:
void operator()(FunctionCall const& _functionCall) override;
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 containsMSize() const { return m_containsMSize; }

View File

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

View File

@ -113,7 +113,12 @@ public:
};
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};
selector(_node);
@ -143,7 +148,7 @@ void eliminateVariables(Dialect const& _dialect, ASTNode& _node, size_t _numVari
}
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),
"Need to run the function grouper before the stack compressor."
);
bool allowMSizeOptimzation = !SideEffectsCollector(_dialect, _ast).containsMSize();
for (size_t iterations = 0; iterations < _maxIterations; iterations++)
{
map<YulString, int> stackSurplus = CompilabilityChecker::run(_dialect, _ast, _optimizeStackAllocation);
@ -168,7 +174,12 @@ bool StackCompressor::run(
if (stackSurplus.count(YulString{}))
{
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)
@ -178,7 +189,12 @@ bool StackCompressor::run(
continue;
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;

View File

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

View File

@ -44,11 +44,13 @@ public:
UnusedPruner(
Dialect const& _dialect,
Block& _ast,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {}
);
UnusedPruner(
Dialect const& _dialect,
FunctionDefinition& _function,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {}
);
@ -59,6 +61,13 @@ public:
bool shouldRunAgain() const { return m_shouldRunAgain; }
// 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(
Dialect const& _dialect,
Block& _ast,
@ -67,9 +76,13 @@ public:
// Run the pruner until the code does not change anymore.
// 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(
Dialect const& _dialect,
FunctionDefinition& _functionDefinition,
bool _allowMSizeOptimization,
std::set<YulString> const& _externallyUsedFunctions = {}
);
@ -78,6 +91,7 @@ private:
void subtractReferences(std::map<YulString, size_t> const& _subtrahend);
Dialect const& m_dialect;
bool m_allowMSizeOptimization = false;
bool m_shouldRunAgain = false;
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) }