mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
commit
563aec1df5
@ -10,6 +10,7 @@ Compiler Features:
|
|||||||
* SMTChecker: Support ``delete``.
|
* SMTChecker: Support ``delete``.
|
||||||
* SMTChecker: Inline external function calls to ``this``.
|
* SMTChecker: Inline external function calls to ``this``.
|
||||||
* Assembler: Encode the compiler version in the deployed bytecode.
|
* Assembler: Encode the compiler version in the deployed bytecode.
|
||||||
|
* Yul Optimizer: Simplify single-run ``for`` loops to ``if`` statements.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -136,7 +136,13 @@ bool SemanticInformation::terminatesControlFlow(AssemblyItem const& _item)
|
|||||||
{
|
{
|
||||||
if (_item.type() != Operation)
|
if (_item.type() != Operation)
|
||||||
return false;
|
return false;
|
||||||
switch (_item.instruction())
|
else
|
||||||
|
return terminatesControlFlow(_item.instruction());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SemanticInformation::terminatesControlFlow(Instruction _instruction)
|
||||||
|
{
|
||||||
|
switch (_instruction)
|
||||||
{
|
{
|
||||||
case Instruction::RETURN:
|
case Instruction::RETURN:
|
||||||
case Instruction::SELFDESTRUCT:
|
case Instruction::SELFDESTRUCT:
|
||||||
|
@ -48,6 +48,7 @@ struct SemanticInformation
|
|||||||
static bool isJumpInstruction(AssemblyItem const& _item);
|
static bool isJumpInstruction(AssemblyItem const& _item);
|
||||||
static bool altersControlFlow(AssemblyItem const& _item);
|
static bool altersControlFlow(AssemblyItem const& _item);
|
||||||
static bool terminatesControlFlow(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
|
/// @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.
|
/// the information in the current block header, memory, storage or stack.
|
||||||
static bool isDeterministic(AssemblyItem const& _item);
|
static bool isDeterministic(AssemblyItem const& _item);
|
||||||
|
@ -115,9 +115,51 @@ void ControlFlowSimplifier::operator()(Block& _block)
|
|||||||
simplify(_block.statements);
|
simplify(_block.statements);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlFlowSimplifier::visit(Statement& _st)
|
||||||
|
{
|
||||||
|
if (_st.type() == typeid(ForLoop))
|
||||||
|
{
|
||||||
|
ForLoop& forLoop = boost::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;
|
||||||
|
TerminationFinder::ControlFlow controlFlow = TerminationFinder::controlFlowKind(forLoop.body.statements.back());
|
||||||
|
if (controlFlow == TerminationFinder::ControlFlow::Break)
|
||||||
|
{
|
||||||
|
isTerminating = true;
|
||||||
|
--m_numBreakStatements;
|
||||||
|
}
|
||||||
|
else if (controlFlow == TerminationFinder::ControlFlow::Terminate)
|
||||||
|
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<yul::Statement>& _statements)
|
void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
||||||
{
|
{
|
||||||
GenericFallbackReturnsVisitor<OptionalStatements, If, Switch, ForLoop> const visitor(
|
GenericFallbackReturnsVisitor<OptionalStatements, If, Switch> const visitor(
|
||||||
[&](If& _ifStmt) -> OptionalStatements {
|
[&](If& _ifStmt) -> OptionalStatements {
|
||||||
if (_ifStmt.body.statements.empty())
|
if (_ifStmt.body.statements.empty())
|
||||||
{
|
{
|
||||||
@ -136,9 +178,6 @@ void ControlFlowSimplifier::simplify(std::vector<yul::Statement>& _statements)
|
|||||||
else if (_switchStmt.cases.size() == 1)
|
else if (_switchStmt.cases.size() == 1)
|
||||||
return reduceSingleCaseSwitch(_switchStmt);
|
return reduceSingleCaseSwitch(_switchStmt);
|
||||||
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
[&](ForLoop&) -> OptionalStatements {
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -38,7 +38,7 @@ namespace yul
|
|||||||
* The ControlFlowSimplifier does record the presence or absence of ``break``
|
* The ControlFlowSimplifier does record the presence or absence of ``break``
|
||||||
* and ``continue`` statements during its traversal.
|
* and ``continue`` statements during its traversal.
|
||||||
*
|
*
|
||||||
* Prerequisite: Disambiguator, ForLoopInitRewriter.
|
* Prerequisite: Disambiguator, FunctionHoister, ForLoopInitRewriter.
|
||||||
*
|
*
|
||||||
* Important: Introduces EVM opcodes and thus can only be used on EVM code for now.
|
* Important: Introduces EVM opcodes and thus can only be used on EVM code for now.
|
||||||
*/
|
*/
|
||||||
@ -46,9 +46,17 @@ class ControlFlowSimplifier: public ASTModifier
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using ASTModifier::operator();
|
using ASTModifier::operator();
|
||||||
|
void operator()(Break&) override { ++m_numBreakStatements; }
|
||||||
|
void operator()(Continue&) override { ++m_numContinueStatements; }
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
|
void visit(Statement& _st) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void simplify(std::vector<Statement>& _statements);
|
void simplify(std::vector<Statement>& _statements);
|
||||||
|
|
||||||
|
size_t m_numBreakStatements = 0;
|
||||||
|
size_t m_numContinueStatements = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||||
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/AsmData.h>
|
#include <libyul/AsmData.h>
|
||||||
|
|
||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
@ -30,42 +31,6 @@ using namespace std;
|
|||||||
using namespace dev;
|
using namespace dev;
|
||||||
using namespace yul;
|
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)
|
void DeadCodeEliminator::operator()(ForLoop& _for)
|
||||||
{
|
{
|
||||||
@ -75,24 +40,19 @@ void DeadCodeEliminator::operator()(ForLoop& _for)
|
|||||||
|
|
||||||
void DeadCodeEliminator::operator()(Block& _block)
|
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);
|
// Erase everything after the terminating statement that is not a function definition.
|
||||||
|
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != size_t(-1))
|
||||||
if (
|
_block.statements.erase(
|
||||||
firstTerminatingStatment != statements.end() &&
|
remove_if(
|
||||||
firstTerminatingStatment + 1 != statements.end()
|
_block.statements.begin() + index + 1,
|
||||||
)
|
_block.statements.end(),
|
||||||
statements.erase(
|
[] (Statement const& _s) { return _s.type() != typeid(yul::FunctionDefinition); }
|
||||||
std::remove_if(
|
|
||||||
firstTerminatingStatment + 1,
|
|
||||||
statements.end(),
|
|
||||||
[] (Statement const& _s)
|
|
||||||
{
|
|
||||||
return _s.type() != typeid(yul::FunctionDefinition);
|
|
||||||
}
|
|
||||||
),
|
),
|
||||||
statements.end()
|
_block.statements.end()
|
||||||
);
|
);
|
||||||
|
|
||||||
ASTModifier::operator()(_block);
|
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,
|
* 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.
|
* we require ForLoopInitRewriter to run before this step.
|
||||||
*
|
*
|
||||||
* Prerequisite: ForLoopInitRewriter
|
* Prerequisite: ForLoopInitRewriter, Function Hoister, Function Grouper
|
||||||
*/
|
*/
|
||||||
class DeadCodeEliminator: public ASTModifier
|
class DeadCodeEliminator: public ASTModifier
|
||||||
{
|
{
|
||||||
|
@ -72,3 +72,41 @@ void MovableChecker::visit(Statement const&)
|
|||||||
{
|
{
|
||||||
assertThrow(false, OptimizerException, "Movability for statement requested.");
|
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;
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
for {} calldatasize() { mstore(1, 2) } {
|
||||||
|
mstore(4, 5)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// if calldatasize() { mstore(4, 5) }
|
||||||
|
// }
|
@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
for {} calldatasize() { mstore(8, 9) } {
|
||||||
|
for {} calldatasize() { mstore(1, 2) } {
|
||||||
|
mstore(4, 5)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if mload(10) { continue }
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// for { } calldatasize() { mstore(8, 9) }
|
||||||
|
// {
|
||||||
|
// if calldatasize() { mstore(4, 5) }
|
||||||
|
// if mload(10) { continue }
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
for {} calldatasize() { mstore(8, 9) } {
|
||||||
|
for {} calldatasize() { mstore(1, 2) } {
|
||||||
|
mstore(4, 5)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// if calldatasize()
|
||||||
|
// {
|
||||||
|
// for { } calldatasize() { mstore(1, 2) }
|
||||||
|
// {
|
||||||
|
// mstore(4, 5)
|
||||||
|
// continue
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
for {} calldatasize() { mstore(1, 2) } {
|
||||||
|
let x := 7
|
||||||
|
mstore(4, 5)
|
||||||
|
revert(0, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// if calldatasize()
|
||||||
|
// {
|
||||||
|
// let x := 7
|
||||||
|
// mstore(4, 5)
|
||||||
|
// revert(0, x)
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
for {} calldatasize() { mstore(1, 2) } {
|
||||||
|
let x := 7
|
||||||
|
mstore(4, 5)
|
||||||
|
break
|
||||||
|
revert(0, x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// for { } calldatasize() { mstore(1, 2) }
|
||||||
|
// {
|
||||||
|
// let x := 7
|
||||||
|
// mstore(4, 5)
|
||||||
|
// break
|
||||||
|
// revert(0, x)
|
||||||
|
// }
|
||||||
|
// }
|
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
for {} calldatasize() { mstore(1, 2) } {
|
||||||
|
if calldatasize() { continue }
|
||||||
|
mstore(4, 5)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ====
|
||||||
|
// step: controlFlowSimplifier
|
||||||
|
// ----
|
||||||
|
// {
|
||||||
|
// for { } calldatasize() { mstore(1, 2) }
|
||||||
|
// {
|
||||||
|
// if calldatasize() { continue }
|
||||||
|
// mstore(4, 5)
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
// }
|
Loading…
Reference in New Issue
Block a user