mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #12091 from ethereum/applyControlFlowSideEffectsUserDefined
Use side effects of user-defined functions in other optimizer steps.
This commit is contained in:
commit
a7b137829f
@ -12,6 +12,7 @@ Compiler Features:
|
|||||||
* SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``.
|
* SMTChecker: Report contract invariants and reentrancy properties. This can be enabled via the CLI option ``--model-checker-invariants`` or the Standard JSON option ``settings.modelChecker.invariants``.
|
||||||
* Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``.
|
* Standard JSON: Accept nested brackets in step sequences passed to ``settings.optimizer.details.yulDetails.optimizerSteps``.
|
||||||
* Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
|
* Standard JSON: Add ``settings.debug.debugInfo`` option for selecting how much extra debug information should be included in the produced EVM assembly and Yul code.
|
||||||
|
* Yul Optimizer: Take control-flow side-effects of user-defined functions into account in various optimizer steps.
|
||||||
|
|
||||||
|
|
||||||
Bugfixes:
|
Bugfixes:
|
||||||
|
@ -19,6 +19,7 @@
|
|||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
#include <libyul/optimiser/NameCollector.h>
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -26,6 +27,12 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
void ConditionalSimplifier::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
|
{
|
||||||
|
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
|
||||||
|
ConditionalSimplifier{_context.dialect, sideEffects.functionSideEffects()}(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
void ConditionalSimplifier::operator()(Switch& _switch)
|
void ConditionalSimplifier::operator()(Switch& _switch)
|
||||||
{
|
{
|
||||||
visit(*_switch.expression);
|
visit(*_switch.expression);
|
||||||
@ -65,7 +72,7 @@ void ConditionalSimplifier::operator()(Block& _block)
|
|||||||
if (
|
if (
|
||||||
holds_alternative<Identifier>(*_if.condition) &&
|
holds_alternative<Identifier>(*_if.condition) &&
|
||||||
!_if.body.statements.empty() &&
|
!_if.body.statements.empty() &&
|
||||||
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
|
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
|
||||||
TerminationFinder::ControlFlow::FlowOut
|
TerminationFinder::ControlFlow::FlowOut
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -44,7 +44,6 @@ namespace solidity::yul
|
|||||||
*
|
*
|
||||||
* Future features:
|
* Future features:
|
||||||
* - allow replacements by "1"
|
* - allow replacements by "1"
|
||||||
* - take termination of user-defined functions into account
|
|
||||||
*
|
*
|
||||||
* Works best with SSA form and if dead code removal has run before.
|
* Works best with SSA form and if dead code removal has run before.
|
||||||
*
|
*
|
||||||
@ -54,20 +53,21 @@ class ConditionalSimplifier: public ASTModifier
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr char const* name{"ConditionalSimplifier"};
|
static constexpr char const* name{"ConditionalSimplifier"};
|
||||||
static void run(OptimiserStepContext& _context, Block& _ast)
|
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||||
{
|
|
||||||
ConditionalSimplifier{_context.dialect}(_ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
using ASTModifier::operator();
|
using ASTModifier::operator();
|
||||||
void operator()(Switch& _switch) override;
|
void operator()(Switch& _switch) override;
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ConditionalSimplifier(Dialect const& _dialect):
|
explicit ConditionalSimplifier(
|
||||||
m_dialect(_dialect)
|
Dialect const& _dialect,
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
|
||||||
|
):
|
||||||
|
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
|
||||||
{}
|
{}
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
#include <libyul/Utilities.h>
|
#include <libyul/Utilities.h>
|
||||||
#include <libyul/optimiser/NameCollector.h>
|
#include <libyul/optimiser/NameCollector.h>
|
||||||
|
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||||
#include <libsolutil/CommonData.h>
|
#include <libsolutil/CommonData.h>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
@ -27,6 +28,12 @@ using namespace solidity;
|
|||||||
using namespace solidity::yul;
|
using namespace solidity::yul;
|
||||||
using namespace solidity::util;
|
using namespace solidity::util;
|
||||||
|
|
||||||
|
void ConditionalUnsimplifier::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
|
{
|
||||||
|
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
|
||||||
|
ConditionalUnsimplifier{_context.dialect, sideEffects.functionSideEffects()}(_ast);
|
||||||
|
}
|
||||||
|
|
||||||
void ConditionalUnsimplifier::operator()(Switch& _switch)
|
void ConditionalUnsimplifier::operator()(Switch& _switch)
|
||||||
{
|
{
|
||||||
visit(*_switch.expression);
|
visit(*_switch.expression);
|
||||||
@ -78,7 +85,7 @@ void ConditionalUnsimplifier::operator()(Block& _block)
|
|||||||
YulString condition = std::get<Identifier>(*_if.condition).name;
|
YulString condition = std::get<Identifier>(*_if.condition).name;
|
||||||
if (
|
if (
|
||||||
holds_alternative<Assignment>(_stmt2) &&
|
holds_alternative<Assignment>(_stmt2) &&
|
||||||
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
|
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
|
||||||
TerminationFinder::ControlFlow::FlowOut
|
TerminationFinder::ControlFlow::FlowOut
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -33,20 +33,21 @@ class ConditionalUnsimplifier: public ASTModifier
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
static constexpr char const* name{"ConditionalUnsimplifier"};
|
static constexpr char const* name{"ConditionalUnsimplifier"};
|
||||||
static void run(OptimiserStepContext& _context, Block& _ast)
|
static void run(OptimiserStepContext& _context, Block& _ast);
|
||||||
{
|
|
||||||
ConditionalUnsimplifier{_context.dialect}(_ast);
|
|
||||||
}
|
|
||||||
|
|
||||||
using ASTModifier::operator();
|
using ASTModifier::operator();
|
||||||
void operator()(Switch& _switch) override;
|
void operator()(Switch& _switch) override;
|
||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ConditionalUnsimplifier(Dialect const& _dialect):
|
explicit ConditionalUnsimplifier(
|
||||||
m_dialect(_dialect)
|
Dialect const& _dialect,
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
|
||||||
|
):
|
||||||
|
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
|
||||||
{}
|
{}
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include <libyul/optimiser/DeadCodeEliminator.h>
|
#include <libyul/optimiser/DeadCodeEliminator.h>
|
||||||
#include <libyul/optimiser/Semantics.h>
|
#include <libyul/optimiser/Semantics.h>
|
||||||
#include <libyul/optimiser/OptimiserStep.h>
|
#include <libyul/optimiser/OptimiserStep.h>
|
||||||
|
#include <libyul/ControlFlowSideEffectsCollector.h>
|
||||||
#include <libyul/AST.h>
|
#include <libyul/AST.h>
|
||||||
|
|
||||||
#include <libevmasm/SemanticInformation.h>
|
#include <libevmasm/SemanticInformation.h>
|
||||||
@ -36,7 +37,11 @@ using namespace solidity::yul;
|
|||||||
|
|
||||||
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast)
|
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast)
|
||||||
{
|
{
|
||||||
DeadCodeEliminator{_context.dialect}(_ast);
|
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
|
||||||
|
DeadCodeEliminator{
|
||||||
|
_context.dialect,
|
||||||
|
sideEffects.functionSideEffects()
|
||||||
|
}(_ast);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeadCodeEliminator::operator()(ForLoop& _for)
|
void DeadCodeEliminator::operator()(ForLoop& _for)
|
||||||
@ -49,7 +54,7 @@ void DeadCodeEliminator::operator()(Block& _block)
|
|||||||
{
|
{
|
||||||
TerminationFinder::ControlFlow controlFlowChange;
|
TerminationFinder::ControlFlow controlFlowChange;
|
||||||
size_t index;
|
size_t index;
|
||||||
tie(controlFlowChange, index) = TerminationFinder{m_dialect}.firstUnconditionalControlFlowChange(_block.statements);
|
tie(controlFlowChange, index) = TerminationFinder{m_dialect, &m_functionSideEffects}.firstUnconditionalControlFlowChange(_block.statements);
|
||||||
|
|
||||||
// Erase everything after the terminating statement that is not a function definition.
|
// Erase everything after the terminating statement that is not a function definition.
|
||||||
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max())
|
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max())
|
||||||
|
@ -31,12 +31,15 @@ namespace solidity::yul
|
|||||||
{
|
{
|
||||||
struct Dialect;
|
struct Dialect;
|
||||||
struct OptimiserStepContext;
|
struct OptimiserStepContext;
|
||||||
|
struct ControlFlowSideEffects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Optimisation stage that removes unreachable code
|
* Optimisation stage that removes unreachable code
|
||||||
*
|
*
|
||||||
* Unreachable code is any code within a block which is preceded by a
|
* Unreachable code is any code within a block which is preceded by a
|
||||||
* leave, return, invalid, break, continue, selfdestruct or revert.
|
* leave, return, invalid, break, continue, selfdestruct or revert or
|
||||||
|
* a call to a user-defined function that never returns (either due to
|
||||||
|
* recursion or a call to return / revert / stop).
|
||||||
*
|
*
|
||||||
* Function definitions are retained as they might be called by earlier
|
* Function definitions are retained as they might be called by earlier
|
||||||
* code and thus are considered reachable.
|
* code and thus are considered reachable.
|
||||||
@ -57,9 +60,13 @@ public:
|
|||||||
void operator()(Block& _block) override;
|
void operator()(Block& _block) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
|
DeadCodeEliminator(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
|
||||||
|
): m_dialect(_dialect), m_functionSideEffects(_sideEffects) {}
|
||||||
|
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition
|
|||||||
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
|
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
|
||||||
{
|
{
|
||||||
if (
|
if (
|
||||||
|
holds_alternative<VariableDeclaration>(_statement) &&
|
||||||
|
std::get<VariableDeclaration>(_statement).value &&
|
||||||
|
containsNonContinuingFunctionCall(*std::get<VariableDeclaration>(_statement).value)
|
||||||
|
)
|
||||||
|
return ControlFlow::Terminate;
|
||||||
|
else if (
|
||||||
|
holds_alternative<Assignment>(_statement) &&
|
||||||
|
containsNonContinuingFunctionCall(*std::get<Assignment>(_statement).value)
|
||||||
|
)
|
||||||
|
return ControlFlow::Terminate;
|
||||||
|
else if (
|
||||||
holds_alternative<ExpressionStatement>(_statement) &&
|
holds_alternative<ExpressionStatement>(_statement) &&
|
||||||
isTerminatingBuiltin(std::get<ExpressionStatement>(_statement))
|
containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression)
|
||||||
)
|
)
|
||||||
return ControlFlow::Terminate;
|
return ControlFlow::Terminate;
|
||||||
else if (holds_alternative<Break>(_statement))
|
else if (holds_alternative<Break>(_statement))
|
||||||
@ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
|
|||||||
return ControlFlow::FlowOut;
|
return ControlFlow::FlowOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
|
bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr)
|
||||||
{
|
{
|
||||||
if (holds_alternative<FunctionCall>(_exprStmnt.expression))
|
if (auto functionCall = std::get_if<FunctionCall>(&_expr))
|
||||||
if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name))
|
{
|
||||||
return evmasm::SemanticInformation::terminatesControlFlow(*instruction);
|
for (auto const& arg: functionCall->arguments)
|
||||||
|
if (containsNonContinuingFunctionCall(arg))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (auto builtin = m_dialect.builtin(functionCall->functionName.name))
|
||||||
|
return !builtin->controlFlowSideEffects.canContinue;
|
||||||
|
else if (m_functionSideEffects && m_functionSideEffects->count(functionCall->functionName.name))
|
||||||
|
return !m_functionSideEffects->at(functionCall->functionName.name).canContinue;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -205,22 +205,31 @@ private:
|
|||||||
std::set<YulString> m_variableReferences;
|
std::set<YulString> m_variableReferences;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ControlFlowSideEffects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to find "irregular" control flow.
|
* Helper class to find "irregular" control flow.
|
||||||
* This includes termination, break and continue.
|
* This includes termination, break, continue and leave.
|
||||||
|
* In general, it is applied only to "simple" statements. The control-flow
|
||||||
|
* of loops, switches and if statements is always "FlowOut" with the assumption
|
||||||
|
* that the caller will descend into them.
|
||||||
*/
|
*/
|
||||||
class TerminationFinder
|
class TerminationFinder
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// TODO check all uses of TerminationFinder!
|
/// "Terminate" here means that there is no continuing control-flow.
|
||||||
|
/// If this is applied to a function that can revert or stop, but can also
|
||||||
|
/// exit regularly, the property is set to "FlowOut".
|
||||||
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
|
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
|
||||||
|
|
||||||
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
|
TerminationFinder(
|
||||||
|
Dialect const& _dialect,
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const* _functionSideEffects = nullptr
|
||||||
|
): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {}
|
||||||
|
|
||||||
/// @returns the index of the first statement in the provided sequence
|
/// @returns the index of the first statement in the provided sequence
|
||||||
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
|
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
|
||||||
/// call to a terminating builtin function.
|
/// call to a terminating function.
|
||||||
/// If control flow can continue at the end of the list,
|
/// If control flow can continue at the end of the list,
|
||||||
/// returns `FlowOut` and ``size_t(-1)``.
|
/// returns `FlowOut` and ``size_t(-1)``.
|
||||||
/// The function might return ``FlowOut`` even though control
|
/// The function might return ``FlowOut`` even though control
|
||||||
@ -233,13 +242,14 @@ public:
|
|||||||
/// This function could return FlowOut even if control flow never continues.
|
/// This function could return FlowOut even if control flow never continues.
|
||||||
ControlFlow controlFlowKind(Statement const& _statement);
|
ControlFlow controlFlowKind(Statement const& _statement);
|
||||||
|
|
||||||
/// @returns true if the expression statement is a direct
|
/// @returns true if the expression contains a
|
||||||
/// call to a builtin terminating function like
|
/// call to a terminating function, i.e. a function that does not have
|
||||||
/// ``stop``, ``revert`` or ``return``.
|
/// a regular "flow out" control-flow (it might also be recursive).
|
||||||
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
|
bool containsNonContinuingFunctionCall(Expression const& _expr);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Dialect const& m_dialect;
|
Dialect const& m_dialect;
|
||||||
|
std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
function recursive() { recursive() }
|
||||||
|
function terminating() { stop() }
|
||||||
|
function maybeReverting() { if calldataload(0) { revert(0, 0) } }
|
||||||
|
|
||||||
|
let a := calldataload(7)
|
||||||
|
if a { recursive() }
|
||||||
|
|
||||||
|
a := calldataload(a)
|
||||||
|
if a { maybeReverting() }
|
||||||
|
|
||||||
|
a := calldataload(a)
|
||||||
|
if a { terminating() }
|
||||||
|
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: conditionalSimplifier
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function recursive()
|
||||||
|
// { recursive() }
|
||||||
|
// function terminating()
|
||||||
|
// { stop() }
|
||||||
|
// function maybeReverting()
|
||||||
|
// {
|
||||||
|
// if calldataload(0) { revert(0, 0) }
|
||||||
|
// }
|
||||||
|
// let a := calldataload(7)
|
||||||
|
// if a { recursive() }
|
||||||
|
// a := 0
|
||||||
|
// a := calldataload(a)
|
||||||
|
// if a { maybeReverting() }
|
||||||
|
// a := calldataload(a)
|
||||||
|
// if a { terminating() }
|
||||||
|
// a := 0
|
||||||
|
// sstore(0, a)
|
||||||
|
// }
|
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
function recursive() { recursive() }
|
||||||
|
function terminating() { stop() }
|
||||||
|
function maybeReverting() { if calldataload(0) { revert(0, 0) } }
|
||||||
|
|
||||||
|
let a := calldataload(7)
|
||||||
|
if a { recursive() }
|
||||||
|
a := 0
|
||||||
|
|
||||||
|
a := calldataload(a)
|
||||||
|
if a { maybeReverting() }
|
||||||
|
|
||||||
|
a := calldataload(a)
|
||||||
|
if a { terminating() }
|
||||||
|
a := 0
|
||||||
|
|
||||||
|
sstore(0, a)
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: conditionalUnsimplifier
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// function recursive()
|
||||||
|
// { recursive() }
|
||||||
|
// function terminating()
|
||||||
|
// { stop() }
|
||||||
|
// function maybeReverting()
|
||||||
|
// {
|
||||||
|
// if calldataload(0) { revert(0, 0) }
|
||||||
|
// }
|
||||||
|
// let a := calldataload(7)
|
||||||
|
// if a { recursive() }
|
||||||
|
// a := calldataload(a)
|
||||||
|
// if a { maybeReverting() }
|
||||||
|
// a := calldataload(a)
|
||||||
|
// if a { terminating() }
|
||||||
|
// sstore(0, a)
|
||||||
|
// }
|
@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
switch calldataload(0)
|
||||||
|
case 0 {
|
||||||
|
recursive()
|
||||||
|
sstore(0, 1)
|
||||||
|
}
|
||||||
|
case 1 {
|
||||||
|
terminating()
|
||||||
|
sstore(0, 7)
|
||||||
|
}
|
||||||
|
case 2 {
|
||||||
|
reverting()
|
||||||
|
sstore(0, 7)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function recursive()
|
||||||
|
{
|
||||||
|
recursive()
|
||||||
|
}
|
||||||
|
function terminating()
|
||||||
|
{
|
||||||
|
return(0, 0)
|
||||||
|
}
|
||||||
|
function reverting()
|
||||||
|
{
|
||||||
|
revert(0, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ----
|
||||||
|
// step: deadCodeEliminator
|
||||||
|
//
|
||||||
|
// {
|
||||||
|
// switch calldataload(0)
|
||||||
|
// case 0 { recursive() }
|
||||||
|
// case 1 { terminating() }
|
||||||
|
// case 2 { reverting() }
|
||||||
|
// function recursive()
|
||||||
|
// { recursive() }
|
||||||
|
// function terminating()
|
||||||
|
// { return(0, 0) }
|
||||||
|
// function reverting()
|
||||||
|
// { revert(0, 0) }
|
||||||
|
// }
|
@ -5,9 +5,7 @@
|
|||||||
|
|
||||||
function fun()
|
function fun()
|
||||||
{
|
{
|
||||||
return(1, 1)
|
sstore(0, 1)
|
||||||
|
|
||||||
pop(sub(10, 5))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pop(add(1, 1))
|
pop(add(1, 1))
|
||||||
@ -19,5 +17,5 @@
|
|||||||
// fun()
|
// fun()
|
||||||
// revert(0, 0)
|
// revert(0, 0)
|
||||||
// function fun()
|
// function fun()
|
||||||
// { return(1, 1) }
|
// { sstore(0, 1) }
|
||||||
// }
|
// }
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
{
|
{
|
||||||
// This function name can be shortened, the other cannot.
|
// This function name can be shortened, the other cannot.
|
||||||
function nonmstore_(x) {
|
function nonmstore_(x) {
|
||||||
nonmstore_(x)
|
if calldataload(0) { nonmstore_(x) }
|
||||||
sstore(10, calldataload(2))
|
sstore(10, calldataload(2))
|
||||||
}
|
}
|
||||||
function mstore_(x) -> y {
|
function mstore_(x) -> y {
|
||||||
let t3_3_ := mstore_(x)
|
if calldataload(0) { let t3_3_ := mstore_(x) }
|
||||||
y := 8
|
y := 8
|
||||||
sstore(y, calldataload(y))
|
sstore(y, calldataload(y))
|
||||||
}
|
}
|
||||||
@ -22,12 +22,12 @@
|
|||||||
// }
|
// }
|
||||||
// function nonmstore(x)
|
// function nonmstore(x)
|
||||||
// {
|
// {
|
||||||
// nonmstore(x)
|
// if calldataload(0) { nonmstore(x) }
|
||||||
// sstore(10, calldataload(2))
|
// sstore(10, calldataload(2))
|
||||||
// }
|
// }
|
||||||
// function mstore_(x) -> y
|
// function mstore_(x) -> y
|
||||||
// {
|
// {
|
||||||
// pop(mstore_(x))
|
// if calldataload(0) { pop(mstore_(x)) }
|
||||||
// y := 8
|
// y := 8
|
||||||
// sstore(y, calldataload(y))
|
// sstore(y, calldataload(y))
|
||||||
// }
|
// }
|
||||||
|
@ -5,7 +5,9 @@
|
|||||||
sstore(1, l)
|
sstore(1, l)
|
||||||
function f(a, b, c) -> x, y, z
|
function f(a, b, c) -> x, y, z
|
||||||
{
|
{
|
||||||
x, y, z := f(1, 2, 3)
|
if calldataload(0) {
|
||||||
|
x, y, z := f(1, 2, 3)
|
||||||
|
}
|
||||||
x := add(x, 1)
|
x := add(x, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,9 +23,13 @@
|
|||||||
// }
|
// }
|
||||||
// function f() -> x, y, z
|
// function f() -> x, y, z
|
||||||
// {
|
// {
|
||||||
// let x_1, y_1, z_1 := f()
|
// if calldataload(0)
|
||||||
// y := y_1
|
// {
|
||||||
// z := z_1
|
// let x_1, y_1, z_1 := f()
|
||||||
// x := add(x_1, 1)
|
// x := x_1
|
||||||
|
// y := y_1
|
||||||
|
// z := z_1
|
||||||
|
// }
|
||||||
|
// x := add(x, 1)
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
Loading…
Reference in New Issue
Block a user