20
20
21
21
#include < libyul/AST.h>
22
22
#include < libyul/Dialect.h>
23
+ #include < libyul/FunctionReferenceResolver.h>
23
24
24
25
#include < libsolutil/Common.h>
25
26
#include < libsolutil/CommonData.h>
@@ -35,16 +36,15 @@ using namespace solidity::yul;
35
36
36
37
ControlFlowBuilder::ControlFlowBuilder (Block const & _ast)
37
38
{
38
- for (auto const & statement: _ast.statements )
39
- if (auto const * function = get_if<FunctionDefinition>(&statement))
40
- (*this )(*function);
39
+ m_currentNode = newNode ();
40
+ (*this )(_ast);
41
41
}
42
42
43
43
void ControlFlowBuilder::operator ()(FunctionCall const & _functionCall)
44
44
{
45
45
walkVector (_functionCall.arguments | ranges::views::reverse);
46
46
newConnectedNode ();
47
- m_currentNode->functionCall = _functionCall. functionName . name ;
47
+ m_currentNode->functionCall = & _functionCall;
48
48
}
49
49
50
50
void ControlFlowBuilder::operator ()(If const & _if)
@@ -78,7 +78,9 @@ void ControlFlowBuilder::operator()(Switch const& _switch)
78
78
void ControlFlowBuilder::operator ()(FunctionDefinition const & _function)
79
79
{
80
80
ScopedSaveAndRestore currentNode (m_currentNode, nullptr );
81
- yulAssert (!m_leave && !m_break && !m_continue, " Function hoister has not been used." );
81
+ ScopedSaveAndRestore leave (m_leave, nullptr );
82
+ ScopedSaveAndRestore _break (m_break, nullptr );
83
+ ScopedSaveAndRestore _continue (m_continue, nullptr );
82
84
83
85
FunctionFlow flow;
84
86
flow.exit = newNode ();
@@ -90,7 +92,7 @@ void ControlFlowBuilder::operator()(FunctionDefinition const& _function)
90
92
91
93
m_currentNode->successors .emplace_back (flow.exit );
92
94
93
- m_functionFlows[_function. name ] = move (flow);
95
+ m_functionFlows[& _function] = move (flow);
94
96
95
97
m_leave = nullptr ;
96
98
}
@@ -164,14 +166,17 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
164
166
Block const & _ast
165
167
):
166
168
m_dialect(_dialect),
167
- m_cfgBuilder(_ast)
169
+ m_cfgBuilder(_ast),
170
+ m_functionReferences(FunctionReferenceResolver{_ast}.references())
168
171
{
169
- for (auto && [name , flow]: m_cfgBuilder.functionFlows ())
172
+ for (auto && [function , flow]: m_cfgBuilder.functionFlows ())
170
173
{
171
174
yulAssert (!flow.entry ->functionCall );
172
- m_processedNodes[name] = {};
173
- m_pendingNodes[name].push_front (flow.entry );
174
- m_functionSideEffects[name] = {false , false , false };
175
+ yulAssert (function);
176
+ m_processedNodes[function] = {};
177
+ m_pendingNodes[function].push_front (flow.entry );
178
+ m_functionSideEffects[function] = {false , false , false };
179
+ m_functionCalls[function] = {};
175
180
}
176
181
177
182
// Process functions while we have progress. For now, we are only interested
@@ -180,8 +185,8 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
180
185
while (progress)
181
186
{
182
187
progress = false ;
183
- for (auto const & functionName : m_pendingNodes | ranges::views::keys)
184
- if (processFunction (functionName ))
188
+ for (FunctionDefinition const * function : m_pendingNodes | ranges::views::keys)
189
+ if (processFunction (*function ))
185
190
progress = true ;
186
191
}
187
192
@@ -190,57 +195,64 @@ ControlFlowSideEffectsCollector::ControlFlowSideEffectsCollector(
190
195
// If we have not set `canContinue` by now, the function's exit
191
196
// is not reachable.
192
197
193
- for (auto && [functionName, calls]: m_functionCalls)
198
+ // Now it is sufficient to handle the reachable function calls (`m_functionCalls`),
199
+ // we do not have to consider the control-flow graph anymore.
200
+ for (auto && [function, calls]: m_functionCalls)
194
201
{
195
- ControlFlowSideEffects& sideEffects = m_functionSideEffects[functionName];
196
- auto _visit = [&, visited = std::set<YulString>{}](YulString _function, auto && _recurse) mutable {
197
- if (sideEffects.canTerminate && sideEffects.canRevert )
202
+ yulAssert (function);
203
+ ControlFlowSideEffects& functionSideEffects = m_functionSideEffects[function];
204
+ auto _visit = [&, visited = std::set<FunctionDefinition const *>{}](FunctionDefinition const & _function, auto && _recurse) mutable {
205
+ // Worst side-effects already, stop searching.
206
+ if (functionSideEffects.canTerminate && functionSideEffects.canRevert )
198
207
return ;
199
- if (!visited.insert (_function).second )
208
+ if (!visited.insert (& _function).second )
200
209
return ;
201
210
202
- ControlFlowSideEffects const * calledSideEffects = nullptr ;
203
- if (BuiltinFunction const * f = _dialect.builtin (_function))
204
- calledSideEffects = &f->controlFlowSideEffects ;
205
- else
206
- calledSideEffects = &m_functionSideEffects.at (_function);
207
-
208
- if (calledSideEffects->canTerminate )
209
- sideEffects.canTerminate = true ;
210
- if (calledSideEffects->canRevert )
211
- sideEffects.canRevert = true ;
212
-
213
- set<YulString> emptySet;
214
- for (YulString callee: util::valueOrDefault (m_functionCalls, _function, emptySet))
215
- _recurse (callee, _recurse);
211
+ for (FunctionCall const * call: m_functionCalls.at (&_function))
212
+ {
213
+ ControlFlowSideEffects const & calledSideEffects = sideEffects (*call);
214
+ if (calledSideEffects.canTerminate )
215
+ functionSideEffects.canTerminate = true ;
216
+ if (calledSideEffects.canRevert )
217
+ functionSideEffects.canRevert = true ;
218
+
219
+ if (m_functionReferences.count (call))
220
+ _recurse (*m_functionReferences.at (call), _recurse);
221
+ }
216
222
};
217
- for (auto const & call: calls)
218
- _visit (call, _visit);
223
+ _visit (*function, _visit);
219
224
}
225
+ }
220
226
227
+ map<YulString, ControlFlowSideEffects> ControlFlowSideEffectsCollector::functionSideEffectsNamed () const
228
+ {
229
+ map<YulString, ControlFlowSideEffects> result;
230
+ for (auto && [function, sideEffects]: m_functionSideEffects)
231
+ yulAssert (result.insert ({function->name , sideEffects}).second );
232
+ return result;
221
233
}
222
234
223
- bool ControlFlowSideEffectsCollector::processFunction (YulString _name )
235
+ bool ControlFlowSideEffectsCollector::processFunction (FunctionDefinition const & _function )
224
236
{
225
237
bool progress = false ;
226
- while (ControlFlowNode const * node = nextProcessableNode (_name ))
238
+ while (ControlFlowNode const * node = nextProcessableNode (_function ))
227
239
{
228
- if (node == m_cfgBuilder.functionFlows ().at (_name ).exit )
240
+ if (node == m_cfgBuilder.functionFlows ().at (&_function ).exit )
229
241
{
230
- m_functionSideEffects[_name ].canContinue = true ;
242
+ m_functionSideEffects[&_function ].canContinue = true ;
231
243
return true ;
232
244
}
233
245
for (ControlFlowNode const * s: node->successors )
234
- recordReachabilityAndQueue (_name , s);
246
+ recordReachabilityAndQueue (_function , s);
235
247
236
248
progress = true ;
237
249
}
238
250
return progress;
239
251
}
240
252
241
- ControlFlowNode const * ControlFlowSideEffectsCollector::nextProcessableNode (YulString _functionName )
253
+ ControlFlowNode const * ControlFlowSideEffectsCollector::nextProcessableNode (FunctionDefinition const & _function )
242
254
{
243
- std::list<ControlFlowNode const *>& nodes = m_pendingNodes[_functionName ];
255
+ std::list<ControlFlowNode const *>& nodes = m_pendingNodes[&_function ];
244
256
auto it = ranges::find_if (nodes, [this ](ControlFlowNode const * _node) {
245
257
return !_node->functionCall || sideEffects (*_node->functionCall ).canContinue ;
246
258
});
@@ -252,22 +264,22 @@ ControlFlowNode const* ControlFlowSideEffectsCollector::nextProcessableNode(YulS
252
264
return node;
253
265
}
254
266
255
- ControlFlowSideEffects const & ControlFlowSideEffectsCollector::sideEffects (YulString _functionName ) const
267
+ ControlFlowSideEffects const & ControlFlowSideEffectsCollector::sideEffects (FunctionCall const & _call ) const
256
268
{
257
- if (auto const * builtin = m_dialect.builtin (_functionName ))
269
+ if (auto const * builtin = m_dialect.builtin (_call. functionName . name ))
258
270
return builtin->controlFlowSideEffects ;
259
271
else
260
- return m_functionSideEffects.at (_functionName );
272
+ return m_functionSideEffects.at (m_functionReferences. at (&_call) );
261
273
}
262
274
263
275
void ControlFlowSideEffectsCollector::recordReachabilityAndQueue (
264
- YulString _functionName ,
276
+ FunctionDefinition const & _function ,
265
277
ControlFlowNode const * _node
266
278
)
267
279
{
268
280
if (_node->functionCall )
269
- m_functionCalls[_functionName ].insert (* _node->functionCall );
270
- if (m_processedNodes[_functionName ].insert (_node).second )
271
- m_pendingNodes.at (_functionName ).push_front (_node);
281
+ m_functionCalls[&_function ].insert (_node->functionCall );
282
+ if (m_processedNodes[&_function ].insert (_node).second )
283
+ m_pendingNodes.at (&_function ).push_front (_node);
272
284
}
273
285
0 commit comments