| #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"; |
| } |