Skip to content

Commit

Permalink
Use side effects of user-defined functions in other optimizer steps.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseth committed Oct 19, 2021
1 parent 863a0d3 commit 3149036
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 30 deletions.
9 changes: 8 additions & 1 deletion libyul/optimiser/ConditionalSimplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@
#include <libyul/optimiser/Semantics.h>
#include <libyul/AST.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libsolutil/CommonData.h>

using namespace std;
using namespace solidity;
using namespace solidity::yul;
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)
{
visit(*_switch.expression);
Expand Down Expand Up @@ -65,7 +72,7 @@ void ConditionalSimplifier::operator()(Block& _block)
if (
holds_alternative<Identifier>(*_if.condition) &&
!_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
)
{
Expand Down
13 changes: 7 additions & 6 deletions libyul/optimiser/ConditionalSimplifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,20 +54,21 @@ class ConditionalSimplifier: public ASTModifier
{
public:
static constexpr char const* name{"ConditionalSimplifier"};
static void run(OptimiserStepContext& _context, Block& _ast)
{
ConditionalSimplifier{_context.dialect}(_ast);
}
static void run(OptimiserStepContext& _context, Block& _ast);

using ASTModifier::operator();
void operator()(Switch& _switch) override;
void operator()(Block& _block) override;

private:
explicit ConditionalSimplifier(Dialect const& _dialect):
m_dialect(_dialect)
explicit ConditionalSimplifier(
Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
):
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
{}
Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
};

}
9 changes: 8 additions & 1 deletion libyul/optimiser/ConditionalUnsimplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,20 @@
#include <libyul/AST.h>
#include <libyul/Utilities.h>
#include <libyul/optimiser/NameCollector.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libsolutil/CommonData.h>

using namespace std;
using namespace solidity;
using namespace solidity::yul;
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)
{
visit(*_switch.expression);
Expand Down Expand Up @@ -78,7 +85,7 @@ void ConditionalUnsimplifier::operator()(Block& _block)
YulString condition = std::get<Identifier>(*_if.condition).name;
if (
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
)
{
Expand Down
13 changes: 7 additions & 6 deletions libyul/optimiser/ConditionalUnsimplifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,21 @@ class ConditionalUnsimplifier: public ASTModifier
{
public:
static constexpr char const* name{"ConditionalUnsimplifier"};
static void run(OptimiserStepContext& _context, Block& _ast)
{
ConditionalUnsimplifier{_context.dialect}(_ast);
}
static void run(OptimiserStepContext& _context, Block& _ast);

using ASTModifier::operator();
void operator()(Switch& _switch) override;
void operator()(Block& _block) override;

private:
explicit ConditionalUnsimplifier(Dialect const& _dialect):
m_dialect(_dialect)
explicit ConditionalUnsimplifier(
Dialect const& _dialect,
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
):
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
{}
Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
};

}
9 changes: 7 additions & 2 deletions libyul/optimiser/DeadCodeEliminator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include <libyul/optimiser/DeadCodeEliminator.h>
#include <libyul/optimiser/Semantics.h>
#include <libyul/optimiser/OptimiserStep.h>
#include <libyul/ControlFlowSideEffectsCollector.h>
#include <libyul/AST.h>

#include <libevmasm/SemanticInformation.h>
Expand All @@ -36,7 +37,11 @@ using namespace solidity::yul;

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)
Expand All @@ -49,7 +54,7 @@ void DeadCodeEliminator::operator()(Block& _block)
{
TerminationFinder::ControlFlow controlFlowChange;
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.
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max())
Expand Down
7 changes: 6 additions & 1 deletion libyul/optimiser/DeadCodeEliminator.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace solidity::yul
{
struct Dialect;
struct OptimiserStepContext;
struct ControlFlowSideEffects;

/**
* Optimisation stage that removes unreachable code
Expand All @@ -57,9 +58,13 @@ class DeadCodeEliminator: public ASTModifier
void operator()(Block& _block) override;

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;
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
};

}
29 changes: 24 additions & 5 deletions libyul/optimiser/Semantics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
{
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) &&
isTerminatingBuiltin(std::get<ExpressionStatement>(_statement))
containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression)
)
return ControlFlow::Terminate;
else if (holds_alternative<Break>(_statement))
Expand All @@ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
return ControlFlow::FlowOut;
}

bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr)
{
if (holds_alternative<FunctionCall>(_exprStmnt.expression))
if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name))
return evmasm::SemanticInformation::terminatesControlFlow(*instruction);
if (auto functionCall = std::get_if<FunctionCall>(&_expr))
{
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;
}
26 changes: 18 additions & 8 deletions libyul/optimiser/Semantics.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,22 +205,31 @@ class MovableChecker: public SideEffectsCollector
std::set<YulString> m_variableReferences;
};

struct ControlFlowSideEffects;

/**
* 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
{
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 };

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
/// 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,
/// returns `FlowOut` and ``size_t(-1)``.
/// The function might return ``FlowOut`` even though control
Expand All @@ -233,13 +242,14 @@ class TerminationFinder
/// This function could return FlowOut even if control flow never continues.
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``.
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
/// @returns true if the expression contains a
/// call to a terminating function, i.e. a function that does not have
/// a regular "flow out" control-flow (it might also be recursive).
bool containsNonContinuingFunctionCall(Expression const& _expr);

private:
Dialect const& m_dialect;
std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects;
};

}

0 comments on commit 3149036

Please sign in to comment.