summaryrefslogtreecommitdiff
path: root/test/example/domtree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'test/example/domtree.cpp')
-rw-r--r--test/example/domtree.cpp163
1 files changed, 163 insertions, 0 deletions
diff --git a/test/example/domtree.cpp b/test/example/domtree.cpp
new file mode 100644
index 000000000..e1b93ccc4
--- /dev/null
+++ b/test/example/domtree.cpp
@@ -0,0 +1,163 @@
+#include <cassert>
+#include <iostream>
+
+#include <cfg/domtree.h>
+#include <wasm.h>
+
+using namespace wasm;
+
+struct BasicBlock {
+ std::vector<BasicBlock*> in;
+
+ void addPred(BasicBlock* pred) { in.push_back(pred); }
+};
+
+struct CFG : public std::vector<std::unique_ptr<BasicBlock>> {
+ BasicBlock* add() {
+ emplace_back(std::make_unique<BasicBlock>());
+ return back().get();
+ }
+
+ void connect(BasicBlock* pred, BasicBlock* succ) { succ->addPred(pred); }
+};
+
+int main() {
+ // An empty CFG.
+ {
+ CFG cfg;
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.empty());
+ }
+
+ // An CFG with just an entry.
+ {
+ CFG cfg;
+ cfg.add();
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 1);
+ assert(domTree.iDoms[0] == Index(-1)); // the entry has no parent.
+ }
+
+ // entry -> a.
+ {
+ CFG cfg;
+ auto* entry = cfg.add();
+ auto* a = cfg.add();
+ cfg.connect(entry, a);
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 2);
+ assert(domTree.iDoms[0] == Index(-1));
+ assert(domTree.iDoms[1] == 0); // a is dominated by the entry.
+ }
+
+ // entry and a non-connected (unreachable) block.
+ {
+ CFG cfg;
+ auto* entry = cfg.add();
+ auto* a = cfg.add();
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 2);
+ assert(domTree.iDoms[0] == Index(-1));
+ assert(domTree.iDoms[1] == Index(-1)); // unreachables have no parent.
+ }
+
+ // entry -> a -> b -> c.
+ {
+ CFG cfg;
+ auto* entry = cfg.add();
+ auto* a = cfg.add();
+ auto* b = cfg.add();
+ auto* c = cfg.add();
+ cfg.connect(entry, a);
+ cfg.connect(a, b);
+ cfg.connect(b, c);
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 4);
+ assert(domTree.iDoms[0] == Index(-1));
+ assert(domTree.iDoms[1] == 0);
+ assert(domTree.iDoms[2] == 1);
+ assert(domTree.iDoms[3] == 2);
+ }
+
+ // An if:
+ // b
+ // / \
+ // entry -> a d
+ // \ /
+ // c
+ {
+ CFG cfg;
+ auto* entry = cfg.add();
+ auto* a = cfg.add();
+ auto* b = cfg.add();
+ auto* c = cfg.add();
+ auto* d = cfg.add();
+ cfg.connect(entry, a);
+ cfg.connect(a, b);
+ cfg.connect(a, c);
+ cfg.connect(b, d);
+ cfg.connect(c, d);
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 5);
+ assert(domTree.iDoms[0] == Index(-1));
+ assert(domTree.iDoms[1] == 0); // a
+ assert(domTree.iDoms[2] == 1); // b
+ assert(domTree.iDoms[3] == 1); // c
+ assert(domTree.iDoms[4] == 1); // d is also dominated by a.
+ }
+
+ // A loop:
+ //
+ // entry -> a -> b -> c -> d
+ // ^ |
+ // | |
+ // \---------/
+ {
+ CFG cfg;
+ auto* entry = cfg.add();
+ auto* a = cfg.add();
+ auto* b = cfg.add();
+ auto* c = cfg.add();
+ auto* d = cfg.add();
+ cfg.connect(entry, a);
+ cfg.connect(a, b);
+ cfg.connect(b, c);
+ cfg.connect(c, d);
+ cfg.connect(d, b);
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 5);
+ assert(domTree.iDoms[0] == Index(-1));
+ assert(domTree.iDoms[1] == 0); // a
+ assert(domTree.iDoms[2] == 1); // b, the loop entry, is dominated by a
+ assert(domTree.iDoms[3] == 2); // c, the loop "middle", is dominated by b
+ assert(domTree.iDoms[4] == 3); // d, the loop "end", is dominated by c
+ }
+
+ // Subsequent blocks after an unreachable one.
+ //
+ // entry a -> b
+ //
+ // (a is unreachable, and b is reached by a, but in unreachable code)
+ {
+ CFG cfg;
+ auto* entry = cfg.add();
+ auto* a = cfg.add();
+ auto* b = cfg.add();
+ cfg.connect(a, b);
+
+ DomTree<BasicBlock> domTree(cfg);
+ assert(domTree.iDoms.size() == 3);
+ assert(domTree.iDoms[0] == Index(-1));
+ assert(domTree.iDoms[1] == Index(-1)); // a
+ assert(domTree.iDoms[2] == Index(-1)); // b
+ }
+
+ std::cout << "success.\n";
+}