Take non-continuing control-flow into account.

This commit is contained in:
chriseth 2022-11-29 12:15:49 +01:00
parent 90a147d371
commit bc127bcc6d
5 changed files with 93 additions and 8 deletions

View File

@ -24,6 +24,7 @@
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimizerUtilities.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libyul/AST.h>
#include <libyul/AsmPrinter.h>
@ -42,7 +43,10 @@ using namespace solidity::yul;
void UnusedAssignEliminator::run(OptimiserStepContext& _context, Block& _ast)
{
UnusedAssignEliminator rae{_context.dialect};
UnusedAssignEliminator rae{
_context.dialect,
ControlFlowSideEffectsCollector{_context.dialect, _ast}.functionSideEffectsNamed()
};
rae(_ast);
rae.m_storesToRemove += rae.m_allStores - rae.m_usedStores;
@ -74,10 +78,27 @@ void UnusedAssignEliminator::operator()(FunctionDefinition const& _functionDefin
UnusedStoreBase::operator()(_functionDefinition);
}
void UnusedAssignEliminator::operator()(FunctionCall const& _functionCall)
{
UnusedStoreBase::operator()(_functionCall);
ControlFlowSideEffects sideEffects;
if (auto builtin = m_dialect.builtin(_functionCall.functionName.name))
sideEffects = builtin->controlFlowSideEffects;
else
sideEffects = m_controlFlowSideEffects.at(_functionCall.functionName.name);
if (!sideEffects.canContinue)
// We do not return from the current function, so it is OK to also
// clear the return variables.
m_activeStores.clear();
}
void UnusedAssignEliminator::operator()(Leave const&)
{
for (YulString name: m_returnVariables)
markUsed(name);
m_activeStores.clear();
}
void UnusedAssignEliminator::operator()(Block const& _block)

View File

@ -26,6 +26,7 @@
#include <libyul/optimiser/ASTWalker.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/UnusedStoreBase.h>
#include <libyul/optimiser/Semantics.h>
#include <map>
#include <vector>
@ -99,7 +100,11 @@ struct Dialect;
* For switch statements that have a "default"-case, there is no control-flow
* part that skips the switch.
*
* At ``leave`` statements, all return variables are set to "used".
* At ``leave`` statements, all return variables are set to "used" and the set of active statements
* is cleared.
*
* If a function or builtin is called that does not continue, the set of active statements is
* cleared for all variables.
*
* In the second traversal, all assignments that are not marked as "used" are removed.
*
@ -114,11 +119,18 @@ public:
static constexpr char const* name{"UnusedAssignEliminator"};
static void run(OptimiserStepContext&, Block& _ast);
explicit UnusedAssignEliminator(Dialect const& _dialect): UnusedStoreBase(_dialect) {}
explicit UnusedAssignEliminator(
Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> _controlFlowSideEffects
):
UnusedStoreBase(_dialect),
m_controlFlowSideEffects(_controlFlowSideEffects)
{}
void operator()(Identifier const& _identifier) override;
void operator()(Assignment const& _assignment) override;
void operator()(FunctionDefinition const&) override;
void operator()(FunctionCall const& _functionCall) override;
void operator()(Leave const&) override;
void operator()(Block const& _block) override;
@ -132,6 +144,7 @@ private:
void markUsed(YulString _variable);
std::set<YulString> m_returnVariables;
std::map<YulString, ControlFlowSideEffects> m_controlFlowSideEffects;
};
}

View File

@ -0,0 +1,33 @@
{
function g() {
if calldataload(10) { revert(0, 0) }
}
function f() {
let a := calldataload(0)
if calldataload(1) {
// this can NOT be removed
a := 2
g()
}
sstore(0, a)
}
}
// ----
// step: unusedAssignEliminator
//
// {
// function g()
// {
// if calldataload(10) { revert(0, 0) }
// }
// function f()
// {
// let a := calldataload(0)
// if calldataload(1)
// {
// a := 2
// g()
// }
// sstore(0, a)
// }
// }

View File

@ -0,0 +1,22 @@
{
function f() {
let a := calldataload(0)
if calldataload(1) {
// this can be removed
a := 2
leave
}
sstore(0, a)
}
}
// ----
// step: unusedAssignEliminator
//
// {
// function f()
// {
// let a := calldataload(0)
// if calldataload(1) { leave }
// sstore(0, a)
// }
// }

View File

@ -12,10 +12,6 @@
//
// {
// let a := calldataload(0)
// if calldataload(1)
// {
// a := 2
// revert(0, 0)
// }
// if calldataload(1) { revert(0, 0) }
// sstore(0, a)
// }