Skip to content

Commit f4effe9

Browse files
authored
Merge pull request #11824 from ethereum/yulControlFlowGraphRecursiveCalls
Mark recursive calls in yul control flow graph.
2 parents 31fd97b + c82f9b9 commit f4effe9

File tree

2 files changed

+74
-14
lines changed

2 files changed

+74
-14
lines changed

libyul/backends/evm/ControlFlowGraph.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,9 @@ struct CFG
131131
std::shared_ptr<DebugData const> debugData;
132132
std::reference_wrapper<Scope::Function const> function;
133133
std::reference_wrapper<yul::FunctionCall const> functionCall;
134+
/// True, if the call is recursive, i.e. entering the function involves a control flow path (potentially involving
135+
/// more intermediate function calls) that leads back to this very call.
136+
bool recursive = false;
134137
};
135138
struct Assignment
136139
{

libyul/backends/evm/ControlFlowGraphBuilder.cpp

Lines changed: 71 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,22 +47,14 @@ using namespace solidity;
4747
using namespace solidity::yul;
4848
using namespace std;
4949

50-
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
51-
AsmAnalysisInfo const& _analysisInfo,
52-
Dialect const& _dialect,
53-
Block const& _block
54-
)
50+
namespace
51+
{
52+
// Removes edges to blocks that are not reachable.
53+
void cleanUnreachable(CFG& _cfg)
5554
{
56-
auto result = std::make_unique<CFG>();
57-
result->entry = &result->makeBlock();
58-
59-
ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect);
60-
builder.m_currentBlock = result->entry;
61-
builder(_block);
62-
6355
// Determine which blocks are reachable from the entry.
64-
util::BreadthFirstSearch<CFG::BasicBlock*> reachabilityCheck{{result->entry}};
65-
for (auto const& functionInfo: result->functionInfo | ranges::views::values)
56+
util::BreadthFirstSearch<CFG::BasicBlock*> reachabilityCheck{{_cfg.entry}};
57+
for (auto const& functionInfo: _cfg.functionInfo | ranges::views::values)
6658
reachabilityCheck.verticesToTraverse.emplace_back(functionInfo.entry);
6759

6860
reachabilityCheck.run([&](CFG::BasicBlock* _node, auto&& _addChild) {
@@ -85,6 +77,71 @@ std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
8577
cxx20::erase_if(node->entries, [&](CFG::BasicBlock* entry) -> bool {
8678
return !reachabilityCheck.visited.count(entry);
8779
});
80+
}
81+
// Sets the ``recursive`` member to ``true`` for all recursive function calls.
82+
void markRecursiveCalls(CFG& _cfg)
83+
{
84+
map<CFG::BasicBlock*, vector<CFG::FunctionCall*>> callsPerBlock;
85+
auto const& findCalls = [&](CFG::BasicBlock* _block)
86+
{
87+
if (auto* calls = util::valueOrNullptr(callsPerBlock, _block))
88+
return *calls;
89+
vector<CFG::FunctionCall*>& calls = callsPerBlock[_block];
90+
util::BreadthFirstSearch<CFG::BasicBlock*>{{_block}}.run([&](CFG::BasicBlock* _block, auto _addChild) {
91+
for (auto& operation: _block->operations)
92+
if (auto* functionCall = get_if<CFG::FunctionCall>(&operation.operation))
93+
calls.emplace_back(functionCall);
94+
std::visit(util::GenericVisitor{
95+
[&](CFG::BasicBlock::MainExit const&) {},
96+
[&](CFG::BasicBlock::Jump const& _jump)
97+
{
98+
_addChild(_jump.target);
99+
},
100+
[&](CFG::BasicBlock::ConditionalJump const& _conditionalJump)
101+
{
102+
_addChild(_conditionalJump.zero);
103+
_addChild(_conditionalJump.nonZero);
104+
},
105+
[&](CFG::BasicBlock::FunctionReturn const&) {},
106+
[&](CFG::BasicBlock::Terminated const&) {},
107+
}, _block->exit);
108+
});
109+
return calls;
110+
};
111+
for (auto& functionInfo: _cfg.functionInfo | ranges::views::values)
112+
for (CFG::FunctionCall* call: findCalls(functionInfo.entry))
113+
{
114+
util::BreadthFirstSearch<CFG::FunctionCall*> breadthFirstSearch{{call}};
115+
breadthFirstSearch.run([&](CFG::FunctionCall* _call, auto _addChild) {
116+
auto& calledFunctionInfo = _cfg.functionInfo.at(&_call->function.get());
117+
if (&calledFunctionInfo == &functionInfo)
118+
{
119+
call->recursive = true;
120+
breadthFirstSearch.abort();
121+
return;
122+
}
123+
for (CFG::FunctionCall* nestedCall: findCalls(_cfg.functionInfo.at(&_call->function.get()).entry))
124+
_addChild(nestedCall);
125+
});
126+
}
127+
}
128+
}
129+
130+
std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
131+
AsmAnalysisInfo const& _analysisInfo,
132+
Dialect const& _dialect,
133+
Block const& _block
134+
)
135+
{
136+
auto result = std::make_unique<CFG>();
137+
result->entry = &result->makeBlock();
138+
139+
ControlFlowGraphBuilder builder(*result, _analysisInfo, _dialect);
140+
builder.m_currentBlock = result->entry;
141+
builder(_block);
142+
143+
cleanUnreachable(*result);
144+
markRecursiveCalls(*result);
88145

89146
// TODO: It might be worthwhile to run some further simplifications on the graph itself here.
90147
// E.g. if there is a jump to a node that has the jumping node as its only entry, the nodes can be fused, etc.

0 commit comments

Comments
 (0)