solidity/libyul/optimiser/ControlFlowSimplifier.cpp

234 lines
6.0 KiB
C++
Raw Normal View History

2019-05-09 19:56:56 +00:00
/*
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 <http://www.gnu.org/licenses/>.
*/
#include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/Semantics.h>
2019-09-23 14:32:50 +00:00
#include <libyul/optimiser/OptimiserStep.h>
2019-05-09 19:56:56 +00:00
#include <libyul/AsmData.h>
#include <libyul/Utilities.h>
2019-05-28 10:57:15 +00:00
#include <libyul/Dialect.h>
2019-05-09 19:56:56 +00:00
#include <libdevcore/CommonData.h>
#include <libdevcore/Visitor.h>
#include <boost/range/algorithm_ext/erase.hpp>
#include <boost/algorithm/cxx11/any_of.hpp>
using namespace std;
2019-12-11 16:31:36 +00:00
using namespace solidity;
using namespace solidity::util;
using namespace solidity::yul;
2019-05-09 19:56:56 +00:00
using OptionalStatements = std::optional<vector<Statement>>;
2019-05-09 19:56:56 +00:00
namespace
{
2019-05-28 10:57:15 +00:00
ExpressionStatement makeDiscardCall(
langutil::SourceLocation const& _location,
Dialect const& _dialect,
Expression&& _expression
)
2019-05-09 19:56:56 +00:00
{
2019-05-28 10:57:15 +00:00
yulAssert(_dialect.discardFunction(), "No discard function available.");
2019-05-20 12:30:32 +00:00
return {_location, FunctionCall{
2019-05-09 19:56:56 +00:00
_location,
2019-05-28 10:57:15 +00:00
Identifier{_location, _dialect.discardFunction()->name},
2019-05-09 19:56:56 +00:00
{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(); }
);
}
2019-05-28 10:57:15 +00:00
OptionalStatements reduceNoCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
2019-05-09 19:56:56 +00:00
{
yulAssert(_switchStmt.cases.empty(), "Expected no case!");
2019-05-28 10:57:15 +00:00
if (!_dialect.discardFunction())
return {};
2019-05-09 19:56:56 +00:00
auto loc = locationOf(*_switchStmt.expression);
2019-05-28 10:57:15 +00:00
return make_vector<Statement>(makeDiscardCall(
loc,
_dialect,
std::move(*_switchStmt.expression)
));
2019-05-09 19:56:56 +00:00
}
2019-05-28 10:57:15 +00:00
OptionalStatements reduceSingleCaseSwitch(Dialect const& _dialect, Switch& _switchStmt)
2019-05-09 19:56:56 +00:00
{
yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
auto& switchCase = _switchStmt.cases.front();
auto loc = locationOf(*_switchStmt.expression);
if (switchCase.value)
2019-05-28 10:57:15 +00:00
{
if (!_dialect.equalityFunction())
return {};
return make_vector<Statement>(If{
std::move(_switchStmt.location),
2019-05-20 12:30:32 +00:00
make_unique<Expression>(FunctionCall{
loc,
2019-05-28 10:57:15 +00:00
Identifier{loc, _dialect.equalityFunction()->name},
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
}),
std::move(switchCase.body)
2019-05-09 19:56:56 +00:00
});
2019-05-28 10:57:15 +00:00
}
2019-05-09 19:56:56 +00:00
else
2019-05-28 10:57:15 +00:00
{
if (!_dialect.discardFunction())
return {};
return make_vector<Statement>(
2019-05-28 10:57:15 +00:00
makeDiscardCall(
loc,
_dialect,
std::move(*_switchStmt.expression)
),
std::move(switchCase.body)
);
2019-05-28 10:57:15 +00:00
}
2019-05-09 19:56:56 +00:00
}
}
2019-09-23 14:32:50 +00:00
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
ControlFlowSimplifier{_context.dialect}(_ast);
}
2019-05-09 19:56:56 +00:00
void ControlFlowSimplifier::operator()(Block& _block)
{
simplify(_block.statements);
}
2019-10-24 16:52:54 +00:00
void ControlFlowSimplifier::operator()(FunctionDefinition& _funDef)
{
ASTModifier::operator()(_funDef);
2019-11-20 12:09:29 +00:00
if (!_funDef.body.statements.empty() && holds_alternative<Leave>(_funDef.body.statements.back()))
2019-10-24 16:52:54 +00:00
_funDef.body.statements.pop_back();
}
void ControlFlowSimplifier::visit(Statement& _st)
{
if (holds_alternative<ForLoop>(_st))
{
ForLoop& forLoop = std::get<ForLoop>(_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;
2019-05-20 12:30:32 +00:00
TerminationFinder::ControlFlow controlFlow = TerminationFinder{m_dialect}.controlFlowKind(forLoop.body.statements.back());
if (controlFlow == TerminationFinder::ControlFlow::Break)
{
isTerminating = true;
--m_numBreakStatements;
}
2019-10-28 14:25:02 +00:00
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);
}
2019-05-09 19:56:56 +00:00
void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
{
2019-11-24 17:46:43 +00:00
GenericVisitor visitor{
VisitorFallback<OptionalStatements>{},
2019-05-09 19:56:56 +00:00
[&](If& _ifStmt) -> OptionalStatements {
2019-05-28 10:57:15 +00:00
if (_ifStmt.body.statements.empty() && m_dialect.discardFunction())
2019-05-09 19:56:56 +00:00
{
OptionalStatements s = vector<Statement>{};
2019-05-28 10:57:15 +00:00
s->emplace_back(makeDiscardCall(
_ifStmt.location,
m_dialect,
std::move(*_ifStmt.condition)
));
2019-05-09 19:56:56 +00:00
return s;
}
return {};
},
[&](Switch& _switchStmt) -> OptionalStatements {
removeEmptyDefaultFromSwitch(_switchStmt);
removeEmptyCasesFromSwitch(_switchStmt);
if (_switchStmt.cases.empty())
2019-05-28 10:57:15 +00:00
return reduceNoCaseSwitch(m_dialect, _switchStmt);
2019-05-09 19:56:56 +00:00
else if (_switchStmt.cases.size() == 1)
2019-05-28 10:57:15 +00:00
return reduceSingleCaseSwitch(m_dialect, _switchStmt);
2019-05-09 19:56:56 +00:00
return {};
}
2019-11-24 17:46:43 +00:00
};
2019-05-09 19:56:56 +00:00
iterateReplacing(
_statements,
[&](Statement& _stmt) -> OptionalStatements
{
OptionalStatements result = std::visit(visitor, _stmt);
2019-05-09 19:56:56 +00:00
if (result)
simplify(*result);
else
visit(_stmt);
return result;
}
);
}