Skip to content

Commit 889c476

Browse files
committed
More logic about control flow with continue and about finalize. Remove BlockScope.
1 parent a1ec494 commit 889c476

File tree

10 files changed

+322
-67
lines changed

10 files changed

+322
-67
lines changed

libyul/optimiser/RedundantAssignEliminator.cpp

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -97,31 +97,30 @@ void RedundantAssignEliminator::operator()(FunctionDefinition const& _functionDe
9797
{
9898
std::set<YulString> outerDeclaredVariables;
9999
TrackedAssignments outerAssignments;
100+
ForLoopInfo forLoopInfo;
100101
swap(m_declaredVariables, outerDeclaredVariables);
101102
swap(m_assignments, outerAssignments);
103+
swap(m_forLoopInfo, forLoopInfo);
102104

103105
(*this)(_functionDefinition.body);
104106

105107
for (auto const& param: _functionDefinition.parameters)
106-
{
107-
changeUndecidedTo(param.name, State::Unused);
108-
finalize(param.name);
109-
}
108+
finalize(param.name, State::Unused);
110109
for (auto const& retParam: _functionDefinition.returnVariables)
111-
{
112-
changeUndecidedTo(retParam.name, State::Used);
113-
finalize(retParam.name);
114-
}
110+
finalize(retParam.name, State::Used);
115111

116112
swap(m_declaredVariables, outerDeclaredVariables);
117113
swap(m_assignments, outerAssignments);
114+
swap(m_forLoopInfo, forLoopInfo);
118115
}
119116

120117
void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
121118
{
122-
// This will set all variables that are declared in this
123-
// block to "unused" when it is destroyed.
124-
BlockScope scope(*this);
119+
ForLoopInfo outerForLoopInfo;
120+
swap(outerForLoopInfo, m_forLoopInfo);
121+
122+
set<YulString> outerDeclaredVariables;
123+
swap(m_declaredVariables, outerDeclaredVariables);
125124

126125
// We need to visit the statements directly because of the
127126
// scoping rules.
@@ -133,9 +132,6 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
133132

134133
visit(*_forLoop.condition);
135134

136-
ForLoopInfo outerForLoopInfo;
137-
swap(outerForLoopInfo, m_forLoopInfo);
138-
139135
TrackedAssignments zeroRuns{m_assignments};
140136

141137
(*this)(_forLoop.body);
@@ -150,6 +146,7 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
150146
(*this)(_forLoop.body);
151147

152148
merge(m_assignments, move(m_forLoopInfo.pendingContinueStmts));
149+
m_forLoopInfo.pendingContinueStmts = {};
153150
(*this)(_forLoop.post);
154151

155152
visit(*_forLoop.condition);
@@ -159,27 +156,37 @@ void RedundantAssignEliminator::operator()(ForLoop const& _forLoop)
159156
merge(m_assignments, move(zeroRuns));
160157
merge(m_assignments, move(m_forLoopInfo.pendingBreakStmts));
161158

162-
// Oestore potential outer for-loop states.
159+
for (auto const& var: m_declaredVariables)
160+
finalize(var, State::Unused);
161+
swap(m_declaredVariables, outerDeclaredVariables);
162+
163+
// Restore potential outer for-loop states.
163164
swap(m_forLoopInfo, outerForLoopInfo);
164165
}
165166

166167
void RedundantAssignEliminator::operator()(Break const&)
167168
{
168-
m_forLoopInfo.pendingBreakStmts.push_back(m_assignments);
169+
m_forLoopInfo.pendingBreakStmts.emplace_back(move(m_assignments));
170+
m_assignments.clear();
169171
}
170172

171173
void RedundantAssignEliminator::operator()(Continue const&)
172174
{
173-
m_forLoopInfo.pendingContinueStmts.push_back(m_assignments);
175+
m_forLoopInfo.pendingContinueStmts.emplace_back(move(m_assignments));
176+
m_assignments.clear();
174177
}
175178

176179
void RedundantAssignEliminator::operator()(Block const& _block)
177180
{
178-
// This will set all variables that are declared in this
179-
// block to "unused" when it is destroyed.
180-
BlockScope scope(*this);
181+
set<YulString> outerDeclaredVariables;
182+
swap(m_declaredVariables, outerDeclaredVariables);
181183

182184
ASTWalker::operator()(_block);
185+
186+
for (auto const& var: m_declaredVariables)
187+
finalize(var, State::Unused);
188+
189+
swap(m_declaredVariables, outerDeclaredVariables);
183190
}
184191

185192
void RedundantAssignEliminator::run(Dialect const& _dialect, Block& _ast)
@@ -239,21 +246,38 @@ void RedundantAssignEliminator::merge(TrackedAssignments& _target, vector<Tracke
239246
void RedundantAssignEliminator::changeUndecidedTo(YulString _variable, RedundantAssignEliminator::State _newState)
240247
{
241248
for (auto& assignment: m_assignments[_variable])
242-
if (assignment.second == State{State::Undecided})
249+
if (assignment.second == State::Undecided)
243250
assignment.second = _newState;
244251
}
245252

246-
void RedundantAssignEliminator::finalize(YulString _variable)
253+
void RedundantAssignEliminator::finalize(YulString _variable, RedundantAssignEliminator::State _finalState)
247254
{
248-
for (auto& assignment: m_assignments[_variable])
255+
finalize(m_assignments, _variable, _finalState);
256+
for (auto& assignments: m_forLoopInfo.pendingBreakStmts)
257+
finalize(assignments, _variable, _finalState);
258+
for (auto& assignments: m_forLoopInfo.pendingContinueStmts)
259+
finalize(assignments, _variable, _finalState);
260+
}
261+
262+
void RedundantAssignEliminator::finalize(
263+
TrackedAssignments& _assignments,
264+
YulString _variable,
265+
RedundantAssignEliminator::State _finalState
266+
)
267+
{
268+
for (auto const& assignment: _assignments[_variable])
249269
{
250-
assertThrow(assignment.second != State::Undecided, OptimizerException, "");
251-
if (assignment.second == State{State::Unused} && MovableChecker{*m_dialect, *assignment.first->value}.movable())
270+
State state = assignment.second;
271+
if (state == State::Undecided)
272+
state = _finalState;
273+
274+
assertThrow(state != State::Undecided, OptimizerException, "");
275+
if (state == State::Unused && MovableChecker{*m_dialect, *assignment.first->value}.movable())
252276
// TODO the only point where we actually need this
253277
// to be a set is for the for loop
254278
m_pendingRemovals.insert(assignment.first);
255279
}
256-
m_assignments.erase(_variable);
280+
_assignments.erase(_variable);
257281
}
258282

259283
void AssignmentRemover::operator()(Block& _block)

libyul/optimiser/RedundantAssignEliminator.h

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -137,33 +137,6 @@ class RedundantAssignEliminator: public ASTWalker
137137
Value m_value = Undecided;
138138
};
139139

140-
/**
141-
* Takes care about storing the list of declared variables and
142-
* sets them to "unused" when it is destroyed.
143-
*/
144-
class BlockScope
145-
{
146-
public:
147-
explicit BlockScope(RedundantAssignEliminator& _rae): m_rae(_rae)
148-
{
149-
swap(m_rae.m_declaredVariables, m_outerDeclaredVariables);
150-
}
151-
~BlockScope()
152-
{
153-
// This should actually store all declared variables
154-
// into a different mapping
155-
for (auto const& var: m_rae.m_declaredVariables)
156-
m_rae.changeUndecidedTo(var, State::Unused);
157-
for (auto const& var: m_rae.m_declaredVariables)
158-
m_rae.finalize(var);
159-
swap(m_rae.m_declaredVariables, m_outerDeclaredVariables);
160-
}
161-
162-
private:
163-
RedundantAssignEliminator& m_rae;
164-
std::set<YulString> m_outerDeclaredVariables;
165-
};
166-
167140
// TODO check that this does not cause nondeterminism!
168141
// This could also be a pseudo-map from state to assignment.
169142
using TrackedAssignments = std::map<YulString, std::map<Assignment const*, State>>;
@@ -174,7 +147,12 @@ class RedundantAssignEliminator: public ASTWalker
174147
static void merge(TrackedAssignments& _target, TrackedAssignments&& _source);
175148
static void merge(TrackedAssignments& _target, std::vector<TrackedAssignments>&& _source);
176149
void changeUndecidedTo(YulString _variable, State _newState);
177-
void finalize(YulString _variable);
150+
/// Called when a variables goes out of scope. Sets the state of all still undecided
151+
/// assignments to the final state. In this case, this also applies to pending
152+
/// break and continue TrackedAssignments.
153+
void finalize(YulString _variable, State _finalState);
154+
/// Helper function for the above.
155+
void finalize(TrackedAssignments& _assignments, YulString _variable, State _finalState);
178156

179157
Dialect const* m_dialect;
180158
std::set<YulString> m_declaredVariables;

test/libyul/yulOptimizerTests/redundantAssignEliminator/for_break.yul

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
2-
// Cannot be removed, because we might run the loop only once
3-
let x := 1
2+
let x
3+
// Cannot be removed, because we might skip the loop
4+
x := 1
45
for { } calldataload(0) { }
56
{
67
if callvalue() {
@@ -14,7 +15,8 @@
1415
// ----
1516
// redundantAssignEliminator
1617
// {
17-
// let x := 1
18+
// let x
19+
// x := 1
1820
// for {
1921
// }
2022
// calldataload(0)

test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue.yul

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
{
2-
// Cannot be removed, because we might run the loop only once
3-
let x := 1
2+
let x
3+
// Can be removed, because x is reassigned after the loop
4+
x := 1
45
for { } calldataload(0) { }
56
{
67
x := 2 // Will not be removed as if-condition can be false.
78
if callvalue() {
9+
// This can be removed because x is overwritten both after the
10+
// loop at at the start of the next iteration.
811
x := 3
912
continue
1013
}
@@ -15,7 +18,7 @@
1518
// ----
1619
// redundantAssignEliminator
1720
// {
18-
// let x := 1
21+
// let x
1922
// for {
2023
// }
2124
// calldataload(0)
@@ -25,7 +28,6 @@
2528
// x := 2
2629
// if callvalue()
2730
// {
28-
// x := 3
2931
// continue
3032
// }
3133
// mstore(x, 2)

test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_2.yul

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
2-
// Cannot be removed, because we might run the loop only once
3-
let x := 1
2+
let x
3+
// Cannot be removed, because we might skip the loop
4+
x := 1
45
for { } calldataload(0) { }
56
{
67
if callvalue() {
@@ -14,7 +15,8 @@
1415
// ----
1516
// redundantAssignEliminator
1617
// {
17-
// let x := 1
18+
// let x
19+
// x := 1
1820
// for {
1921
// }
2022
// calldataload(0)

test/libyul/yulOptimizerTests/redundantAssignEliminator/for_continue_3.yul

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{
2-
// Cannot be removed, because we might run the loop only once
3-
let x := 1
2+
let x
3+
// Can be removed, because x is not used after the loop.
4+
x := 1
45
for { } calldataload(0) { mstore(x, 0x42) }
56
{
67
if callvalue() {
@@ -13,7 +14,7 @@
1314
// ----
1415
// redundantAssignEliminator
1516
// {
16-
// let x := 1
17+
// let x
1718
// for {
1819
// }
1920
// calldataload(0)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
let x := 1
3+
for { } calldataload(0) { }
4+
{
5+
// This will go out of scope at the end of the block,
6+
// but the continue/break statements still refer to it.
7+
{
8+
let y := 9
9+
if callvalue() {
10+
y := 2 // will be removed
11+
break
12+
}
13+
if eq(callvalue(), 3) {
14+
y := 12 // will be removed
15+
continue
16+
}
17+
}
18+
}
19+
mstore(x, 0x42)
20+
}
21+
// ----
22+
// redundantAssignEliminator
23+
// {
24+
// let x := 1
25+
// for {
26+
// }
27+
// calldataload(0)
28+
// {
29+
// }
30+
// {
31+
// {
32+
// let y := 9
33+
// if callvalue()
34+
// {
35+
// break
36+
// }
37+
// if eq(callvalue(), 3)
38+
// {
39+
// continue
40+
// }
41+
// }
42+
// }
43+
// mstore(x, 0x42)
44+
// }

0 commit comments

Comments
 (0)