@@ -48,7 +48,7 @@ using namespace std;
4848
4949namespace
5050{
51- // Removes edges to blocks that are not reachable.
51+ // / Removes edges to blocks that are not reachable.
5252void cleanUnreachable (CFG & _cfg)
5353{
5454 // Determine which blocks are reachable from the entry.
@@ -77,7 +77,8 @@ void cleanUnreachable(CFG& _cfg)
7777 return !reachabilityCheck.visited .count (entry);
7878 });
7979}
80- // Sets the ``recursive`` member to ``true`` for all recursive function calls.
80+
81+ // / Sets the ``recursive`` member to ``true`` for all recursive function calls.
8182void markRecursiveCalls (CFG & _cfg)
8283{
8384 map<CFG ::BasicBlock*, vector<CFG ::FunctionCall*>> callsPerBlock;
@@ -124,6 +125,84 @@ void markRecursiveCalls(CFG& _cfg)
124125 });
125126 }
126127}
128+
129+ // / Marks each cut-vertex in the CFG, i.e. each block that begins a disconnected sub-graph of the CFG.
130+ // / Entering such a block means that control flow will never return to a previously visited block.
131+ void markStartsOfSubGraphs (CFG & _cfg)
132+ {
133+ vector<CFG ::BasicBlock*> entries;
134+ entries.emplace_back (_cfg.entry );
135+ for (auto && functionInfo: _cfg.functionInfo | ranges::views::values)
136+ entries.emplace_back (functionInfo.entry );
137+ for (auto & entry: entries)
138+ {
139+ /* *
140+ * Detect bridges following Algorithm 1 in https://arxiv.org/pdf/2108.07346.pdf
141+ * and mark the bridge targets as starts of sub-graphs.
142+ */
143+ set<CFG ::BasicBlock*> visited;
144+ map<CFG ::BasicBlock*, size_t > disc;
145+ map<CFG ::BasicBlock*, size_t > low;
146+ map<CFG ::BasicBlock*, CFG ::BasicBlock*> parent;
147+ size_t time = 0 ;
148+ auto dfs = [&](CFG ::BasicBlock* _u, auto _recurse) -> void {
149+ visited.insert (_u);
150+ disc[_u] = low[_u] = time;
151+ time++;
152+
153+ vector<CFG ::BasicBlock*> children = _u->entries ;
154+ visit (util::GenericVisitor{
155+ [&](CFG ::BasicBlock::Jump const & _jump) {
156+ children.emplace_back (_jump.target );
157+ },
158+ [&](CFG ::BasicBlock::ConditionalJump const & _jump) {
159+ children.emplace_back (_jump.zero );
160+ children.emplace_back (_jump.nonZero );
161+ },
162+ [&](CFG ::BasicBlock::FunctionReturn const &) {},
163+ [&](CFG ::BasicBlock::Terminated const &) { _u->isStartOfSubGraph = true ; },
164+ [&](CFG ::BasicBlock::MainExit const &) { _u->isStartOfSubGraph = true ; }
165+ }, _u->exit );
166+ yulAssert (!util::contains (children, _u));
167+
168+ for (CFG ::BasicBlock* v: children)
169+ if (!visited.count (v))
170+ {
171+ parent[v] = _u;
172+ _recurse (v, _recurse);
173+ low[_u] = min (low[_u], low[v]);
174+ if (low[v] > disc[_u])
175+ {
176+ // _u <-> v is a cut edge in the undirected graph
177+ bool edgeVtoU = util::contains (_u->entries , v);
178+ bool edgeUtoV = util::contains (v->entries , _u);
179+ if (edgeVtoU && !edgeUtoV)
180+ // Cut edge v -> _u
181+ _u->isStartOfSubGraph = true ;
182+ else if (edgeUtoV && !edgeVtoU)
183+ // Cut edge _u -> v
184+ v->isStartOfSubGraph = true ;
185+ }
186+ }
187+ else if (v != parent[_u])
188+ low[_u] = min (low[_u], disc[v]);
189+ };
190+ dfs (entry, dfs);
191+ }
192+ }
193+
194+ // / Marks each block that needs to maintain a clean stack. That is each block that has an outgoing
195+ // / path to a function return.
196+ void markNeedsCleanStack (CFG & _cfg)
197+ {
198+ for (auto & functionInfo: _cfg.functionInfo | ranges::views::values)
199+ for (CFG ::BasicBlock* exit: functionInfo.exits )
200+ util::BreadthFirstSearch<CFG ::BasicBlock*>{{exit}}.run ([&](CFG ::BasicBlock* _block, auto _addChild) {
201+ _block->needsCleanStack = true ;
202+ for (CFG ::BasicBlock* entry: _block->entries )
203+ _addChild (entry);
204+ });
205+ }
127206}
128207
129208std::unique_ptr<CFG > ControlFlowGraphBuilder::build (
@@ -141,6 +220,8 @@ std::unique_ptr<CFG> ControlFlowGraphBuilder::build(
141220
142221 cleanUnreachable (*result);
143222 markRecursiveCalls (*result);
223+ markStartsOfSubGraphs (*result);
224+ markNeedsCleanStack (*result);
144225
145226 // TODO: It might be worthwhile to run some further simplifications on the graph itself here.
146227 // 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.
@@ -379,6 +460,7 @@ void ControlFlowGraphBuilder::operator()(Leave const& leave_)
379460{
380461 yulAssert (m_currentFunction.has_value (), " " );
381462 m_currentBlock->exit = CFG ::BasicBlock::FunctionReturn{debugDataOf (leave_), *m_currentFunction};
463+ (*m_currentFunction)->exits .emplace_back (m_currentBlock);
382464 m_currentBlock = &m_graph.makeBlock (debugDataOf (*m_currentBlock));
383465}
384466
@@ -395,6 +477,7 @@ void ControlFlowGraphBuilder::operator()(FunctionDefinition const& _function)
395477 builder.m_currentFunction = &functionInfo;
396478 builder.m_currentBlock = functionInfo.entry ;
397479 builder (_function.body );
480+ functionInfo.exits .emplace_back (builder.m_currentBlock );
398481 builder.m_currentBlock ->exit = CFG ::BasicBlock::FunctionReturn{debugDataOf (_function), &functionInfo};
399482}
400483
@@ -423,7 +506,8 @@ void ControlFlowGraphBuilder::registerFunction(FunctionDefinition const& _functi
423506 std::get<Scope::Variable>(virtualFunctionScope->identifiers .at (_retVar.name )),
424507 _retVar.debugData
425508 };
426- }) | ranges::to<vector>
509+ }) | ranges::to<vector>,
510+ {}
427511 })).second ;
428512 yulAssert (inserted);
429513}
0 commit comments