Jlm
Loading...
Searching...
No Matches
ControlFlowRestructuring.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2015 Nico Reißmann <nico.reissmann@gmail.com>
3 * See COPYING for terms of redistribution.
4 */
5
9
10#include <deque>
11#include <unordered_map>
12
13namespace jlm::llvm
14{
15
28
31{
32 JLM_ASSERT(loopExit.NumOutEdges() == 2);
33 auto & cfg = loopEntry.cfg();
34
35 auto er = loopExit.OutEdge(0);
36 auto ex = loopExit.OutEdge(1);
37 if (er->sink() != &loopEntry)
38 {
39 er = loopExit.OutEdge(1);
40 ex = loopExit.OutEdge(0);
41 }
42 JLM_ASSERT(er->sink() == &loopEntry);
43
44 auto exsink = BasicBlock::create(cfg);
45 auto replacement = BasicBlock::create(cfg);
46 loopEntry.divert_inedges(replacement);
47 replacement->add_outedge(ex->sink());
48 ex->divert(exsink);
49 er->divert(&loopEntry);
50
51 return TailControlledLoop(&loopEntry, exsink, replacement);
52}
53
54static void
56{
57 JLM_ASSERT(loop.insert->NumInEdges() == 1);
59 auto & cfg = loop.ne->cfg();
60
61 loop.replacement->divert_inedges(loop.ne);
62 loop.insert->divert_inedges(loop.replacement->OutEdge(0)->sink());
63
64 cfg.remove_node(loop.insert);
65 cfg.remove_node(loop.replacement);
66}
67
68static const ThreeAddressCodeVariable *
69CreateContinuationVariable(BasicBlock & bb, std::shared_ptr<const rvsdg::ControlType> type)
70{
71 static size_t c = 0;
72 const auto name = util::strfmt("#p", c++, "#");
73 return bb.insert_before_branch(UndefValueOperation::Create(std::move(type), name))->result(0);
74}
75
76static const ThreeAddressCodeVariable &
77CreateLoopExitVariable(BasicBlock & bb, std::shared_ptr<const rvsdg::ControlType> type)
78{
79 static size_t c = 0;
80 const auto name = util::strfmt("#q", c++, "#");
81
82 auto exitVariable = UndefValueOperation::Create(std::move(type), name);
83 return *bb.append_last(std::move(exitVariable))->result(0);
84}
85
86static const ThreeAddressCodeVariable &
87CreateLoopEntryVariable(BasicBlock & bb, std::shared_ptr<const rvsdg::ControlType> type)
88{
89 static size_t c = 0;
90 const auto name = util::strfmt("#q", c++, "#");
91
92 auto entryVariable = UndefValueOperation::Create(std::move(type), name);
93 return *bb.insert_before_branch(std::move(entryVariable))->result(0);
94}
95
96static const ThreeAddressCodeVariable &
98{
99 static size_t c = 0;
100 const auto name = util::strfmt("#r", c++, "#");
101
102 auto repetitionVariable = UndefValueOperation::Create(rvsdg::ControlType::Create(2), name);
103 return *basicBlock.append_last(std::move(repetitionVariable))->result(0);
104}
105
106static void
107AppendBranch(BasicBlock & basicBlock, const Variable * operand)
108{
109 const auto numAlternatives =
110 util::assertedCast<const rvsdg::ControlType>(&operand->type())->nalternatives();
111 basicBlock.append_last(BranchOperation::create(numAlternatives, operand));
112}
113
114static void
116 BasicBlock & basicBlock,
117 const ThreeAddressCodeVariable & variable,
118 const size_t value)
119{
120 const auto numAlternatives =
121 util::assertedCast<const rvsdg::ControlType>(&variable.type())->nalternatives();
122
123 auto op = std::make_unique<rvsdg::ControlConstantOperation>(
124 rvsdg::ControlValueRepresentation(value, numAlternatives));
125 basicBlock.append_last(ThreeAddressCode::create(std::move(op), {}));
126 basicBlock.append_last(AssignmentOperation::create(basicBlock.last()->result(0), &variable));
127}
128
129static void
131 const StronglyConnectedComponentStructure & sccStructure,
132 BasicBlock * newEntryNode,
133 const ThreeAddressCodeVariable * entryVariable)
134{
135 size_t n = 0;
136 std::unordered_map<ControlFlowGraphNode *, size_t> indices;
137 for (auto & node : sccStructure.EntryNodes())
138 {
139 newEntryNode->add_outedge(node);
140 indices[node] = n++;
141 }
142
143 if (entryVariable)
144 AppendBranch(*newEntryNode, entryVariable);
145
146 for (auto & edge : sccStructure.EntryEdges())
147 {
148 auto os = edge->sink();
149 edge->divert(newEntryNode);
150 if (entryVariable)
151 AppendConstantAssignment(*edge->split(), *entryVariable, indices[os]);
152 }
153}
154
155static void
157 const StronglyConnectedComponentStructure & sccStructure,
158 BasicBlock & newRepetitionNode,
159 BasicBlock & newExitNode,
160 ControlFlowGraphNode & regionExit,
161 const ThreeAddressCodeVariable & repetitionVariable,
162 const ThreeAddressCodeVariable * exitVariable)
163{
164 // It could be that an SCC has no exit edge. This can arise when the input CFG contains a
165 // statically detectable endless loop, e.g., entry -> basic block exit. Note the missing
166 // ^_________|
167 // edge to the exit node.
168 //
169 // Such CFGs do not play well with our restructuring algorithm, as the exit node does not
170 // post-dominate the basic block. We circumvent this problem by inserting an additional
171 // edge from the newly created exit basic block of the loop to the exit of the SESE region.
172 // This edge is never taken at runtime, but fixes the CFGs structure at compile-time such
173 // that we can create an RVSDG.
174 if (sccStructure.NumExitEdges() == 0)
175 {
176 newExitNode.add_outedge(&regionExit);
177 return;
178 }
179
180 size_t n = 0;
181 std::unordered_map<ControlFlowGraphNode *, size_t> indices;
182 for (auto & node : sccStructure.ExitNodes())
183 {
184 newExitNode.add_outedge(node);
185 indices[node] = n++;
186 }
187
188 if (exitVariable)
189 AppendBranch(newExitNode, exitVariable);
190
191 for (auto & edge : sccStructure.ExitEdges())
192 {
193 auto os = edge->sink();
194 edge->divert(&newRepetitionNode);
195 auto bb = edge->split();
196 if (exitVariable)
197 AppendConstantAssignment(*bb, *exitVariable, indices[os]);
198 AppendConstantAssignment(*bb, repetitionVariable, 0);
199 }
200}
201
202static void
204 const StronglyConnectedComponentStructure & sccStructure,
205 ControlFlowGraphNode & newRepetitionNode,
206 const ThreeAddressCodeVariable * entryVariable,
207 const ThreeAddressCodeVariable & repetitionVariable)
208{
209 size_t n = 0;
210 std::unordered_map<ControlFlowGraphNode *, size_t> indices;
211 for (auto & node : sccStructure.EntryNodes())
212 indices[node] = n++;
213
214 for (auto & edge : sccStructure.RepetitionEdges())
215 {
216 auto os = edge->sink();
217 edge->divert(&newRepetitionNode);
218 auto basicBlock = edge->split();
219 if (entryVariable)
220 AppendConstantAssignment(*basicBlock, *entryVariable, indices[os]);
221 AppendConstantAssignment(*basicBlock, repetitionVariable, 1);
222 }
223}
224
225static BasicBlock *
227{
228 if (auto basicBlock = dynamic_cast<BasicBlock *>(node))
229 return basicBlock;
230
231 auto sink = node->OutEdge(0)->sink();
232 JLM_ASSERT(is<BasicBlock>(sink));
233
234 return static_cast<BasicBlock *>(sink);
235}
236
237static void
239 ControlFlowGraphNode &,
240 ControlFlowGraphNode &,
241 std::vector<TailControlledLoop> &);
242
249static std::unique_ptr<rvsdg::MatchOperation>
251{
252 JLM_ASSERT(matchOperation.nalternatives() == 2);
253
254 const size_t numBits = matchOperation.nbits();
255 const auto oldDefaultAlternative = matchOperation.default_alternative();
256 auto [oldValue, oldAlternative] = *matchOperation.begin();
257 const auto newDefaultAlternative = oldAlternative;
258 auto newAlternative = oldDefaultAlternative;
259
260 return std::unique_ptr<rvsdg::MatchOperation>(new rvsdg::MatchOperation(
261 numBits,
262 { { oldValue, newAlternative } },
263 newDefaultAlternative,
264 matchOperation.nalternatives()));
265}
266
275static void
277{
278 JLM_ASSERT(sccStructure.IsTailControlledLoop());
279
280 const auto repetitionEdge = *sccStructure.RepetitionEdges().begin();
281 if (repetitionEdge->index() == 1)
282 {
283 // Nothing needs to be done. The repetition edge has already index 1.
284 return;
285 }
286 JLM_ASSERT(repetitionEdge->index() == 0);
287
288 // We need to adjust the tail-controlled loop such that the repetition edge is on index 1,
289 // and swapping the alternatives of the match operation accordingly
290 auto exitNode = util::assertedCast<BasicBlock>((*sccStructure.ExitEdges().begin())->source());
291 auto exitEdge = *sccStructure.ExitEdges().begin();
292 auto exitEdgeSink = exitEdge->sink();
293 auto repetitionEdgeSink = repetitionEdge->sink();
294
295 repetitionEdge->divert(exitEdgeSink);
296 exitEdge->divert(repetitionEdgeSink);
297
298 // The node's tac list is guaranteed to end with a branch instruction
299 auto & threeAddressCodes = exitNode->tacs();
300 auto & branchTac = *threeAddressCodes.last();
301 JLM_ASSERT(is<BranchOperation>(&branchTac));
302 // The operand of the branch instruction is always a tac variable assigned by a match instruction
303 auto & branchOperand = *util::assertedCast<const ThreeAddressCodeVariable>(branchTac.operand(0));
304 auto & matchTac = *branchOperand.tac();
305 JLM_ASSERT(is<rvsdg::MatchOperation>(&matchTac));
306 JLM_ASSERT(branchTac.operand(0) == matchTac.result(0));
307 auto & matchOperation = *util::assertedCast<const rvsdg::MatchOperation>(&matchTac.operation());
308 JLM_ASSERT(matchOperation.nalternatives() == 2);
309
310 auto newMatchOperation = invertMatchOperation(matchOperation);
311 auto newMatchOperand = matchTac.operand(0);
312 matchTac.replace(*newMatchOperation, { newMatchOperand });
313}
314
315static void
317 ControlFlowGraphNode & regionEntry,
318 ControlFlowGraphNode & regionExit,
319 std::vector<TailControlledLoop> & loops)
320{
321 if (&regionEntry == &regionExit)
322 return;
323
324 auto & cfg = regionEntry.cfg();
325
326 const auto stronglyConnectedComponents = find_sccs(&regionEntry, &regionExit);
327 for (auto & scc : stronglyConnectedComponents)
328 {
329 auto sccStructure = StronglyConnectedComponentStructure::Create(scc);
330
331 if (sccStructure->IsTailControlledLoop())
332 {
333 // We already have a tail-controlled loop
334 auto loopEntry = *sccStructure->EntryNodes().begin();
335 auto loopExit = (*sccStructure->ExitEdges().begin())->source();
336 RestructureControlFlow(*loopEntry, *loopExit, loops);
337 adjustLoopRepetitionEdge(*sccStructure);
338 loops.push_back(ExtractLoop(*loopEntry, *loopExit));
339 }
340 else
341 {
342 // It is not a tail-controlled loop. We need to restructure the loop.
343 auto & newEntryNode = *BasicBlock::create(cfg);
344 auto & newRepetitionNode = *BasicBlock::create(cfg);
345 auto & newExitNode = *BasicBlock::create(cfg);
346 newRepetitionNode.add_outedge(&newExitNode);
347 newRepetitionNode.add_outedge(&newEntryNode);
348
349 const ThreeAddressCodeVariable * entryVariable = nullptr;
350 if (sccStructure->NumEntryNodes() > 1)
351 {
352 auto bb = GetEntryVariableBlock(&regionEntry);
353 entryVariable = &CreateLoopEntryVariable(
354 *bb,
355 rvsdg::ControlType::Create(sccStructure->NumEntryNodes()));
356 }
357
358 auto & repetitionVariable = CreateLoopRepetitionVariable(newEntryNode);
359
360 const ThreeAddressCodeVariable * exitVariable = nullptr;
361 if (sccStructure->NumExitNodes() > 1)
362 exitVariable = &CreateLoopExitVariable(
363 newEntryNode,
364 rvsdg::ControlType::Create(sccStructure->NumExitNodes()));
365
366 AppendBranch(newRepetitionNode, &repetitionVariable);
367
368 RestructureLoopEntry(*sccStructure, &newEntryNode, entryVariable);
370 *sccStructure,
371 newRepetitionNode,
372 newExitNode,
373 regionExit,
374 repetitionVariable,
375 exitVariable);
377 *sccStructure,
378 newRepetitionNode,
379 entryVariable,
380 repetitionVariable);
381
382 RestructureControlFlow(newEntryNode, newRepetitionNode, loops);
383 loops.push_back(ExtractLoop(newEntryNode, newRepetitionNode));
384 }
385 }
386}
387
388static ControlFlowGraphNode &
390{
391 ControlFlowGraphNode * headBranch = &start;
392 do
393 {
394 if (headBranch->is_branch() || headBranch == &end)
395 break;
396
397 headBranch = headBranch->OutEdge(0)->sink();
398 } while (true);
399
400 return *headBranch;
401}
402
405{
407 util::HashSet edges({ edge });
408
409 std::deque toVisit(1, edge->sink());
410 while (toVisit.size() != 0)
411 {
412 ControlFlowGraphNode * node = toVisit.front();
413 toVisit.pop_front();
414 if (nodes.Contains(node))
415 continue;
416
417 bool accept = true;
418 for (auto & inedge : node->InEdges())
419 {
420 if (!edges.Contains(&inedge))
421 {
422 accept = false;
423 break;
424 }
425 }
426
427 if (accept)
428 {
429 nodes.insert(node);
430 for (auto & outedge : node->OutEdges())
431 {
432 edges.insert(&outedge);
433 toVisit.push_back(outedge.sink());
434 }
435 }
436 }
437
438 return nodes;
439}
440
442{
444 std::unordered_map<ControlFlowGraphEdge *, util::HashSet<ControlFlowGraphEdge *>> edges;
445};
446
447static Continuation
449{
450 JLM_ASSERT(headBranch.NumOutEdges() > 1);
451
452 std::unordered_map<ControlFlowGraphEdge *, util::HashSet<ControlFlowGraphNode *>> dominatorGraphs;
453 for (auto & outedge : headBranch.OutEdges())
454 dominatorGraphs[&outedge] = ComputeDominatorGraph(&outedge);
455
456 Continuation c;
457 for (auto & outedge : headBranch.OutEdges())
458 {
459 auto & dominatorGraph = dominatorGraphs[&outedge];
460 if (dominatorGraph.IsEmpty())
461 {
462 c.edges[&outedge].insert(&outedge);
463 c.points.insert(outedge.sink());
464 continue;
465 }
466
467 for (const auto & node : dominatorGraph.Items())
468 {
469 for (auto & outedge2 : node->OutEdges())
470 {
471 if (!dominatorGraph.Contains(outedge2.sink()))
472 {
473 c.edges[&outedge].insert(&outedge2);
474 c.points.insert(outedge2.sink());
475 }
476 }
477 }
478 }
479
480 return c;
481}
482
483static void
485{
486 auto & cfg = entry.cfg();
487
488 auto & headBranch = ComputeHeadBranch(entry, exit);
489 if (&headBranch == &exit)
490 return;
491
492 JLM_ASSERT(is<BasicBlock>(&headBranch));
493 auto & hbb = *static_cast<BasicBlock *>(&headBranch);
494
495 auto [continuationPoints, continuationEdgesDict] = ComputeContinuation(headBranch);
496 JLM_ASSERT(!continuationPoints.IsEmpty());
497
498 if (continuationPoints.Size() == 1)
499 {
500 const auto continuationPoint = *continuationPoints.Items().begin();
501 for (auto & outedge : headBranch.OutEdges())
502 {
503 auto continuationEdges = continuationEdgesDict[&outedge];
504
505 // Empty branch subgraph
506 if (outedge.sink() == continuationPoint)
507 {
508 outedge.split();
509 continue;
510 }
511
512 // only one continuation edge
513 if (continuationEdges.Size() == 1)
514 {
515 const auto continuationEdge = *continuationEdges.Items().begin();
516 JLM_ASSERT(continuationEdge != &outedge);
517 RestructureBranches(*outedge.sink(), *continuationEdge->source());
518 continue;
519 }
520
521 // more than one continuation edge
522 auto nullNode = BasicBlock::create(cfg);
523 nullNode->add_outedge(continuationPoint);
524 for (const auto & e : continuationEdges.Items())
525 e->divert(nullNode);
526 RestructureBranches(*outedge.sink(), *nullNode);
527 }
528
529 // Restructure tail subgraph
530 RestructureBranches(*continuationPoint, exit);
531 return;
532 }
533
534 // insert new continuation point
535 auto p = CreateContinuationVariable(hbb, rvsdg::ControlType::Create(continuationPoints.Size()));
536 auto continuationNode = BasicBlock::create(cfg);
537 AppendBranch(*continuationNode, p);
538 std::unordered_map<ControlFlowGraphNode *, size_t> indices;
539 for (const auto & cp : continuationPoints.Items())
540 {
541 continuationNode->add_outedge(cp);
542 indices.insert({ cp, indices.size() });
543 }
544
545 // Restructure branch subgraphs
546 for (auto & outedge : headBranch.OutEdges())
547 {
548 auto continuationEdges = continuationEdgesDict[&outedge];
549
550 auto nullNode = BasicBlock::create(cfg);
551 nullNode->add_outedge(continuationNode);
552 for (const auto & e : continuationEdges.Items())
553 {
554 auto bb = BasicBlock::create(cfg);
555 AppendConstantAssignment(*bb, *p, indices[e->sink()]);
556 bb->add_outedge(nullNode);
557 e->divert(bb);
558 }
559
560 RestructureBranches(*outedge.sink(), *nullNode);
561 }
562
563 // Restructure tail subgraph
564 RestructureBranches(*continuationNode, exit);
565}
566
567void
569{
570 JLM_ASSERT(is_closed(cfg));
571
572 std::vector<TailControlledLoop> loops;
573 RestructureLoops(*cfg.entry(), *cfg.exit(), loops);
574
575 for (const auto & loop : loops)
576 ReinsertLoop(loop);
577}
578
579void
586
587static void
589 ControlFlowGraphNode & entry,
591 std::vector<TailControlledLoop> & tailControlledLoops)
592{
593 RestructureLoops(entry, exit, tailControlledLoops);
594 RestructureBranches(entry, exit);
595}
596
597void
599{
600 JLM_ASSERT(is_closed(cfg));
601
602 std::vector<TailControlledLoop> loops;
603 RestructureControlFlow(*cfg.entry(), *cfg.exit(), loops);
604
605 for (const auto & loop : loops)
606 ReinsertLoop(loop);
607
609}
610
611}
std::vector< rvsdg::Node * > nodes
static std::unique_ptr< llvm::ThreeAddressCode > create(const Variable *rhs, const Variable *lhs)
llvm::ThreeAddressCode * append_last(std::unique_ptr< llvm::ThreeAddressCode > tac)
ThreeAddressCode * last() const noexcept
llvm::ThreeAddressCode * insert_before_branch(std::unique_ptr< llvm::ThreeAddressCode > tac)
static BasicBlock * create(ControlFlowGraph &cfg)
static std::unique_ptr< llvm::ThreeAddressCode > create(size_t nalternatives, const Variable *operand)
ControlFlowGraphNode * sink() const noexcept
Definition cfg-node.hpp:57
bool is_branch() const noexcept
Definition cfg-node.hpp:196
inedge_iterator_range InEdges() const
Definition cfg-node.hpp:163
ControlFlowGraph & cfg() const noexcept
Definition cfg-node.hpp:106
size_t NumInEdges() const noexcept
Definition cfg-node.cpp:63
ControlFlowGraphEdge * OutEdge(size_t n) const
Definition cfg-node.hpp:115
outedge_iterator_range OutEdges() const
Definition cfg-node.hpp:122
ControlFlowGraphEdge * add_outedge(ControlFlowGraphNode *sink)
Definition cfg-node.hpp:130
size_t NumOutEdges() const noexcept
Definition cfg-node.cpp:46
void divert_inedges(llvm::ControlFlowGraphNode *new_successor)
Definition cfg-node.hpp:171
ExitNode * exit() const noexcept
Definition cfg.hpp:212
EntryNode * entry() const noexcept
Definition cfg.hpp:206
Strongly Connected Component Structure.
static std::unique_ptr< StronglyConnectedComponentStructure > Create(const StronglyConnectedComponent &scc)
static std::unique_ptr< llvm::ThreeAddressCode > create(std::unique_ptr< rvsdg::SimpleOperation > operation, const std::vector< const Variable * > &operands)
Definition tac.hpp:135
const ThreeAddressCodeVariable * result(size_t index) const noexcept
Definition tac.hpp:109
static jlm::rvsdg::Output * Create(rvsdg::Region &region, std::shared_ptr< const jlm::rvsdg::Type > type)
const jlm::rvsdg::Type & type() const noexcept
Definition variable.hpp:56
static std::shared_ptr< const ControlType > Create(std::size_t nalternatives)
Instantiates control type.
Definition control.cpp:50
uint64_t nalternatives() const noexcept
Definition control.hpp:180
const_iterator begin() const
Definition control.hpp:208
size_t nbits() const noexcept
Definition control.hpp:202
uint64_t default_alternative() const noexcept
Definition control.hpp:196
#define JLM_ASSERT(x)
Definition common.hpp:16
Global memory state passed between functions.
static void RestructureLoopRepetition(const StronglyConnectedComponentStructure &sccStructure, ControlFlowGraphNode &newRepetitionNode, const ThreeAddressCodeVariable *entryVariable, const ThreeAddressCodeVariable &repetitionVariable)
std::vector< StronglyConnectedComponent > find_sccs(const ControlFlowGraph &cfg)
static void RestructureBranches(ControlFlowGraphNode &entry, ControlFlowGraphNode &exit)
static void AppendBranch(BasicBlock &basicBlock, const Variable *operand)
static Continuation ComputeContinuation(const ControlFlowGraphNode &headBranch)
static void RestructureControlFlow(ControlFlowGraphNode &, ControlFlowGraphNode &, std::vector< TailControlledLoop > &)
static void RestructureLoopExit(const StronglyConnectedComponentStructure &sccStructure, BasicBlock &newRepetitionNode, BasicBlock &newExitNode, ControlFlowGraphNode &regionExit, const ThreeAddressCodeVariable &repetitionVariable, const ThreeAddressCodeVariable *exitVariable)
static void RestructureLoops(ControlFlowGraphNode &regionEntry, ControlFlowGraphNode &regionExit, std::vector< TailControlledLoop > &loops)
static void AppendConstantAssignment(BasicBlock &basicBlock, const ThreeAddressCodeVariable &variable, const size_t value)
static void ReinsertLoop(const TailControlledLoop &loop)
static const ThreeAddressCodeVariable * CreateContinuationVariable(BasicBlock &bb, std::shared_ptr< const rvsdg::ControlType > type)
static BasicBlock * GetEntryVariableBlock(ControlFlowGraphNode *node)
static util::HashSet< ControlFlowGraphNode * > ComputeDominatorGraph(const ControlFlowGraphEdge *edge)
static void adjustLoopRepetitionEdge(const StronglyConnectedComponentStructure &sccStructure)
static TailControlledLoop ExtractLoop(ControlFlowGraphNode &loopEntry, ControlFlowGraphNode &loopExit)
static const ThreeAddressCodeVariable & CreateLoopRepetitionVariable(BasicBlock &basicBlock)
static const ThreeAddressCodeVariable & CreateLoopExitVariable(BasicBlock &bb, std::shared_ptr< const rvsdg::ControlType > type)
bool is_proper_structured(const ControlFlowGraph &cfg)
static const ThreeAddressCodeVariable & CreateLoopEntryVariable(BasicBlock &bb, std::shared_ptr< const rvsdg::ControlType > type)
static std::unique_ptr< rvsdg::MatchOperation > invertMatchOperation(const rvsdg::MatchOperation &matchOperation)
bool is_closed(const ControlFlowGraph &cfg)
static bool is_acyclic(const ControlFlowGraph &cfg)
static ControlFlowGraphNode & ComputeHeadBranch(ControlFlowGraphNode &start, ControlFlowGraphNode &end)
static void RestructureLoopEntry(const StronglyConnectedComponentStructure &sccStructure, BasicBlock *newEntryNode, const ThreeAddressCodeVariable *entryVariable)
static std::string strfmt(Args... args)
Definition strfmt.hpp:35
util::HashSet< ControlFlowGraphNode * > points
std::unordered_map< ControlFlowGraphEdge *, util::HashSet< ControlFlowGraphEdge * > > edges
TailControlledLoop(ControlFlowGraphNode *entry, BasicBlock *i, BasicBlock *r)