Skip to content

Commit a904964

Browse files
committed
Use side effects of user-defined functions in other optimizer steps.
1 parent 35b5a68 commit a904964

8 files changed

+85
-30
lines changed

libyul/optimiser/ConditionalSimplifier.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,20 @@
1919
#include <libyul/optimiser/Semantics.h>
2020
#include <libyul/AST.h>
2121
#include <libyul/optimiser/NameCollector.h>
22+
#include <libyul/ControlFlowSideEffectsCollector.h>
2223
#include <libsolutil/CommonData.h>
2324

2425
using namespace std;
2526
using namespace solidity;
2627
using namespace solidity::yul;
2728
using namespace solidity::util;
2829

30+
void ConditionalSimplifier::run(OptimiserStepContext& _context, Block& _ast)
31+
{
32+
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
33+
ConditionalSimplifier{_context.dialect, sideEffects.functionSideEffects()}(_ast);
34+
}
35+
2936
void ConditionalSimplifier::operator()(Switch& _switch)
3037
{
3138
visit(*_switch.expression);
@@ -65,7 +72,7 @@ void ConditionalSimplifier::operator()(Block& _block)
6572
if (
6673
holds_alternative<Identifier>(*_if.condition) &&
6774
!_if.body.statements.empty() &&
68-
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
75+
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
6976
TerminationFinder::ControlFlow::FlowOut
7077
)
7178
{

libyul/optimiser/ConditionalSimplifier.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,20 +54,21 @@ class ConditionalSimplifier: public ASTModifier
5454
{
5555
public:
5656
static constexpr char const* name{"ConditionalSimplifier"};
57-
static void run(OptimiserStepContext& _context, Block& _ast)
58-
{
59-
ConditionalSimplifier{_context.dialect}(_ast);
60-
}
57+
static void run(OptimiserStepContext& _context, Block& _ast);
6158

6259
using ASTModifier::operator();
6360
void operator()(Switch& _switch) override;
6461
void operator()(Block& _block) override;
6562

6663
private:
67-
explicit ConditionalSimplifier(Dialect const& _dialect):
68-
m_dialect(_dialect)
64+
explicit ConditionalSimplifier(
65+
Dialect const& _dialect,
66+
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
67+
):
68+
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
6969
{}
7070
Dialect const& m_dialect;
71+
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
7172
};
7273

7374
}

libyul/optimiser/ConditionalUnsimplifier.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,20 @@
2020
#include <libyul/AST.h>
2121
#include <libyul/Utilities.h>
2222
#include <libyul/optimiser/NameCollector.h>
23+
#include <libyul/ControlFlowSideEffectsCollector.h>
2324
#include <libsolutil/CommonData.h>
2425

2526
using namespace std;
2627
using namespace solidity;
2728
using namespace solidity::yul;
2829
using namespace solidity::util;
2930

31+
void ConditionalUnsimplifier::run(OptimiserStepContext& _context, Block& _ast)
32+
{
33+
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
34+
ConditionalUnsimplifier{_context.dialect, sideEffects.functionSideEffects()}(_ast);
35+
}
36+
3037
void ConditionalUnsimplifier::operator()(Switch& _switch)
3138
{
3239
visit(*_switch.expression);
@@ -78,7 +85,7 @@ void ConditionalUnsimplifier::operator()(Block& _block)
7885
YulString condition = std::get<Identifier>(*_if.condition).name;
7986
if (
8087
holds_alternative<Assignment>(_stmt2) &&
81-
TerminationFinder(m_dialect).controlFlowKind(_if.body.statements.back()) !=
88+
TerminationFinder(m_dialect, &m_functionSideEffects).controlFlowKind(_if.body.statements.back()) !=
8289
TerminationFinder::ControlFlow::FlowOut
8390
)
8491
{

libyul/optimiser/ConditionalUnsimplifier.h

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,21 @@ class ConditionalUnsimplifier: public ASTModifier
3333
{
3434
public:
3535
static constexpr char const* name{"ConditionalUnsimplifier"};
36-
static void run(OptimiserStepContext& _context, Block& _ast)
37-
{
38-
ConditionalUnsimplifier{_context.dialect}(_ast);
39-
}
36+
static void run(OptimiserStepContext& _context, Block& _ast);
4037

4138
using ASTModifier::operator();
4239
void operator()(Switch& _switch) override;
4340
void operator()(Block& _block) override;
4441

4542
private:
46-
explicit ConditionalUnsimplifier(Dialect const& _dialect):
47-
m_dialect(_dialect)
43+
explicit ConditionalUnsimplifier(
44+
Dialect const& _dialect,
45+
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
46+
):
47+
m_dialect(_dialect), m_functionSideEffects(_sideEffects)
4848
{}
4949
Dialect const& m_dialect;
50+
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
5051
};
5152

5253
}

libyul/optimiser/DeadCodeEliminator.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <libyul/optimiser/DeadCodeEliminator.h>
2323
#include <libyul/optimiser/Semantics.h>
2424
#include <libyul/optimiser/OptimiserStep.h>
25+
#include <libyul/ControlFlowSideEffectsCollector.h>
2526
#include <libyul/AST.h>
2627

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

3738
void DeadCodeEliminator::run(OptimiserStepContext& _context, Block& _ast)
3839
{
39-
DeadCodeEliminator{_context.dialect}(_ast);
40+
ControlFlowSideEffectsCollector sideEffects(_context.dialect, _ast);
41+
DeadCodeEliminator{
42+
_context.dialect,
43+
sideEffects.functionSideEffects()
44+
}(_ast);
4045
}
4146

4247
void DeadCodeEliminator::operator()(ForLoop& _for)
@@ -49,7 +54,7 @@ void DeadCodeEliminator::operator()(Block& _block)
4954
{
5055
TerminationFinder::ControlFlow controlFlowChange;
5156
size_t index;
52-
tie(controlFlowChange, index) = TerminationFinder{m_dialect}.firstUnconditionalControlFlowChange(_block.statements);
57+
tie(controlFlowChange, index) = TerminationFinder{m_dialect, &m_functionSideEffects}.firstUnconditionalControlFlowChange(_block.statements);
5358

5459
// Erase everything after the terminating statement that is not a function definition.
5560
if (controlFlowChange != TerminationFinder::ControlFlow::FlowOut && index != std::numeric_limits<size_t>::max())

libyul/optimiser/DeadCodeEliminator.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ namespace solidity::yul
3131
{
3232
struct Dialect;
3333
struct OptimiserStepContext;
34+
struct ControlFlowSideEffects;
3435

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

5960
private:
60-
DeadCodeEliminator(Dialect const& _dialect): m_dialect(_dialect) {}
61+
DeadCodeEliminator(
62+
Dialect const& _dialect,
63+
std::map<YulString, ControlFlowSideEffects> const& _sideEffects
64+
): m_dialect(_dialect), m_functionSideEffects(_sideEffects) {}
6165

6266
Dialect const& m_dialect;
67+
std::map<YulString, ControlFlowSideEffects> const& m_functionSideEffects;
6368
};
6469

6570
}

libyul/optimiser/Semantics.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,19 @@ pair<TerminationFinder::ControlFlow, size_t> TerminationFinder::firstUncondition
182182
TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement const& _statement)
183183
{
184184
if (
185+
holds_alternative<VariableDeclaration>(_statement) &&
186+
std::get<VariableDeclaration>(_statement).value &&
187+
containsNonContinuingFunctionCall(*std::get<VariableDeclaration>(_statement).value)
188+
)
189+
return ControlFlow::Terminate;
190+
else if (
191+
holds_alternative<Assignment>(_statement) &&
192+
containsNonContinuingFunctionCall(*std::get<Assignment>(_statement).value)
193+
)
194+
return ControlFlow::Terminate;
195+
else if (
185196
holds_alternative<ExpressionStatement>(_statement) &&
186-
isTerminatingBuiltin(std::get<ExpressionStatement>(_statement))
197+
containsNonContinuingFunctionCall(std::get<ExpressionStatement>(_statement).expression)
187198
)
188199
return ControlFlow::Terminate;
189200
else if (holds_alternative<Break>(_statement))
@@ -196,10 +207,18 @@ TerminationFinder::ControlFlow TerminationFinder::controlFlowKind(Statement cons
196207
return ControlFlow::FlowOut;
197208
}
198209

199-
bool TerminationFinder::isTerminatingBuiltin(ExpressionStatement const& _exprStmnt)
210+
bool TerminationFinder::containsNonContinuingFunctionCall(Expression const& _expr)
200211
{
201-
if (holds_alternative<FunctionCall>(_exprStmnt.expression))
202-
if (auto instruction = toEVMInstruction(m_dialect, std::get<FunctionCall>(_exprStmnt.expression).functionName.name))
203-
return evmasm::SemanticInformation::terminatesControlFlow(*instruction);
212+
if (auto functionCall = std::get_if<FunctionCall>(&_expr))
213+
{
214+
for (auto const& arg: functionCall->arguments)
215+
if (containsNonContinuingFunctionCall(arg))
216+
return true;
217+
218+
if (auto builtin = m_dialect.builtin(functionCall->functionName.name))
219+
return !builtin->controlFlowSideEffects.canContinue;
220+
else if (m_functionSideEffects && m_functionSideEffects->count(functionCall->functionName.name))
221+
return !m_functionSideEffects->at(functionCall->functionName.name).canContinue;
222+
}
204223
return false;
205224
}

libyul/optimiser/Semantics.h

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -205,22 +205,31 @@ class MovableChecker: public SideEffectsCollector
205205
std::set<YulString> m_variableReferences;
206206
};
207207

208+
struct ControlFlowSideEffects;
208209

209210
/**
210211
* Helper class to find "irregular" control flow.
211-
* This includes termination, break and continue.
212+
* This includes termination, break, continue and leave.
213+
* In general, it is applied only to "simple" statements. The control-flow
214+
* of loops, switches and if statements is always "FlowOut" with the assumption
215+
* that the caller will descend into them.
212216
*/
213217
class TerminationFinder
214218
{
215219
public:
216-
// TODO check all uses of TerminationFinder!
220+
/// "Terminate" here means that there is no continuing control-flow.
221+
/// If this is applied to a function that can revert or stop, but can also
222+
/// exit regularly, the property is set to "FlowOut".
217223
enum class ControlFlow { FlowOut, Break, Continue, Terminate, Leave };
218224

219-
TerminationFinder(Dialect const& _dialect): m_dialect(_dialect) {}
225+
TerminationFinder(
226+
Dialect const& _dialect,
227+
std::map<YulString, ControlFlowSideEffects> const* _functionSideEffects = nullptr
228+
): m_dialect(_dialect), m_functionSideEffects(_functionSideEffects) {}
220229

221230
/// @returns the index of the first statement in the provided sequence
222231
/// that is an unconditional ``break``, ``continue``, ``leave`` or a
223-
/// call to a terminating builtin function.
232+
/// call to a terminating function.
224233
/// If control flow can continue at the end of the list,
225234
/// returns `FlowOut` and ``size_t(-1)``.
226235
/// The function might return ``FlowOut`` even though control
@@ -233,13 +242,14 @@ class TerminationFinder
233242
/// This function could return FlowOut even if control flow never continues.
234243
ControlFlow controlFlowKind(Statement const& _statement);
235244

236-
/// @returns true if the expression statement is a direct
237-
/// call to a builtin terminating function like
238-
/// ``stop``, ``revert`` or ``return``.
239-
bool isTerminatingBuiltin(ExpressionStatement const& _exprStmnt);
245+
/// @returns true if the expression contains a
246+
/// call to a terminating function, i.e. a function that does not have
247+
/// a regular "flow out" control-flow (it might also be recursive).
248+
bool containsNonContinuingFunctionCall(Expression const& _expr);
240249

241250
private:
242251
Dialect const& m_dialect;
252+
std::map<YulString, ControlFlowSideEffects> const* m_functionSideEffects;
243253
};
244254

245255
}

0 commit comments

Comments
 (0)