@@ -38,7 +38,7 @@ void RedundantAssignEliminator::run(OptimiserStepContext& _context, Block& _ast)
38
38
RedundantAssignEliminator rae{_context.dialect };
39
39
rae (_ast);
40
40
41
- AssignmentRemover remover{rae.m_pendingRemovals };
41
+ StatementRemover remover{rae.m_pendingRemovals };
42
42
remover (_ast);
43
43
}
44
44
@@ -49,7 +49,7 @@ void RedundantAssignEliminator::operator()(Identifier const& _identifier)
49
49
50
50
void RedundantAssignEliminator::operator ()(VariableDeclaration const & _variableDeclaration)
51
51
{
52
- ASTWalker ::operator ()(_variableDeclaration);
52
+ RedundantStoreBase ::operator ()(_variableDeclaration);
53
53
54
54
for (auto const & var: _variableDeclaration.variables )
55
55
m_declaredVariables.emplace (var.name );
@@ -60,151 +60,17 @@ void RedundantAssignEliminator::operator()(Assignment const& _assignment)
60
60
visit (*_assignment.value );
61
61
for (auto const & var: _assignment.variableNames )
62
62
changeUndecidedTo (var.name , State::Unused);
63
-
64
- if (_assignment.variableNames .size () == 1 )
65
- // Default-construct it in "Undecided" state if it does not yet exist.
66
- m_assignments[_assignment.variableNames .front ().name ][&_assignment];
67
- }
68
-
69
- void RedundantAssignEliminator::operator ()(If const & _if)
70
- {
71
- visit (*_if.condition );
72
-
73
- TrackedAssignments skipBranch{m_assignments};
74
- (*this )(_if.body );
75
-
76
- merge (m_assignments, move (skipBranch));
77
- }
78
-
79
- void RedundantAssignEliminator::operator ()(Switch const & _switch)
80
- {
81
- visit (*_switch.expression );
82
-
83
- TrackedAssignments const preState{m_assignments};
84
-
85
- bool hasDefault = false ;
86
- vector<TrackedAssignments> branches;
87
- for (auto const & c: _switch.cases )
88
- {
89
- if (!c.value )
90
- hasDefault = true ;
91
- (*this )(c.body );
92
- branches.emplace_back (move (m_assignments));
93
- m_assignments = preState;
94
- }
95
-
96
- if (hasDefault)
97
- {
98
- m_assignments = move (branches.back ());
99
- branches.pop_back ();
100
- }
101
- for (auto & branch: branches)
102
- merge (m_assignments, move (branch));
103
63
}
104
64
105
65
void RedundantAssignEliminator::operator ()(FunctionDefinition const & _functionDefinition)
106
66
{
107
- std::set<YulString> outerDeclaredVariables;
108
- std::set<YulString> outerReturnVariables;
109
- TrackedAssignments outerAssignments;
110
- ForLoopInfo forLoopInfo;
111
- swap (m_declaredVariables, outerDeclaredVariables);
112
- swap (m_returnVariables, outerReturnVariables);
113
- swap (m_assignments, outerAssignments);
114
- swap (m_forLoopInfo, forLoopInfo);
67
+ ScopedSaveAndRestore outerDeclaredVariables (m_declaredVariables, {});
68
+ ScopedSaveAndRestore outerReturnVariables (m_returnVariables, {});
115
69
116
70
for (auto const & retParam: _functionDefinition.returnVariables )
117
71
m_returnVariables.insert (retParam.name );
118
72
119
- (*this )(_functionDefinition.body );
120
-
121
- for (auto const & param: _functionDefinition.parameters )
122
- finalize (param.name , State::Unused);
123
- for (auto const & retParam: _functionDefinition.returnVariables )
124
- finalize (retParam.name , State::Used);
125
-
126
- swap (m_declaredVariables, outerDeclaredVariables);
127
- swap (m_returnVariables, outerReturnVariables);
128
- swap (m_assignments, outerAssignments);
129
- swap (m_forLoopInfo, forLoopInfo);
130
- }
131
-
132
- void RedundantAssignEliminator::operator ()(ForLoop const & _forLoop)
133
- {
134
- ForLoopInfo outerForLoopInfo;
135
- swap (outerForLoopInfo, m_forLoopInfo);
136
- ++m_forLoopNestingDepth;
137
-
138
- // If the pre block was not empty,
139
- // we would have to deal with more complicated scoping rules.
140
- assertThrow (_forLoop.pre .statements .empty (), OptimizerException, " " );
141
-
142
- // We just run the loop twice to account for the back edge.
143
- // There need not be more runs because we only have three different states.
144
-
145
- visit (*_forLoop.condition );
146
-
147
- TrackedAssignments zeroRuns{m_assignments};
148
-
149
- (*this )(_forLoop.body );
150
- merge (m_assignments, move (m_forLoopInfo.pendingContinueStmts ));
151
- m_forLoopInfo.pendingContinueStmts = {};
152
- (*this )(_forLoop.post );
153
-
154
- visit (*_forLoop.condition );
155
-
156
- if (m_forLoopNestingDepth < 6 )
157
- {
158
- // Do the second run only for small nesting depths to avoid horrible runtime.
159
- TrackedAssignments oneRun{m_assignments};
160
-
161
- (*this )(_forLoop.body );
162
-
163
- merge (m_assignments, move (m_forLoopInfo.pendingContinueStmts ));
164
- m_forLoopInfo.pendingContinueStmts .clear ();
165
- (*this )(_forLoop.post );
166
-
167
- visit (*_forLoop.condition );
168
- // Order of merging does not matter because "max" is commutative and associative.
169
- merge (m_assignments, move (oneRun));
170
- }
171
- else
172
- {
173
- // Shortcut to avoid horrible runtime:
174
- // Change all assignments that were newly introduced in the for loop to "used".
175
- // We do not have to do that with the "break" or "continue" paths, because
176
- // they will be joined later anyway.
177
- // TODO parallel traversal might be more efficient here.
178
- for (auto & var: m_assignments)
179
- for (auto & assignment: var.second )
180
- {
181
- auto zeroIt = zeroRuns.find (var.first );
182
- if (zeroIt != zeroRuns.end () && zeroIt->second .count (assignment.first ))
183
- continue ;
184
- assignment.second = State::Value::Used;
185
- }
186
- }
187
-
188
- // Order of merging does not matter because "max" is commutative and associative.
189
- merge (m_assignments, move (zeroRuns));
190
- merge (m_assignments, move (m_forLoopInfo.pendingBreakStmts ));
191
- m_forLoopInfo.pendingBreakStmts .clear ();
192
-
193
- // Restore potential outer for-loop states.
194
- swap (m_forLoopInfo, outerForLoopInfo);
195
- --m_forLoopNestingDepth;
196
- }
197
-
198
- void RedundantAssignEliminator::operator ()(Break const &)
199
- {
200
- m_forLoopInfo.pendingBreakStmts .emplace_back (move (m_assignments));
201
- m_assignments.clear ();
202
- }
203
-
204
- void RedundantAssignEliminator::operator ()(Continue const &)
205
- {
206
- m_forLoopInfo.pendingContinueStmts .emplace_back (move (m_assignments));
207
- m_assignments.clear ();
73
+ RedundantStoreBase::operator ()(_functionDefinition);
208
74
}
209
75
210
76
void RedundantAssignEliminator::operator ()(Leave const &)
@@ -215,101 +81,76 @@ void RedundantAssignEliminator::operator()(Leave const&)
215
81
216
82
void RedundantAssignEliminator::operator ()(Block const & _block)
217
83
{
218
- set<YulString> outerDeclaredVariables;
219
- swap (m_declaredVariables, outerDeclaredVariables);
84
+ ScopedSaveAndRestore outerDeclaredVariables (m_declaredVariables, {});
220
85
221
- ASTWalker ::operator ()(_block);
86
+ RedundantStoreBase ::operator ()(_block);
222
87
223
88
for (auto const & var: m_declaredVariables)
224
89
finalize (var, State::Unused);
225
-
226
- swap (m_declaredVariables, outerDeclaredVariables);
227
90
}
228
91
229
-
230
- template <class K , class V , class F >
231
- void joinMap (std::map<K, V>& _a, std::map<K, V>&& _b, F _conflictSolver)
92
+ void RedundantAssignEliminator::visit (Statement const & _statement)
232
93
{
233
- // TODO Perhaps it is better to just create a sorted list
234
- // and then use insert(begin, end)
94
+ RedundantStoreBase::visit (_statement);
235
95
236
- auto ita = _a.begin ();
237
- auto aend = _a.end ();
238
- auto itb = _b.begin ();
239
- auto bend = _b.end ();
240
-
241
- for (; itb != bend; ++ita)
242
- {
243
- if (ita == aend)
244
- ita = _a.insert (ita, std::move (*itb++));
245
- else if (ita->first < itb->first )
246
- continue ;
247
- else if (itb->first < ita->first )
248
- ita = _a.insert (ita, std::move (*itb++));
249
- else
250
- {
251
- _conflictSolver (ita->second , std::move (itb->second ));
252
- ++itb;
253
- }
254
- }
96
+ if (auto const * assignment = get_if<Assignment>(&_statement))
97
+ if (assignment->variableNames .size () == 1 )
98
+ // Default-construct it in "Undecided" state if it does not yet exist.
99
+ m_stores[assignment->variableNames .front ().name ][&_statement];
255
100
}
256
101
257
- void RedundantAssignEliminator::merge (TrackedAssignments& _target, TrackedAssignments&& _other )
102
+ void RedundantAssignEliminator::shortcutNestedLoop (TrackedStores const & _zeroRuns )
258
103
{
259
- joinMap (_target, move (_other), [](
260
- map<Assignment const *, State>& _assignmentHere,
261
- map<Assignment const *, State>&& _assignmentThere
262
- )
263
- {
264
- return joinMap (_assignmentHere, move (_assignmentThere), State::join);
265
- });
104
+ // Shortcut to avoid horrible runtime:
105
+ // Change all assignments that were newly introduced in the for loop to "used".
106
+ // We do not have to do that with the "break" or "continue" paths, because
107
+ // they will be joined later anyway.
108
+ // TODO parallel traversal might be more efficient here.
109
+ for (auto & [variable, stores]: m_stores)
110
+ for (auto & assignment: stores)
111
+ {
112
+ auto zeroIt = _zeroRuns.find (variable);
113
+ if (zeroIt != _zeroRuns.end () && zeroIt->second .count (assignment.first ))
114
+ continue ;
115
+ assignment.second = State::Value::Used;
116
+ }
266
117
}
267
118
268
- void RedundantAssignEliminator::merge (TrackedAssignments& _target, vector<TrackedAssignments>&& _source )
119
+ void RedundantAssignEliminator::finalizeFunctionDefinition (FunctionDefinition const & _functionDefinition )
269
120
{
270
- for (TrackedAssignments& ts: _source)
271
- merge (_target, move (ts));
272
- _source.clear ();
121
+ for (auto const & param: _functionDefinition.parameters )
122
+ finalize (param.name , State::Unused);
123
+ for (auto const & retParam: _functionDefinition.returnVariables )
124
+ finalize (retParam.name , State::Used);
273
125
}
274
126
275
127
void RedundantAssignEliminator::changeUndecidedTo (YulString _variable, RedundantAssignEliminator::State _newState)
276
128
{
277
- for (auto & assignment: m_assignments [_variable])
129
+ for (auto & assignment: m_stores [_variable])
278
130
if (assignment.second == State::Undecided)
279
131
assignment.second = _newState;
280
132
}
281
133
282
134
void RedundantAssignEliminator::finalize (YulString _variable, RedundantAssignEliminator::State _finalState)
283
135
{
284
- std::map<Assignment const *, State> assignments;
285
- joinMap (assignments, std::move (m_assignments[_variable]), State::join);
286
- m_assignments.erase (_variable);
136
+ std::map<Statement const *, State> stores = std::move (m_stores[_variable]);
137
+ m_stores.erase (_variable);
287
138
288
139
for (auto & breakAssignments: m_forLoopInfo.pendingBreakStmts )
289
140
{
290
- joinMap (assignments , std::move (breakAssignments[_variable]), State::join);
141
+ util:: joinMap (stores , std::move (breakAssignments[_variable]), State::join);
291
142
breakAssignments.erase (_variable);
292
143
}
293
144
for (auto & continueAssignments: m_forLoopInfo.pendingContinueStmts )
294
145
{
295
- joinMap (assignments , std::move (continueAssignments[_variable]), State::join);
146
+ util:: joinMap (stores , std::move (continueAssignments[_variable]), State::join);
296
147
continueAssignments.erase (_variable);
297
148
}
298
149
299
- for (auto const & assignment: assignments)
300
- {
301
- State const state = assignment.second == State::Undecided ? _finalState : assignment.second ;
302
-
303
- if (state == State::Unused && SideEffectsCollector{*m_dialect, *assignment.first ->value }.movable ())
304
- m_pendingRemovals.insert (assignment.first );
305
- }
306
- }
307
-
308
- void AssignmentRemover::operator ()(Block& _block)
309
- {
310
- ranges::actions::remove_if (_block.statements , [&](Statement const & _statement) -> bool {
311
- return holds_alternative<Assignment>(_statement) && m_toRemove.count (&std::get<Assignment>(_statement));
312
- });
313
-
314
- ASTModifier::operator ()(_block);
150
+ for (auto && [statement, state]: stores)
151
+ if (
152
+ (state == State::Unused || (state == State::Undecided && _finalState == State::Unused)) &&
153
+ SideEffectsCollector{m_dialect, *std::get<Assignment>(*statement).value }.movable ()
154
+ )
155
+ m_pendingRemovals.insert (statement);
315
156
}
0 commit comments