23
23
#include < libyul/AST.h>
24
24
#include < libyul/Exceptions.h>
25
25
#include < libyul/Utilities.h>
26
+ #include < libyul/ControlFlowSideEffectsCollector.h>
26
27
27
28
#include < libsolutil/cxx20.h>
28
29
#include < libsolutil/Visitor.h>
@@ -214,7 +215,8 @@ std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
214
215
auto result = std::make_unique<CFG>();
215
216
result->entry = &result->makeBlock (debugDataOf (_block));
216
217
217
- ControlFlowGraphBuilder builder (*result, _analysisInfo, _dialect);
218
+ ControlFlowSideEffectsCollector sideEffects (_dialect, _block);
219
+ ControlFlowGraphBuilder builder (*result, _analysisInfo, sideEffects.functionSideEffects (), _dialect);
218
220
builder.m_currentBlock = result->entry ;
219
221
builder (_block);
220
222
@@ -232,10 +234,12 @@ std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
232
234
ControlFlowGraphBuilder::ControlFlowGraphBuilder (
233
235
CFG& _graph,
234
236
AsmAnalysisInfo const & _analysisInfo,
237
+ map<FunctionDefinition const *, ControlFlowSideEffects> const & _functionSideEffects,
235
238
Dialect const & _dialect
236
239
):
237
240
m_graph(_graph),
238
241
m_info(_analysisInfo),
242
+ m_functionSideEffects(_functionSideEffects),
239
243
m_dialect(_dialect)
240
244
{
241
245
}
@@ -285,10 +289,10 @@ void ControlFlowGraphBuilder::operator()(Assignment const& _assignment)
285
289
return VariableSlot{lookupVariable (_var.name ), _var.debugData };
286
290
}) | ranges::to<vector<VariableSlot>>;
287
291
288
- yulAssert (m_currentBlock, " " );
292
+ Stack input = visitAssignmentRightHandSide (*_assignment.value , assignedVariables.size ());
293
+ yulAssert (m_currentBlock);
289
294
m_currentBlock->operations .emplace_back (CFG::Operation{
290
- // input
291
- visitAssignmentRightHandSide (*_assignment.value , assignedVariables.size ()),
295
+ std::move (input),
292
296
// output
293
297
assignedVariables | ranges::to<Stack>,
294
298
// operation
@@ -297,24 +301,13 @@ void ControlFlowGraphBuilder::operator()(Assignment const& _assignment)
297
301
}
298
302
void ControlFlowGraphBuilder::operator ()(ExpressionStatement const & _exprStmt)
299
303
{
300
- yulAssert (m_currentBlock, " " );
301
304
std::visit (util::GenericVisitor{
302
305
[&](FunctionCall const & _call) {
303
306
Stack const & output = visitFunctionCall (_call);
304
307
yulAssert (output.empty (), " " );
305
308
},
306
309
[&](auto const &) { yulAssert (false , " " ); }
307
310
}, _exprStmt.expression );
308
-
309
- // TODO: Ideally this would be done on the expression label and for all functions that always revert,
310
- // not only for builtins.
311
- if (auto const * funCall = get_if<FunctionCall>(&_exprStmt.expression ))
312
- if (BuiltinFunction const * builtin = m_dialect.builtin (funCall->functionName .name ))
313
- if (builtin->controlFlowSideEffects .terminatesOrReverts ())
314
- {
315
- m_currentBlock->exit = CFG::BasicBlock::Terminated{};
316
- m_currentBlock = &m_graph.makeBlock (debugDataOf (*m_currentBlock));
317
- }
318
311
}
319
312
320
313
void ControlFlowGraphBuilder::operator ()(Block const & _block)
@@ -331,7 +324,8 @@ void ControlFlowGraphBuilder::operator()(If const& _if)
331
324
{
332
325
auto & ifBranch = m_graph.makeBlock (debugDataOf (_if.body ));
333
326
auto & afterIf = m_graph.makeBlock (debugDataOf (*m_currentBlock));
334
- makeConditionalJump (debugDataOf (_if), std::visit (*this , *_if.condition ), ifBranch, afterIf);
327
+ StackSlot condition = std::visit (*this , *_if.condition );
328
+ makeConditionalJump (debugDataOf (_if), std::move (condition), ifBranch, afterIf);
335
329
m_currentBlock = &ifBranch;
336
330
(*this )(_if.body );
337
331
jump (debugDataOf (_if.body ), afterIf);
@@ -349,8 +343,9 @@ void ControlFlowGraphBuilder::operator()(Switch const& _switch)
349
343
// Artificially generate:
350
344
// let <ghostVariable> := <switchExpression>
351
345
VariableSlot ghostVarSlot{ghostVar, debugDataOf (*_switch.expression )};
346
+ StackSlot expression = std::visit (*this , *_switch.expression );
352
347
m_currentBlock->operations .emplace_back (CFG::Operation{
353
- Stack{std::visit (* this , *_switch. expression )},
348
+ Stack{std::move ( expression)},
354
349
Stack{ghostVarSlot},
355
350
CFG::Assignment{_switch.debugData , {ghostVarSlot}}
356
351
});
@@ -430,7 +425,8 @@ void ControlFlowGraphBuilder::operator()(ForLoop const& _loop)
430
425
else
431
426
{
432
427
jump (debugDataOf (_loop.pre ), loopCondition);
433
- makeConditionalJump (debugDataOf (*_loop.condition ), std::visit (*this , *_loop.condition ), loopBody, afterLoop);
428
+ StackSlot condition = std::visit (*this , *_loop.condition );
429
+ makeConditionalJump (debugDataOf (*_loop.condition ), std::move (condition), loopBody, afterLoop);
434
430
m_currentBlock = &loopBody;
435
431
(*this )(_loop.body );
436
432
jump (debugDataOf (_loop.body ), post);
@@ -473,41 +469,43 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
473
469
474
470
CFG::FunctionInfo& functionInfo = m_graph.functionInfo .at (&function);
475
471
476
- ControlFlowGraphBuilder builder{m_graph, m_info, m_dialect};
472
+ ControlFlowGraphBuilder builder{m_graph, m_info, m_functionSideEffects, m_dialect};
477
473
builder.m_currentFunction = &functionInfo;
478
474
builder.m_currentBlock = functionInfo.entry ;
479
475
builder (_function.body );
480
476
functionInfo.exits .emplace_back (builder.m_currentBlock );
481
477
builder.m_currentBlock ->exit = CFG::BasicBlock::FunctionReturn{debugDataOf (_function), &functionInfo};
482
478
}
483
479
484
- void ControlFlowGraphBuilder::registerFunction (FunctionDefinition const & _function )
480
+ void ControlFlowGraphBuilder::registerFunction (FunctionDefinition const & _functionDefinition )
485
481
{
486
482
yulAssert (m_scope, " " );
487
- yulAssert (m_scope->identifiers .count (_function .name ), " " );
488
- Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers .at (_function .name ));
483
+ yulAssert (m_scope->identifiers .count (_functionDefinition .name ), " " );
484
+ Scope::Function& function = std::get<Scope::Function>(m_scope->identifiers .at (_functionDefinition .name ));
489
485
490
- yulAssert (m_info.scopes .at (&_function .body ), " " );
491
- Scope* virtualFunctionScope = m_info.scopes .at (m_info.virtualBlocks .at (&_function ).get ()).get ();
486
+ yulAssert (m_info.scopes .at (&_functionDefinition .body ), " " );
487
+ Scope* virtualFunctionScope = m_info.scopes .at (m_info.virtualBlocks .at (&_functionDefinition ).get ()).get ();
492
488
yulAssert (virtualFunctionScope, " " );
493
489
494
490
bool inserted = m_graph.functionInfo .emplace (std::make_pair (&function, CFG::FunctionInfo{
495
- _function .debugData ,
491
+ _functionDefinition .debugData ,
496
492
function,
497
- &m_graph.makeBlock (debugDataOf (_function.body )),
498
- _function.parameters | ranges::views::transform ([&](auto const & _param) {
493
+ _functionDefinition,
494
+ &m_graph.makeBlock (debugDataOf (_functionDefinition.body )),
495
+ _functionDefinition.parameters | ranges::views::transform ([&](auto const & _param) {
499
496
return VariableSlot{
500
497
std::get<Scope::Variable>(virtualFunctionScope->identifiers .at (_param.name )),
501
498
_param.debugData
502
499
};
503
500
}) | ranges::to<vector>,
504
- _function .returnVariables | ranges::views::transform ([&](auto const & _retVar) {
501
+ _functionDefinition .returnVariables | ranges::views::transform ([&](auto const & _retVar) {
505
502
return VariableSlot{
506
503
std::get<Scope::Variable>(virtualFunctionScope->identifiers .at (_retVar.name )),
507
504
_retVar.debugData
508
505
};
509
506
}) | ranges::to<vector>,
510
- {}
507
+ {},
508
+ m_functionSideEffects.at (&_functionDefinition).canContinue
511
509
})).second ;
512
510
yulAssert (inserted);
513
511
}
@@ -517,14 +515,16 @@ Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _cal
517
515
yulAssert (m_scope, " " );
518
516
yulAssert (m_currentBlock, " " );
519
517
518
+ Stack const * output = nullptr ;
519
+ bool canContinue = true ;
520
520
if (BuiltinFunction const * builtin = m_dialect.builtin (_call.functionName .name ))
521
521
{
522
522
Stack inputs;
523
523
for (auto && [idx, arg]: _call.arguments | ranges::views::enumerate | ranges::views::reverse)
524
524
if (!builtin->literalArgument (idx).has_value ())
525
525
inputs.emplace_back (std::visit (*this , arg));
526
526
CFG::BuiltinCall builtinCall{_call.debugData , *builtin, _call, inputs.size ()};
527
- return m_currentBlock->operations .emplace_back (CFG::Operation{
527
+ output = & m_currentBlock->operations .emplace_back (CFG::Operation{
528
528
// input
529
529
std::move (inputs),
530
530
// output
@@ -534,24 +534,34 @@ Stack const& ControlFlowGraphBuilder::visitFunctionCall(FunctionCall const& _cal
534
534
// operation
535
535
std::move (builtinCall)
536
536
}).output ;
537
+ canContinue = builtin->controlFlowSideEffects .canContinue ;
537
538
}
538
539
else
539
540
{
540
541
Scope::Function const & function = lookupFunction (_call.functionName .name );
541
- Stack inputs{FunctionCallReturnLabelSlot{_call}};
542
+ canContinue = m_graph.functionInfo .at (&function).canContinue ;
543
+ Stack inputs;
544
+ if (canContinue)
545
+ inputs.emplace_back (FunctionCallReturnLabelSlot{_call});
542
546
for (auto const & arg: _call.arguments | ranges::views::reverse)
543
547
inputs.emplace_back (std::visit (*this , arg));
544
- return m_currentBlock->operations .emplace_back (CFG::Operation{
548
+ output = & m_currentBlock->operations .emplace_back (CFG::Operation{
545
549
// input
546
550
std::move (inputs),
547
551
// output
548
552
ranges::views::iota (0u , function.returns .size ()) | ranges::views::transform ([&](size_t _i) {
549
553
return TemporarySlot{_call, _i};
550
554
}) | ranges::to<Stack>,
551
555
// operation
552
- CFG::FunctionCall{_call.debugData , function, _call}
556
+ CFG::FunctionCall{_call.debugData , function, _call, /* recursive */ false , canContinue }
553
557
}).output ;
554
558
}
559
+ if (!canContinue)
560
+ {
561
+ m_currentBlock->exit = CFG::BasicBlock::Terminated{};
562
+ m_currentBlock = &m_graph.makeBlock (debugDataOf (*m_currentBlock));
563
+ }
564
+ return *output;
555
565
}
556
566
557
567
Stack ControlFlowGraphBuilder::visitAssignmentRightHandSide (Expression const & _expression, size_t _expectedSlotCount)
0 commit comments