/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
using namespace dev;
using namespace yul;
using OptionalStatements = std::optional>;
namespace
{
ExpressionStatement makeDiscardCall(
langutil::SourceLocation const& _location,
Dialect const& _dialect,
Expression&& _expression
)
{
yulAssert(_dialect.discardFunction(), "No discard function available.");
return {_location, FunctionCall{
_location,
Identifier{_location, _dialect.discardFunction()->name},
{std::move(_expression)}
}};
}
void removeEmptyDefaultFromSwitch(Switch& _switchStmt)
{
boost::remove_erase_if(
_switchStmt.cases,
[](Case const& _case) { return !_case.value && _case.body.statements.empty(); }
);
}
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
{
bool hasDefault = boost::algorithm::any_of(
_switchStmt.cases,
[](Case const& _case) { return !_case.value; }
);
if (hasDefault)
return;
boost::remove_erase_if(
_switchStmt.cases,
[](Case const& _case) { return _case.body.statements.empty(); }
);
}
OptionalStatements reduceNoCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
{
yulAssert(_switchStmt.cases.empty(), "Expected no case!");
if (!_dialect.discardFunction())
return {};
auto loc = locationOf(*_switchStmt.expression);
return make_vector(makeDiscardCall(
loc,
_dialect,
std::move(*_switchStmt.expression)
));
}
OptionalStatements reduceSingleCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
{
yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
auto& switchCase = _switchStmt.cases.front();
auto loc = locationOf(*_switchStmt.expression);
if (switchCase.value)
{
if (!_dialect.equalityFunction())
return {};
return make_vector(If{
std::move(_switchStmt.location),
make_unique(FunctionCall{
loc,
Identifier{loc, _dialect.equalityFunction()->name},
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
}),
std::move(switchCase.body)
});
}
else
{
if (!_dialect.discardFunction())
return {};
return make_vector(
makeDiscardCall(
loc,
_dialect,
std::move(*_switchStmt.expression)
),
std::move(switchCase.body)
);
}
}
}
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
ControlFlowSimplifier{_context.dialect}(_ast);
}
void ControlFlowSimplifier::operator()(Block& _block)
{
simplify(_block.statements);
}
void ControlFlowSimplifier::operator()(FunctionDefinition& _funDef)
{
ASTModifier::operator()(_funDef);
if (!_funDef.body.statements.empty() && holds_alternative(_funDef.body.statements.back()))
_funDef.body.statements.pop_back();
}
void ControlFlowSimplifier::visit(Statement& _st)
{
if (holds_alternative(_st))
{
ForLoop& forLoop = std::get(_st);
yulAssert(forLoop.pre.statements.empty(), "");
size_t outerBreak = m_numBreakStatements;
size_t outerContinue = m_numContinueStatements;
m_numBreakStatements = 0;
m_numContinueStatements = 0;
ASTModifier::visit(_st);
if (!forLoop.body.statements.empty())
{
bool isTerminating = false;
TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back());
if (controlFlow == TerminationFinder::ControlFlow::Break)
{
isTerminating = true;
--m_numBreakStatements;
}
else if (
controlFlow == TerminationFinder::ControlFlow::Terminate ||
controlFlow == TerminationFinder::ControlFlow::Leave
)
isTerminating = true;
if (isTerminating && m_numContinueStatements == 0 && m_numBreakStatements == 0)
{
If replacement{forLoop.location, std::move(forLoop.condition), std::move(forLoop.body)};
if (controlFlow == TerminationFinder::ControlFlow::Break)
replacement.body.statements.resize(replacement.body.statements.size() - 1);
_st = std::move(replacement);
}
}
m_numBreakStatements = outerBreak;
m_numContinueStatements = outerContinue;
}
else
ASTModifier::visit(_st);
}
void ControlFlowSimplifier::simplify(std::vector& _statements)
{
GenericVisitor visitor{
VisitorFallback{},
[&](If& _ifStmt) -> OptionalStatements {
if (_ifStmt.body.statements.empty() && m_dialect.discardFunction())
{
OptionalStatements s = vector{};
s->emplace_back(makeDiscardCall(
_ifStmt.location,
m_dialect,
std::move(*_ifStmt.condition)
));
return s;
}
return {};
},
[&](Switch& _switchStmt) -> OptionalStatements {
removeEmptyDefaultFromSwitch(_switchStmt);
removeEmptyCasesFromSwitch(_switchStmt);
if (_switchStmt.cases.empty())
return reduceNoCaseSwitch(m_dialect, _switchStmt);
else if (_switchStmt.cases.size() == 1)
return reduceSingleCaseSwitch(m_dialect, _switchStmt);
return {};
}
};
iterateReplacing(
_statements,
[&](Statement& _stmt) -> OptionalStatements
{
OptionalStatements result = std::visit(visitor, _stmt);
if (result)
simplify(*result);
else
visit(_stmt);
return result;
}
);
}