solidity/libyul/optimiser/ControlFlowSimplifier.cpp

231 lines
6.2 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/>.
*/
// SPDX-License-Identifier: GPL-3.0
2019-05-09 19:56:56 +00:00
#include <libyul/optimiser/ControlFlowSimplifier.h>
#include <libyul/optimiser/Semantics.h>
2019-09-23 14:32:50 +00:00
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/optimiser/TypeInfo.h>
#include <libyul/AST.h>
2019-05-09 19:56:56 +00:00
#include <libyul/Utilities.h>
2019-05-28 10:57:15 +00:00
#include <libyul/Dialect.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/Visitor.h>
2019-05-09 19:56:56 +00:00
#include <range/v3/action/remove_if.hpp>
2019-05-09 19:56:56 +00:00
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<std::vector<Statement>>;
2019-05-09 19:56:56 +00:00
namespace
{
2019-05-28 10:57:15 +00:00
ExpressionStatement makeDiscardCall(
2021-04-27 14:53:04 +00:00
std::shared_ptr<DebugData const> const& _debugData,
BuiltinFunction const& _discardFunction,
2019-05-28 10:57:15 +00:00
Expression&& _expression
)
2019-05-09 19:56:56 +00:00
{
2021-04-27 14:53:04 +00:00
return {_debugData, FunctionCall{
_debugData,
Identifier{_debugData, _discardFunction.name},
2019-05-09 19:56:56 +00:00
{std::move(_expression)}
}};
}
void removeEmptyDefaultFromSwitch(Switch& _switchStmt)
{
ranges::actions::remove_if(
2019-05-09 19:56:56 +00:00
_switchStmt.cases,
[](Case const& _case) { return !_case.value && _case.body.statements.empty(); }
);
}
void removeEmptyCasesFromSwitch(Switch& _switchStmt)
{
if (hasDefaultCase(_switchStmt))
2019-05-09 19:56:56 +00:00
return;
ranges::actions::remove_if(
2019-05-09 19:56:56 +00:00
_switchStmt.cases,
[](Case const& _case) { return _case.body.statements.empty(); }
);
}
}
2019-09-23 14:32:50 +00:00
void ControlFlowSimplifier::run(OptimiserStepContext& _context, Block& _ast)
{
TypeInfo typeInfo(_context.dialect, _ast);
ControlFlowSimplifier{_context.dialect, typeInfo}(_ast);
2019-09-23 14:32:50 +00:00
}
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);
if (!_funDef.body.statements.empty() && std::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 (std::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)
{
2021-04-27 14:53:04 +00:00
If replacement{forLoop.debugData, 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 {
if (_ifStmt.body.statements.empty() && m_dialect.discardFunction(m_dialect.boolType))
2019-05-09 19:56:56 +00:00
{
OptionalStatements s = std::vector<Statement>{};
2019-05-28 10:57:15 +00:00
s->emplace_back(makeDiscardCall(
2021-04-27 14:53:04 +00:00
_ifStmt.debugData,
*m_dialect.discardFunction(m_dialect.boolType),
2019-05-28 10:57:15 +00:00
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())
return reduceNoCaseSwitch(_switchStmt);
2019-05-09 19:56:56 +00:00
else if (_switchStmt.cases.size() == 1)
return reduceSingleCaseSwitch(_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;
}
);
}
OptionalStatements ControlFlowSimplifier::reduceNoCaseSwitch(Switch& _switchStmt) const
{
yulAssert(_switchStmt.cases.empty(), "Expected no case!");
BuiltinFunction const* discardFunction =
m_dialect.discardFunction(m_typeInfo.typeOf(*_switchStmt.expression));
if (!discardFunction)
return {};
return make_vector<Statement>(makeDiscardCall(
2021-04-27 14:53:04 +00:00
debugDataOf(*_switchStmt.expression),
*discardFunction,
std::move(*_switchStmt.expression)
));
}
OptionalStatements ControlFlowSimplifier::reduceSingleCaseSwitch(Switch& _switchStmt) const
{
yulAssert(_switchStmt.cases.size() == 1, "Expected only one case!");
auto& switchCase = _switchStmt.cases.front();
std::shared_ptr<DebugData const> debugData = debugDataOf(*_switchStmt.expression);
YulString type = m_typeInfo.typeOf(*_switchStmt.expression);
if (switchCase.value)
{
if (!m_dialect.equalityFunction(type))
return {};
return make_vector<Statement>(If{
2021-04-27 14:53:04 +00:00
std::move(_switchStmt.debugData),
std::make_unique<Expression>(FunctionCall{
2021-04-27 14:53:04 +00:00
debugData,
Identifier{debugData, m_dialect.equalityFunction(type)->name},
{std::move(*switchCase.value), std::move(*_switchStmt.expression)}
}),
std::move(switchCase.body)
});
}
else
{
if (!m_dialect.discardFunction(type))
return {};
return make_vector<Statement>(
makeDiscardCall(
2021-04-27 14:53:04 +00:00
debugData,
*m_dialect.discardFunction(type),
std::move(*_switchStmt.expression)
),
std::move(switchCase.body)
);
}
}