Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
dominators_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
38#include <gtest/gtest.h>
39#include <chrono>
40#include <cstdlib>
41#include <string>
42#include <random>
43
44#include <Dominators.H>
45#include <tpl_dynArray.H>
46#include <tpl_dynMapTree.H>
47#include <tpl_graph.H>
48
49using namespace Aleph;
50
51namespace
52{
54
55 DynArray<DG::Node *> make_nodes(DG & g, int n)
56 {
58 nodes.reserve(static_cast<size_t>(n));
59 for (int i = 0; i < n; ++i)
60 nodes(static_cast<size_t>(i)) = g.insert_node(i);
61 return nodes;
62 }
63
64 // Build idom map: node_info -> idom_info (-1 if root/no idom)
65 DynMapTree<int, int> idom_map(const DynList<std::pair<DG::Node *,
66 DG::Node *>> & idoms)
67 {
69 for (auto it = idoms.get_it(); it.has_curr(); it.next_ne())
70 {
71 auto [node, idom] = it.get_curr();
72 m.insert(node->get_info(), idom ? idom->get_info() : -1);
73 }
74 return m;
75 }
76
77} // namespace
78
79
80// ============================================================================
81// Basic Structure Tests
82// ============================================================================
83
85{
86 DG g;
87 auto nodes = make_nodes(g, 1);
88
89 auto idoms = compute_dominators(g, nodes[0]);
90 auto m = idom_map(idoms);
91
92 EXPECT_EQ(m.size(), 1U);
93 EXPECT_EQ(m[0], -1); // root has no idom
94}
95
97{
98 // 0 -> 1 -> 2 -> 3 -> 4
99 // idom: 0=root, 1<-0, 2<-1, 3<-2, 4<-3
100 DG g;
101 auto nodes = make_nodes(g, 5);
102 g.insert_arc(nodes[0], nodes[1]);
103 g.insert_arc(nodes[1], nodes[2]);
104 g.insert_arc(nodes[2], nodes[3]);
105 g.insert_arc(nodes[3], nodes[4]);
106
107 auto m = idom_map(compute_dominators(g, nodes[0]));
108
109 EXPECT_EQ(m[0], -1);
110 EXPECT_EQ(m[1], 0);
111 EXPECT_EQ(m[2], 1);
112 EXPECT_EQ(m[3], 2);
113 EXPECT_EQ(m[4], 3);
114}
115
117{
118 // 0
119 // / |
120 // 1 2
121 // \ /
122 // 3
123 // idom(1) = 0, idom(2) = 0, idom(3) = 0
124 DG g;
125 auto nodes = make_nodes(g, 4);
126 g.insert_arc(nodes[0], nodes[1]);
127 g.insert_arc(nodes[0], nodes[2]);
128 g.insert_arc(nodes[1], nodes[3]);
129 g.insert_arc(nodes[2], nodes[3]);
130
131 auto m = idom_map(compute_dominators(g, nodes[0]));
132
133 EXPECT_EQ(m[0], -1);
134 EXPECT_EQ(m[1], 0);
135 EXPECT_EQ(m[2], 0);
136 EXPECT_EQ(m[3], 0);
137}
138
140{
141 // Classic if-then-else CFG:
142 // 0 (entry)
143 // / |
144 // 1 2 (then / else)
145 // \ /
146 // 3 (join)
147 // |
148 // 4 (exit)
149 DG g;
150 auto nodes = make_nodes(g, 5);
151 g.insert_arc(nodes[0], nodes[1]);
152 g.insert_arc(nodes[0], nodes[2]);
153 g.insert_arc(nodes[1], nodes[3]);
154 g.insert_arc(nodes[2], nodes[3]);
155 g.insert_arc(nodes[3], nodes[4]);
156
157 auto m = idom_map(compute_dominators(g, nodes[0]));
158
159 EXPECT_EQ(m[0], -1);
160 EXPECT_EQ(m[1], 0);
161 EXPECT_EQ(m[2], 0);
162 EXPECT_EQ(m[3], 0);
163 EXPECT_EQ(m[4], 3);
164}
165
167{
168 // CFG with a loop:
169 // 0 (entry)
170 // |
171 // 1 (loop header) <---+
172 // / | |
173 // 2 3 (loop body) ----+
174 // |
175 // 4 (exit)
176 DG g;
177 auto nodes = make_nodes(g, 5);
178 g.insert_arc(nodes[0], nodes[1]);
179 g.insert_arc(nodes[1], nodes[2]);
180 g.insert_arc(nodes[1], nodes[3]);
181 g.insert_arc(nodes[3], nodes[1]); // back-edge
182 g.insert_arc(nodes[2], nodes[4]);
183
184 auto m = idom_map(compute_dominators(g, nodes[0]));
185
186 EXPECT_EQ(m[0], -1);
187 EXPECT_EQ(m[1], 0);
188 EXPECT_EQ(m[2], 1);
189 EXPECT_EQ(m[3], 1);
190 EXPECT_EQ(m[4], 2);
191}
192
194{
195 // Classic textbook example (Appel "Modern Compiler Implementation")
196 // 13 nodes (0..12), root = 0
197 //
198 // Edges:
199 // 0->1, 0->5, 0->9
200 // 1->2, 2->3, 3->4, 3->1 (back), 4->12
201 // 5->6, 6->7, 6->8, 7->4, 8->12
202 // 9->10, 10->11, 11->9 (back), 11->12
203 DG g;
204 auto n = make_nodes(g, 13);
205 g.insert_arc(n[0], n[1]);
206 g.insert_arc(n[0], n[5]);
207 g.insert_arc(n[0], n[9]);
208 g.insert_arc(n[1], n[2]);
209 g.insert_arc(n[2], n[3]);
210 g.insert_arc(n[3], n[4]);
211 g.insert_arc(n[3], n[1]); // back-edge
212 g.insert_arc(n[4], n[12]);
213 g.insert_arc(n[5], n[6]);
214 g.insert_arc(n[6], n[7]);
215 g.insert_arc(n[6], n[8]);
216 g.insert_arc(n[7], n[4]);
217 g.insert_arc(n[8], n[12]);
218 g.insert_arc(n[9], n[10]);
219 g.insert_arc(n[10], n[11]);
220 g.insert_arc(n[11], n[9]); // back-edge
221 g.insert_arc(n[11], n[12]);
222
223 auto m = idom_map(compute_dominators(g, n[0]));
224
225 // Root
226 EXPECT_EQ(m[0], -1);
227
228 // Branch 1: 0->1->2->3
229 EXPECT_EQ(m[1], 0);
230 EXPECT_EQ(m[2], 1);
231 EXPECT_EQ(m[3], 2);
232
233 // Node 4: reachable from both 3 and 7; idom = 0
234 EXPECT_EQ(m[4], 0);
235
236 // Branch 2: 0->5->6->{7,8}
237 EXPECT_EQ(m[5], 0);
238 EXPECT_EQ(m[6], 5);
239 EXPECT_EQ(m[7], 6);
240 EXPECT_EQ(m[8], 6);
241
242 // Branch 3: 0->9->10->11
243 EXPECT_EQ(m[9], 0);
244 EXPECT_EQ(m[10], 9);
245 EXPECT_EQ(m[11], 10);
246
247 // Node 12: reachable from 4, 8, 11; idom = 0
248 EXPECT_EQ(m[12], 0);
249}
250
251
252// ============================================================================
253// Unreachable Nodes and Edge Cases
254// ============================================================================
255
257{
258 // 0 -> 1 -> 2, but 3 is disconnected
259 DG g;
260 auto nodes = make_nodes(g, 4);
261 g.insert_arc(nodes[0], nodes[1]);
262 g.insert_arc(nodes[1], nodes[2]);
263 // Node 3 is unreachable
264
265 auto idoms = compute_dominators(g, nodes[0]);
266 auto m = idom_map(idoms);
267
268 // Only 3 reachable nodes
269 EXPECT_EQ(m.size(), 3U);
270 EXPECT_EQ(m.search(3), nullptr); // node 3 not in result
271}
272
274{
275 // 0 -> 1 -> 1 (self-loop)
276 DG g;
277 auto nodes = make_nodes(g, 2);
278 g.insert_arc(nodes[0], nodes[1]);
279 g.insert_arc(nodes[1], nodes[1]); // self-loop
280
281 auto m = idom_map(compute_dominators(g, nodes[0]));
282
283 EXPECT_EQ(m[0], -1);
284 EXPECT_EQ(m[1], 0);
285}
286
288{
289 DG g;
290 // Create algorithm instance for empty graph
292 // Empty graph has no nodes, so we can't call with a root.
293 // The algorithm should handle this gracefully.
294 EXPECT_TRUE(g.get_num_nodes() == 0);
295}
296
298{
299 DG g;
300 auto nodes = make_nodes(g, 2);
301 g.insert_arc(nodes[0], nodes[1]);
302
303 EXPECT_THROW(compute_dominators(g, nullptr), std::domain_error);
304}
305
306
307// ============================================================================
308// Dominator Tree Structure
309// ============================================================================
310
312{
313 // 0
314 // / |
315 // 1 2
316 // \ /
317 // 3
318 // |
319 // 4
320 DG g;
321 auto nodes = make_nodes(g, 5);
322 g.insert_arc(nodes[0], nodes[1]);
323 g.insert_arc(nodes[0], nodes[2]);
324 g.insert_arc(nodes[1], nodes[3]);
325 g.insert_arc(nodes[2], nodes[3]);
326 g.insert_arc(nodes[3], nodes[4]);
327
328 DG tree;
329 build_dominator_tree(g, nodes[0], tree);
330
331 // Tree should have 5 nodes and 4 arcs (n-1 for a tree)
332 EXPECT_EQ(tree.get_num_nodes(), 5U);
333 EXPECT_EQ(tree.get_num_arcs(), 4U);
334
335 // Verify node mapping
336 for (auto it = tree.get_node_it(); it.has_curr(); it.next_ne())
337 {
338 auto tn = it.get_curr();
339 auto orig = mapped_node<DG>(tn);
340 EXPECT_NE(orig, nullptr);
341 EXPECT_EQ(tn->get_info(), orig->get_info());
342 }
343}
344
346{
347 // 0->1->2->3: dom tree is the same chain
348 DG g;
349 auto nodes = make_nodes(g, 4);
350 g.insert_arc(nodes[0], nodes[1]);
351 g.insert_arc(nodes[1], nodes[2]);
352 g.insert_arc(nodes[2], nodes[3]);
353
354 DG tree;
355 build_dominator_tree(g, nodes[0], tree);
356
357 EXPECT_EQ(tree.get_num_nodes(), 4U);
358 EXPECT_EQ(tree.get_num_arcs(), 3U);
359}
360
361
362// ============================================================================
363// Dominance Checks
364// ============================================================================
365
367{
368 DG g;
369 auto nodes = make_nodes(g, 3);
370 g.insert_arc(nodes[0], nodes[1]);
371 g.insert_arc(nodes[1], nodes[2]);
372
374
375 // Every reachable node dominates itself
376 for (int i = 0; i < 3; ++i)
377 EXPECT_TRUE(alg.dominates(g, nodes[0], nodes[i], nodes[i]));
378}
379
381{
382 DG g;
383 auto nodes = make_nodes(g, 5);
384 g.insert_arc(nodes[0], nodes[1]);
385 g.insert_arc(nodes[0], nodes[2]);
386 g.insert_arc(nodes[1], nodes[3]);
387 g.insert_arc(nodes[2], nodes[4]);
388
390
391 for (int i = 0; i < 5; ++i)
392 EXPECT_TRUE(alg.dominates(g, nodes[0], nodes[0], nodes[i]));
393}
394
396{
397 // 0
398 // / |
399 // 1 2
400 // \ /
401 // 3
402 DG g;
403 auto nodes = make_nodes(g, 4);
404 g.insert_arc(nodes[0], nodes[1]);
405 g.insert_arc(nodes[0], nodes[2]);
406 g.insert_arc(nodes[1], nodes[3]);
407 g.insert_arc(nodes[2], nodes[3]);
408
410
411 // Node 1 does NOT dominate 3 (path 0->2->3 bypasses 1)
412 EXPECT_FALSE(alg.dominates(g, nodes[0], nodes[1], nodes[3]));
413 // Node 2 does NOT dominate 3 (path 0->1->3 bypasses 2)
414 EXPECT_FALSE(alg.dominates(g, nodes[0], nodes[2], nodes[3]));
415}
416
417
418// ============================================================================
419// Dominance Frontiers
420// ============================================================================
421
423{
424 // 0 (entry)
425 // / |
426 // 1 2
427 // \ /
428 // 3 (join)
429 DG g;
430 auto nodes = make_nodes(g, 4);
431 g.insert_arc(nodes[0], nodes[1]);
432 g.insert_arc(nodes[0], nodes[2]);
433 g.insert_arc(nodes[1], nodes[3]);
434 g.insert_arc(nodes[2], nodes[3]);
435
437
438 // DF(0) = {} (root dominates everything reachable)
439 EXPECT_TRUE(df.find(nodes[0]).is_empty());
440
441 // DF(1) = {3} (1 dominates itself. 3 has predecessors 1 and 2.
442 // 1 dominates pred 1 of 3, but 1 does NOT strictly dominate 3 (idom(3)=0).
443 // So 3 is in DF(1).
444 auto & df1 = df.find(nodes[1]);
445 EXPECT_EQ(df1.size(), 1U);
446 EXPECT_EQ(df1.get_first()->get_info(), 3);
447
448 // DF(2) = {3}
449 auto & df2 = df.find(nodes[2]);
450 EXPECT_EQ(df2.size(), 1U);
451 EXPECT_EQ(df2.get_first()->get_info(), 3);
452
453 // DF(3) = {} (3 has only one predecessor in the idom sense)
454 EXPECT_TRUE(df.find(nodes[3]).is_empty());
455}
456
458{
459 // 0 -> 1 -> 2
460 // ^ |
461 // +----+ (back-edge 2->1)
462 DG g;
463 auto nodes = make_nodes(g, 3);
464 g.insert_arc(nodes[0], nodes[1]);
465 g.insert_arc(nodes[1], nodes[2]);
466 g.insert_arc(nodes[2], nodes[1]); // back-edge
467
469
470 // Node 1 is a loop header with predecessors 0 and 2.
471 // DF(1) = {1} (1 dominates 2 which is pred of 1, but 1 doesn't strictly dom 1)
472 auto & df1 = df.find(nodes[1]);
473 EXPECT_EQ(df1.size(), 1U);
474 EXPECT_EQ(df1.get_first()->get_info(), 1);
475
476 // DF(2) = {1}
477 auto & df2 = df.find(nodes[2]);
478 EXPECT_EQ(df2.size(), 1U);
479 EXPECT_EQ(df2.get_first()->get_info(), 1);
480}
481
482
483// ============================================================================
484// get_idom tests
485// ============================================================================
486
488{
489 DG g;
490 auto nodes = make_nodes(g, 4);
491 g.insert_arc(nodes[0], nodes[1]);
492 g.insert_arc(nodes[1], nodes[2]);
493 g.insert_arc(nodes[2], nodes[3]);
494
496
497 DG::Node * n0 = nodes[0];
498 DG::Node * n1 = nodes[1];
499 DG::Node * n2 = nodes[2];
500 DG::Node * n3 = nodes[3];
501
502 EXPECT_EQ(alg.get_idom(g, n0, n0), nullptr); // root
503 EXPECT_EQ(alg.get_idom(g, n0, n1), n0);
504 EXPECT_EQ(alg.get_idom(g, n0, n2), n1);
505 EXPECT_EQ(alg.get_idom(g, n0, n3), n2);
506}
507
509{
510 DG g;
511 auto nodes = make_nodes(g, 2);
512 g.insert_arc(nodes[0], nodes[1]);
513
515 EXPECT_THROW(alg.get_idom(g, nodes[0], nullptr), std::domain_error);
516}
517
518
519// ============================================================================
520// Large / Random Graph
521// ============================================================================
522
524{
525 // Build a random DAG with ~500 nodes and verify structural properties
526 const int N = 500;
527 DG g;
528 auto nodes = make_nodes(g, N);
529
530 std::mt19937 rng(42);
531
532 // Add random forward edges (i -> j where j > i to ensure DAG)
533 for (int i = 0; i < N - 1; ++i)
534 {
535 // Each node has at least one outgoing edge
536 int j = i + 1 + static_cast<int>(rng() % std::min(5, N - i - 1));
537 if (j < N)
538 g.insert_arc(nodes[i], nodes[j]);
539
540 // Add a few more random edges
541 for (int k = 0; k < 2; ++k)
542 {
543 int t = i + 1 + static_cast<int>(rng() % (N - i - 1));
544 if (t < N)
545 g.insert_arc(nodes[i], nodes[t]);
546 }
547 }
548
549 auto idoms = compute_dominators(g, nodes[0]);
550 auto m = idom_map(idoms);
551
552 // Root should have no idom
553 EXPECT_EQ(m[0], -1);
554
555 // Every non-root reachable node should have an idom
556 for (auto it = m.get_it(); it.has_curr(); it.next_ne())
557 {
558 const auto & [node_id, idom_id] = it.get_curr();
559 if (node_id == 0)
560 continue;
561 EXPECT_GE(idom_id, 0) << "Node " << node_id << " has no idom";
562 }
563
564 // Verify dominator tree is a proper tree
565 DG tree;
566 build_dominator_tree(g, nodes[0], tree);
567 EXPECT_EQ(tree.get_num_nodes(), m.size());
568 EXPECT_EQ(tree.get_num_arcs(), m.size() - 1);
569}
570
572{
573 const char * run_perf = std::getenv("ALEPH_RUN_DOMINATORS_PERF");
574 if (run_perf == nullptr or std::string(run_perf) != "1")
575 GTEST_SKIP() << "Set ALEPH_RUN_DOMINATORS_PERF=1 to run dominators perf test";
576
577 constexpr int N = 5000;
578# ifdef NDEBUG
579 constexpr long long kBudgetMs = 9000;
580# else
581 constexpr long long kBudgetMs = 20000;
582# endif
583
584 DG g;
585 auto nodes = make_nodes(g, N);
586 std::mt19937 rng(0xD01A70u);
587
588 for (int i = 0; i < N - 1; ++i)
589 {
590 const int max_step = std::min(12, N - i - 1);
591 const int j = i + 1 + static_cast<int>(rng() % max_step);
592 g.insert_arc(nodes[i], nodes[j]);
593
594 for (int k = 0; k < 4; ++k)
595 {
596 const int t = i + 1 + static_cast<int>(rng() % (N - i - 1));
597 g.insert_arc(nodes[i], nodes[t]);
598 }
599 }
600
601 const auto t0 = std::chrono::steady_clock::now();
602 auto idoms = compute_dominators(g, nodes[0]);
603 auto m = idom_map(idoms);
604 DG tree;
605 build_dominator_tree(g, nodes[0], tree);
606 const auto elapsed_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
607 std::chrono::steady_clock::now() - t0).count();
608
609 EXPECT_EQ(m[0], -1);
610 EXPECT_EQ(tree.get_num_nodes(), m.size());
611 EXPECT_EQ(tree.get_num_arcs(), m.size() - 1);
612 EXPECT_LT(elapsed_ms, kBudgetMs)
613 << "Dominators perf regression: N=" << N
614 << ", elapsed_ms=" << elapsed_ms
615 << ", budget_ms=" << kBudgetMs;
616}
617
618
619// ============================================================================
620// Operator() alias
621// ============================================================================
622
624{
625 DG g;
626 auto nodes = make_nodes(g, 3);
627 g.insert_arc(nodes[0], nodes[1]);
628 g.insert_arc(nodes[1], nodes[2]);
629
630 DG tree1, tree2;
632
633 alg.build_tree(g, nodes[0], tree1);
634 alg(g, nodes[0], tree2);
635
636 EXPECT_EQ(tree1.get_num_nodes(), tree2.get_num_nodes());
637 EXPECT_EQ(tree1.get_num_arcs(), tree2.get_num_arcs());
638}
639
640
641// ============================================================================
642// Cache reuse test
643// ============================================================================
644
646{
647 DG g;
648 auto nodes = make_nodes(g, 4);
649 g.insert_arc(nodes[0], nodes[1]);
650 g.insert_arc(nodes[0], nodes[2]);
651 g.insert_arc(nodes[1], nodes[3]);
652 g.insert_arc(nodes[2], nodes[3]);
653
655
656 // First call computes
657 auto m1 = idom_map(alg.compute_idom(g, nodes[0]));
658 // Second call should reuse cache
659 auto m2 = idom_map(alg.compute_idom(g, nodes[0]));
660
661 EXPECT_EQ(m1, m2);
662 EXPECT_TRUE(alg.has_computation());
663 EXPECT_EQ(alg.get_graph(), &g);
664 DG::Node * root = nodes[0];
665 EXPECT_EQ(alg.get_root(), root);
666}
667
668
669// ============================================================================
670// String-typed graph
671// ============================================================================
672
674{
676
677 SDG g;
678 auto entry = g.insert_node("entry");
679 auto body = g.insert_node("body");
680 auto exit_node = g.insert_node("exit");
681
682 g.insert_arc(entry, body);
683 g.insert_arc(body, exit_node);
684
685 auto idoms = compute_dominators(g, entry);
686
688 for (auto it = idoms.get_it(); it.has_curr(); it.next_ne())
689 {
690 auto [node, idom] = it.get_curr();
691 m[node->get_info()] = idom ? idom->get_info() : "none";
692 }
693
694 EXPECT_EQ(m["entry"], "none");
695 EXPECT_EQ(m["body"], "entry");
696 EXPECT_EQ(m["exit"], "body");
697}
698
699
700// ============================================================================
701// Post-Dominator Tests
702// ============================================================================
703
704namespace
705{
706 // Build ipdom map: node_info -> ipdom_info (-1 if exit/no ipdom)
707 DynMapTree<int, int> ipdom_map(const DynList<std::pair<DG::Node*,
708 DG::Node*>> & ipdoms)
709 {
711 for (auto it = ipdoms.get_it(); it.has_curr(); it.next_ne())
712 {
713 auto [node, ipdom] = it.get_curr();
714 m[node->get_info()] = ipdom ? ipdom->get_info() : -1;
715 }
716 return m;
717 }
718} // namespace
719
721{
722 DG g;
723 auto nodes = make_nodes(g, 1);
724
726 auto m = ipdom_map(ipdoms);
727
728 EXPECT_EQ(m.size(), 1U);
729 EXPECT_EQ(m[0], -1); // exit has no ipdom
730}
731
733{
734 // 0 -> 1 -> 2 -> 3; exit = 3
735 // ipdom(2) = 3, ipdom(1) = 2, ipdom(0) = 1
736 DG g;
737 auto nodes = make_nodes(g, 4);
738 g.insert_arc(nodes[0], nodes[1]);
739 g.insert_arc(nodes[1], nodes[2]);
740 g.insert_arc(nodes[2], nodes[3]);
741
743
744 EXPECT_EQ(m[3], -1); // exit
745 EXPECT_EQ(m[2], 3);
746 EXPECT_EQ(m[1], 2);
747 EXPECT_EQ(m[0], 1);
748}
749
751{
752 // 0
753 // / |
754 // 1 2
755 // \ /
756 // 3
757 // exit = 3; ipdom(1) = 3, ipdom(2) = 3, ipdom(0) = 3
758 DG g;
759 auto nodes = make_nodes(g, 4);
760 g.insert_arc(nodes[0], nodes[1]);
761 g.insert_arc(nodes[0], nodes[2]);
762 g.insert_arc(nodes[1], nodes[3]);
763 g.insert_arc(nodes[2], nodes[3]);
764
766
767 EXPECT_EQ(m[3], -1);
768 EXPECT_EQ(m[1], 3);
769 EXPECT_EQ(m[2], 3);
770 EXPECT_EQ(m[0], 3);
771}
772
774{
775 // Classic if-then-else CFG:
776 // 0 (entry)
777 // / |
778 // 1 2 (then / else)
779 // \ /
780 // 3 (join)
781 // |
782 // 4 (exit)
783 // ipdom(3) = 4, ipdom(1) = 3, ipdom(2) = 3, ipdom(0) = 3
784 DG g;
785 auto nodes = make_nodes(g, 5);
786 g.insert_arc(nodes[0], nodes[1]);
787 g.insert_arc(nodes[0], nodes[2]);
788 g.insert_arc(nodes[1], nodes[3]);
789 g.insert_arc(nodes[2], nodes[3]);
790 g.insert_arc(nodes[3], nodes[4]);
791
793
794 EXPECT_EQ(m[4], -1);
795 EXPECT_EQ(m[3], 4);
796 EXPECT_EQ(m[1], 3);
797 EXPECT_EQ(m[2], 3);
798 EXPECT_EQ(m[0], 3);
799}
800
802{
803 // CFG with a loop:
804 // 0 (entry)
805 // |
806 // 1 (loop header) <---+
807 // / | |
808 // 2 3 (loop body) ----+
809 // |
810 // 4 (exit)
811 // exit = 4; ipdom(2) = 4, ipdom(1) = 2, ipdom(0) = 1
812 // Node 3 cannot reach 4 (it only loops back to 1), so it may or may
813 // not appear in the post-dominator result depending on reachability
814 // in the reversed graph.
815 DG g;
816 auto nodes = make_nodes(g, 5);
817 g.insert_arc(nodes[0], nodes[1]);
818 g.insert_arc(nodes[1], nodes[2]);
819 g.insert_arc(nodes[1], nodes[3]);
820 g.insert_arc(nodes[3], nodes[1]); // back-edge
821 g.insert_arc(nodes[2], nodes[4]);
822
824
825 EXPECT_EQ(m[4], -1);
826 EXPECT_EQ(m[2], 4);
827 EXPECT_EQ(m[1], 2);
828 EXPECT_EQ(m[0], 1);
829}
830
832{
833 // Every node post-dominates itself
834 // 0 -> 1 -> 2
835 DG g;
836 auto nodes = make_nodes(g, 3);
837 g.insert_arc(nodes[0], nodes[1]);
838 g.insert_arc(nodes[1], nodes[2]);
839
841
842 for (int i = 0; i < 3; ++i)
843 EXPECT_TRUE(alg.post_dominates(g, nodes[2], nodes[i], nodes[i]));
844}
845
847{
848 // The exit node post-dominates every node that can reach it
849 // 0 -> 1 -> 2 -> 3
850 DG g;
851 auto nodes = make_nodes(g, 4);
852 g.insert_arc(nodes[0], nodes[1]);
853 g.insert_arc(nodes[1], nodes[2]);
854 g.insert_arc(nodes[2], nodes[3]);
855
857
858 for (int i = 0; i < 4; ++i)
859 EXPECT_TRUE(alg.post_dominates(g, nodes[3], nodes[3], nodes[i]));
860}
861
863{
864 // Diamond graph for PDF:
865 // 0
866 // / |
867 // 1 2
868 // \ /
869 // 3
870 // exit = 3
871 // In reversed graph: 3->{1,2}, 1->0, 2->0. Root=3.
872 // Dom on reversed: idom(1)=3, idom(2)=3, idom(0)=3
873 // DF on reversed at node 0: {0 has preds 1,2 in rev. Runner from 1: 1!=idom(0)=3 → DF(1)+=0,
874 // runner=idom(1)=3=idom(0) stop. Runner from 2: 2!=3 → DF(2)+=0, runner=3 stop.}
875 // PDF(1) = DF_rev(1) mapped back = {0}
876 // PDF(2) = DF_rev(2) mapped back = {0}
877 // PDF(0) = {} (only one "predecessor" in rev)
878 // PDF(3) = {}
879 DG g;
880 auto nodes = make_nodes(g, 4);
881 g.insert_arc(nodes[0], nodes[1]);
882 g.insert_arc(nodes[0], nodes[2]);
883 g.insert_arc(nodes[1], nodes[3]);
884 g.insert_arc(nodes[2], nodes[3]);
885
887
888 // PDF(3) = {} (exit)
889 EXPECT_TRUE(pdf.find(nodes[3]).is_empty());
890
891 // PDF(1) = {0}
892 auto & pdf1 = pdf.find(nodes[1]);
893 EXPECT_EQ(pdf1.size(), 1U);
894 EXPECT_EQ(pdf1.get_first()->get_info(), 0);
895
896 // PDF(2) = {0}
897 auto & pdf2 = pdf.find(nodes[2]);
898 EXPECT_EQ(pdf2.size(), 1U);
899 EXPECT_EQ(pdf2.get_first()->get_info(), 0);
900
901 // PDF(0) = {}
902 EXPECT_TRUE(pdf.find(nodes[0]).is_empty());
903}
904
906{
907 // 0 -> 1 -> 2 -> 3
908 // exit = 3; post-dom tree: 3->2->1->0 (chain)
909 DG g;
910 auto nodes = make_nodes(g, 4);
911 g.insert_arc(nodes[0], nodes[1]);
912 g.insert_arc(nodes[1], nodes[2]);
913 g.insert_arc(nodes[2], nodes[3]);
914
915 DG tree;
916 build_post_dominator_tree(g, nodes[3], tree);
917
918 // Tree should have 4 nodes and 3 arcs (n-1)
919 EXPECT_EQ(tree.get_num_nodes(), 4U);
920 EXPECT_EQ(tree.get_num_arcs(), 3U);
921
922 // Verify node mapping
923 for (auto it = tree.get_node_it(); it.has_curr(); it.next_ne())
924 {
925 auto tn = it.get_curr();
926 auto orig = mapped_node<DG>(tn);
927 EXPECT_NE(orig, nullptr);
928 EXPECT_EQ(tn->get_info(), orig->get_info());
929 }
930}
931
933{
934 DG g;
935 auto nodes = make_nodes(g, 2);
936 g.insert_arc(nodes[0], nodes[1]);
937
938 EXPECT_THROW(compute_post_dominators(g, nullptr), std::domain_error);
939}
Dominator and post-dominator tree computation via the Lengauer-Tarjan algorithm.
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3854
void reserve(const size_t l, const size_t r)
Allocate a range of entries.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
Generic key-value map implemented on top of a binary search tree.
Computes dominator tree and dominance frontiers of a digraph using the Lengauer-Tarjan algorithm.
Definition Dominators.H:148
void build_tree(GT &g, Node *root, GT &tree)
Build the dominator tree as a separate graph.
Definition Dominators.H:441
Computes post-dominator tree and post-dominance frontiers of a digraph using the Lengauer-Tarjan algo...
Definition Dominators.H:729
Key * search(const Key &key) const noexcept
searches the table for the key.
Definition tpl_odhash.H:543
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:222
constexpr size_t size() const noexcept
Returns the number of entries in the table.
Definition hashDry.H:619
Key * insert(const Key &key)
Inserts a key into the hash table (copy version).
Definition hashDry.H:203
#define TEST(name)
static mt19937 rng
#define N
Definition fib.C:294
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
void build_post_dominator_tree(const GT &g, typename GT::Node *exit_node, GT &tree, SA sa=SA())
Build the post-dominator tree of a digraph.
void build_dominator_tree(GT &g, typename GT::Node *root, GT &tree, SA sa=SA())
Build the dominator tree of a digraph.
Definition Dominators.H:657
DynList< std::pair< typename GT::Node *, typename GT::Node * > > compute_dominators(GT &g, typename GT::Node *root, SA sa=SA())
Compute immediate dominators of a digraph from a root node.
Definition Dominators.H:636
DynList< std::pair< typename GT::Node *, typename GT::Node * > > compute_post_dominators(const GT &g, typename GT::Node *exit_node, SA sa=SA())
Compute immediate post-dominators of a digraph from an exit node.
DynMapTree< typename GT::Node *, DynSetTree< typename GT::Node * > > compute_post_dominance_frontiers(const GT &g, typename GT::Node *exit_node, SA sa=SA())
Compute post-dominance frontiers of a digraph.
DynMapTree< typename GT::Node *, DynSetTree< typename GT::Node * > > compute_dominance_frontiers(GT &g, typename GT::Node *root, SA sa=SA())
Compute dominance frontiers of a digraph.
Definition Dominators.H:679
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
static long & df(typename GT::Node *p)
Internal helper: DFS discovery time stored in NODE_COUNTER(p).
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
static int * k
Lazy and scalable dynamic array implementation.
Dynamic key-value map based on balanced binary search trees.
Generic graph and digraph implementations.