@@ -50,31 +50,41 @@ namespace
50
50
/* *
51
51
* Class that discovers all variables that can be fully eliminated by rematerialization,
52
52
* and the corresponding approximate costs.
53
+ *
54
+ * Prerequisite: Disambiguator, Function Grouper
53
55
*/
54
56
class RematCandidateSelector : public DataFlowAnalyzer
55
57
{
56
58
public:
57
59
explicit RematCandidateSelector (Dialect const & _dialect): DataFlowAnalyzer(_dialect) {}
58
60
59
- // / @returns a map from rematerialisation costs to a vector of variables to rematerialise
61
+ // / @returns a map from function name to rematerialisation costs to a vector of variables to rematerialise
60
62
// / and variables that occur in their expression.
61
63
// / While the map is sorted by cost, the contained vectors are sorted by the order of occurrence.
62
- map<size_t , vector<tuple<YulString, set<YulString>>>> candidates ()
64
+ map<YulString, map< size_t , vector<tuple<YulString, set<YulString> >>>> candidates ()
63
65
{
64
- map<size_t , vector<tuple<YulString, set<YulString>>>> cand;
65
- for (auto const & candidate: m_candidates)
66
+ map<YulString, map< size_t , vector<tuple<YulString, set<YulString> >>>> cand;
67
+ for (auto const & [functionName, candidate] : m_candidates)
66
68
{
67
69
if (size_t const * cost = util::valueOrNullptr (m_expressionCodeCost, candidate))
68
70
{
69
71
size_t numRef = m_numReferences[candidate];
70
72
set<YulString> const * ref = references (candidate);
71
- cand[*cost * numRef].emplace_back (candidate, ref ? move (*ref) : set<YulString>{});
73
+ cand[functionName][ *cost * numRef].emplace_back (candidate, ref ? move (*ref) : set<YulString>{});
72
74
}
73
75
}
74
76
return cand;
75
77
}
76
78
77
79
using DataFlowAnalyzer::operator ();
80
+ void operator ()(FunctionDefinition& _function) override
81
+ {
82
+ yulAssert (m_currentFunctionName.empty ());
83
+ m_currentFunctionName = _function.name ;
84
+ DataFlowAnalyzer::operator ()(_function);
85
+ m_currentFunctionName = {};
86
+ }
87
+
78
88
void operator ()(VariableDeclaration& _varDecl) override
79
89
{
80
90
DataFlowAnalyzer::operator ()(_varDecl);
@@ -84,7 +94,7 @@ class RematCandidateSelector: public DataFlowAnalyzer
84
94
if (AssignedValue const * value = variableValue (varName))
85
95
{
86
96
yulAssert (!m_expressionCodeCost.count (varName), " " );
87
- m_candidates.emplace_back (varName);
97
+ m_candidates.emplace_back (m_currentFunctionName, varName);
88
98
m_expressionCodeCost[varName] = CodeCost::codeCost (m_dialect, *value->value );
89
99
}
90
100
}
@@ -122,8 +132,10 @@ class RematCandidateSelector: public DataFlowAnalyzer
122
132
m_expressionCodeCost.erase (_variable);
123
133
}
124
134
125
- // / All candidate variables in order of occurrence.
126
- vector<YulString> m_candidates;
135
+ YulString m_currentFunctionName = {};
136
+
137
+ // / All candidate variables by function name, in order of occurrence.
138
+ vector<pair<YulString, YulString>> m_candidates;
127
139
// / Candidate variables and the code cost of their value.
128
140
map<YulString, size_t > m_expressionCodeCost;
129
141
// / Number of references to each candidate variable.
@@ -156,62 +168,80 @@ set<YulString> chooseVarsToEliminate(
156
168
return varsToEliminate;
157
169
}
158
170
159
- template <typename ASTNode>
160
171
void eliminateVariables (
161
172
Dialect const & _dialect,
162
- ASTNode& _node ,
163
- size_t _numVariables,
173
+ Block& _ast ,
174
+ map<YulString, int > const & _numVariables,
164
175
bool _allowMSizeOptimization
165
176
)
166
177
{
167
178
RematCandidateSelector selector{_dialect};
168
- selector (_node);
169
- Rematerialiser::run (_dialect, _node, chooseVarsToEliminate (selector.candidates (), _numVariables));
170
- UnusedPruner::runUntilStabilised (_dialect, _node, _allowMSizeOptimization);
179
+ selector (_ast);
180
+ map<YulString, map<size_t , vector<tuple<YulString, set<YulString>>>>> candidates = selector.candidates ();
181
+
182
+ set<YulString> varsToEliminate;
183
+ for (auto const & [functionName, numVariables]: _numVariables)
184
+ {
185
+ yulAssert (numVariables > 0 );
186
+ varsToEliminate += chooseVarsToEliminate (candidates[functionName], static_cast <size_t >(numVariables));
187
+ }
188
+
189
+ Rematerialiser::run (_dialect, _ast, move (varsToEliminate));
190
+ // Do not remove functions.
191
+ set<YulString> allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names ();
192
+ UnusedPruner::runUntilStabilised (_dialect, _ast, _allowMSizeOptimization, nullptr , allFunctions);
171
193
}
172
194
173
- void eliminateVariables (
195
+ void eliminateVariablesOptimizedCodegen (
174
196
Dialect const & _dialect,
175
- Block& _block ,
176
- vector<StackLayoutGenerator::StackTooDeep> const & _unreachables,
197
+ Block& _ast ,
198
+ map<YulString, vector<StackLayoutGenerator::StackTooDeep> > const & _unreachables,
177
199
bool _allowMSizeOptimization
178
200
)
179
201
{
202
+ if (std::all_of (_unreachables.begin (), _unreachables.end (), [](auto const & _item) { return _item.second .empty (); }))
203
+ return ;
204
+
180
205
RematCandidateSelector selector{_dialect};
181
- selector (_block);
182
- std::map<YulString, size_t > candidates;
183
- for (auto [cost, candidatesWithCost]: selector.candidates ())
184
- for (auto candidate: candidatesWithCost)
185
- candidates[get<0 >(candidate)] = cost;
206
+ selector (_ast);
207
+
208
+ map<YulString, size_t > candidates;
209
+ for (auto const & [functionName, candidatesInFunction]: selector.candidates ())
210
+ for (auto [cost, candidatesWithCost]: candidatesInFunction)
211
+ for (auto candidate: candidatesWithCost)
212
+ candidates[get<0 >(candidate)] = cost;
186
213
187
214
set<YulString> varsToEliminate;
188
215
189
216
// TODO: this currently ignores the fact that variables may reference other variables we want to eliminate.
190
- for (auto const & unreachable: _unreachables)
191
- {
192
- map<size_t , vector<YulString>> suitableCandidates;
193
- size_t neededSlots = unreachable.deficit ;
194
- for (auto varName: unreachable.variableChoices )
195
- {
196
- if (varsToEliminate.count (varName))
197
- --neededSlots;
198
- else if (size_t * cost = util::valueOrNullptr (candidates, varName))
199
- if (!util::contains (suitableCandidates[*cost], varName))
200
- suitableCandidates[*cost].emplace_back (varName);
201
- }
202
- for (auto candidatesByCost: suitableCandidates)
217
+ for (auto const & [functionName, unreachables]: _unreachables)
218
+ for (auto const & unreachable: unreachables)
203
219
{
204
- for (auto candidate: candidatesByCost.second )
205
- if (neededSlots--)
206
- varsToEliminate.emplace (candidate);
207
- else
220
+ map<size_t , vector<YulString>> suitableCandidates;
221
+ size_t neededSlots = unreachable.deficit ;
222
+ for (auto varName: unreachable.variableChoices )
223
+ {
224
+ if (varsToEliminate.count (varName))
225
+ --neededSlots;
226
+ else if (size_t * cost = util::valueOrNullptr (candidates, varName))
227
+ if (!util::contains (suitableCandidates[*cost], varName))
228
+ suitableCandidates[*cost].emplace_back (varName);
229
+ }
230
+ for (auto candidatesByCost: suitableCandidates)
231
+ {
232
+ for (auto candidate: candidatesByCost.second )
233
+ if (neededSlots--)
234
+ varsToEliminate.emplace (candidate);
235
+ else
236
+ break ;
237
+ if (!neededSlots)
208
238
break ;
209
- if (!neededSlots)
210
- break ;
239
+ }
211
240
}
212
- }
213
- Rematerialiser::run (_dialect, _block, std::move (varsToEliminate), true );
214
- UnusedPruner::runUntilStabilised (_dialect, _block, _allowMSizeOptimization);
241
+ Rematerialiser::run (_dialect, _ast, std::move (varsToEliminate), true );
242
+ // Do not remove functions.
243
+ set<YulString> allFunctions = NameCollector{_ast, NameCollector::OnlyFunctions}.names ();
244
+ UnusedPruner::runUntilStabilised (_dialect, _ast, _allowMSizeOptimization, nullptr , allFunctions);
215
245
}
216
246
217
247
}
@@ -239,54 +269,25 @@ bool StackCompressor::run(
239
269
{
240
270
yul::AsmAnalysisInfo analysisInfo = yul::AsmAnalyzer::analyzeStrictAssertCorrect (_dialect, _object);
241
271
unique_ptr<CFG> cfg = ControlFlowGraphBuilder::build (analysisInfo, _dialect, *_object.code );
242
- Block& mainBlock = std::get<Block>(_object.code ->statements .at (0 ));
243
- if (
244
- auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep (*cfg, YulString{});
245
- !stackTooDeepErrors.empty ()
246
- )
247
- eliminateVariables (_dialect, mainBlock, stackTooDeepErrors, allowMSizeOptimzation);
248
- for (size_t i = 1 ; i < _object.code ->statements .size (); ++i)
249
- {
250
- auto & fun = std::get<FunctionDefinition>(_object.code ->statements [i]);
251
- if (
252
- auto stackTooDeepErrors = StackLayoutGenerator::reportStackTooDeep (*cfg, fun.name );
253
- !stackTooDeepErrors.empty ()
254
- )
255
- eliminateVariables (_dialect, fun.body , stackTooDeepErrors, allowMSizeOptimzation);
256
- }
272
+ eliminateVariablesOptimizedCodegen (
273
+ _dialect,
274
+ *_object.code ,
275
+ StackLayoutGenerator::reportStackTooDeep (*cfg),
276
+ allowMSizeOptimzation
277
+ );
257
278
}
258
279
else
259
280
for (size_t iterations = 0 ; iterations < _maxIterations; iterations++)
260
281
{
261
282
map<YulString, int > stackSurplus = CompilabilityChecker (_dialect, _object, _optimizeStackAllocation).stackDeficit ;
262
283
if (stackSurplus.empty ())
263
284
return true ;
264
-
265
- if (stackSurplus.count (YulString{}))
266
- {
267
- yulAssert (stackSurplus.at ({}) > 0 , " Invalid surplus value." );
268
- eliminateVariables (
269
- _dialect,
270
- std::get<Block>(_object.code ->statements .at (0 )),
271
- static_cast <size_t >(stackSurplus.at ({})),
272
- allowMSizeOptimzation
273
- );
274
- }
275
-
276
- for (size_t i = 1 ; i < _object.code ->statements .size (); ++i)
277
- {
278
- auto & fun = std::get<FunctionDefinition>(_object.code ->statements [i]);
279
- if (!stackSurplus.count (fun.name ))
280
- continue ;
281
-
282
- yulAssert (stackSurplus.at (fun.name ) > 0 , " Invalid surplus value." );
283
- eliminateVariables (
284
- _dialect,
285
- fun,
286
- static_cast <size_t >(stackSurplus.at (fun.name )),
287
- allowMSizeOptimzation
288
- );
289
- }
285
+ eliminateVariables (
286
+ _dialect,
287
+ *_object.code ,
288
+ stackSurplus,
289
+ allowMSizeOptimzation
290
+ );
290
291
}
291
292
return false ;
292
293
}
0 commit comments