mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Refactor termination detection.
This commit is contained in:
parent
1d75770700
commit
99e96c2d66
@ -136,7 +136,13 @@ bool SemanticInformation::terminatesControlFlow(AssemblyItem const& _item)
|
||||
{
|
||||
if (_item.type() != Operation)
|
||||
return false;
|
||||
switch (_item.instruction())
|
||||
else
|
||||
return terminatesControlFlow(_item.instruction());
|
||||
}
|
||||
|
||||
bool SemanticInformation::terminatesControlFlow(Instruction _instruction)
|
||||
{
|
||||
switch (_instruction)
|
||||
{
|
||||
case Instruction::RETURN:
|
||||
case Instruction::SELFDESTRUCT:
|
||||
|
@ -48,6 +48,7 @@ struct SemanticInformation
|
||||
static bool isJumpInstruction(AssemblyItem const& _item);
|
||||
static bool altersControlFlow(AssemblyItem const& _item);
|
||||
static bool terminatesControlFlow(AssemblyItem const& _item);
|
||||
static bool terminatesControlFlow(Instruction _instruction);
|
||||
/// @returns false if the value put on the stack by _item depends on anything else than
|
||||
/// the information in the current block header, memory, storage or stack.
|
||||
static bool isDeterministic(AssemblyItem const& _item);
|
||||
|
@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||
#include <libyul/optimiser/Semantics.h>
|
||||
#include <libyul/AsmData.h>
|
||||
|
||||
#include <libevmasm/SemanticInformation.h>
|
||||
@ -30,42 +31,6 @@ using namespace std;
|
||||
using namespace dev;
|
||||
using namespace yul;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool isTerminating(yul::ExpressionStatement const& _exprStmnt)
|
||||
{
|
||||
if (_exprStmnt.expression.type() != typeid(FunctionalInstruction))
|
||||
return false;
|
||||
|
||||
auto const& funcInstr = boost::get<FunctionalInstruction>(_exprStmnt.expression);
|
||||
|
||||
return eth::SemanticInformation::terminatesControlFlow(funcInstr.instruction);
|
||||
}
|
||||
|
||||
/// Returns an iterator to the first terminating statement or
|
||||
/// `_block.statements.end()()` when none was found
|
||||
auto findFirstTerminatingStatement(Block& _block)
|
||||
{
|
||||
return find_if(
|
||||
_block.statements.begin(),
|
||||
_block.statements.end(),
|
||||
[](Statement const& _stmnt)
|
||||
{
|
||||
if (
|
||||
_stmnt.type() == typeid(ExpressionStatement) &&
|
||||
isTerminating(boost::get<ExpressionStatement>(_stmnt))
|
||||
)
|
||||
return true;
|
||||
else if (_stmnt.type() == typeid(Break))
|
||||
return true;
|
||||
else if (_stmnt.type() == typeid(Continue))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void DeadCodeEliminator::operator()(ForLoop& _for)
|
||||
{
|
||||
@ -75,24 +40,19 @@ void DeadCodeEliminator::operator()(ForLoop& _for)
|
||||
|
||||
void DeadCodeEliminator::operator()(Block& _block)
|
||||
{
|
||||
auto& statements = _block.statements;
|
||||
TerminationFinder::ControlFlow controlFlowChange;
|
||||
size_t index;
|
||||
tie(controlFlowChange, index) = TerminationFinder::firstUnconditionalControlFlowChange(_block.statements);
|
||||
|
||||
auto firstTerminatingStatment = findFirstTerminatingStatement(_block);
|
||||
|
||||
if (
|
||||
firstTerminatingStatment != statements.end() &&
|
||||
firstTerminatingStatment + 1 != statements.end()
|
||||
)
|
||||
statements.erase(
|
||||
std::remove_if(
|
||||
firstTerminatingStatment + 1,
|
||||
statements.end(),
|
||||
[] (Statement const& _s)
|
||||
{
|
||||
return _s.type() != typeid(yul::FunctionDefinition);
|
||||
}
|
||||
// Erase everything after the terminating statement that is not a function definition.
|
||||
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != size_t(-1))
|
||||
_block.statements.erase(
|
||||
remove_if(
|
||||
_block.statements.begin() + index + 1,
|
||||
_block.statements.end(),
|
||||
[] (Statement const& _s) { return _s.type() != typeid(yul::FunctionDefinition); }
|
||||
),
|
||||
statements.end()
|
||||
_block.statements.end()
|
||||
);
|
||||
|
||||
ASTModifier::operator()(_block);
|
||||
|
@ -41,7 +41,7 @@ namespace yul
|
||||
* Because variables declared in a for loop's init block have their scope extended to the loop body,
|
||||
* we require ForLoopInitRewriter to run before this step.
|
||||
*
|
||||
* Prerequisite: ForLoopInitRewriter
|
||||
* Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper
|
||||
*/
|
||||
class DeadCodeEliminator: public ASTModifier
|
||||
{
|
||||
|
@ -72,3 +72,41 @@ void MovableChecker::visit(Statement const&)
|
||||
{
|
||||
assertThrow(false, OptimizerException, "Movability for statement requested.");
|
||||
}
|
||||
|
||||
pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUnconditionalControlFlowChange(
|
||||
vector<Statement> const& _statements
|
||||
)
|
||||
{
|
||||
for (size_t i = 0; i < _statements.size(); ++i)
|
||||
{
|
||||
ControlFlow controlFlow = controlFlowKind(_statements[i]);
|
||||
if (controlFlow != ControlFlow::FlowOut)
|
||||
return {controlFlow, i};
|
||||
}
|
||||
return {ControlFlow::FlowOut, size_t(-1)};
|
||||
}
|
||||
|
||||
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
|
||||
{
|
||||
if (
|
||||
_statement.type() == typeid(ExpressionStatement) &&
|
||||
isTerminatingBuiltin(boost::get<ExpressionStatement>(_statement))
|
||||
)
|
||||
return ControlFlow::Terminate;
|
||||
else if (_statement.type() == typeid(Break))
|
||||
return ControlFlow::Break;
|
||||
else if (_statement.type() == typeid(Continue))
|
||||
return ControlFlow::Continue;
|
||||
else
|
||||
return ControlFlow::FlowOut;
|
||||
}
|
||||
|
||||
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
|
||||
{
|
||||
if (_exprStmnt.expression.type() != typeid(FunctionalInstruction))
|
||||
return false;
|
||||
|
||||
return eth::SemanticInformation::terminatesControlFlow(
|
||||
boost::get<FunctionalInstruction>(_exprStmnt.expression).instruction
|
||||
);
|
||||
}
|
||||
|
@ -56,4 +56,34 @@ private:
|
||||
bool m_movable = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper class to find "irregular" control flow.
|
||||
* This includes termination, break and continue.
|
||||
*/
|
||||
class TerminationFinder
|
||||
{
|
||||
public:
|
||||
enum class ControlFlow { FlowOut, Break, Continue, Terminate };
|
||||
|
||||
/// @returns the index of the first statement in the provided sequence
|
||||
/// that is an unconditional ``break``, ``continue`` or a
|
||||
/// call to a terminating builtin function.
|
||||
/// If control flow can continue at the end of the list,
|
||||
/// returns `FlowOut` and ``size_t(-1)``.
|
||||
/// The function might return ``FlowOut`` even though control
|
||||
/// flow cannot actually continue.
|
||||
static std::pair<ControlFlow, size_t> firstUnconditionalControlFlowChange(
|
||||
std::vector<Statement> const& _statements
|
||||
);
|
||||
|
||||
/// @returns the control flow type of the given statement.
|
||||
/// This function could return FlowOut even if control flow never continues.
|
||||
static ControlFlow controlFlowKind(Statement const& _statement);
|
||||
|
||||
/// @returns true if the expression statement is a direct
|
||||
/// call to a builtin terminating function like
|
||||
/// ``stop``, ``revert`` or ``return``.
|
||||
static bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
|
||||
};
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user