208 lines
9.7 KiB
Java
208 lines
9.7 KiB
Java
|
package ch.usi.inf.sp.dom;
|
||
|
|
||
|
import ch.usi.inf.sp.cfg.BasicBlock;
|
||
|
import ch.usi.inf.sp.cfg.ControlFlowGraph;
|
||
|
import org.junit.jupiter.api.Test;
|
||
|
|
||
|
import java.util.Arrays;
|
||
|
|
||
|
import static ch.usi.inf.sp.dom.DominatorTreeAssertions.*;
|
||
|
import static org.junit.jupiter.api.Assertions.*;
|
||
|
|
||
|
class DominatorAnalyzerTest {
|
||
|
|
||
|
@Test
|
||
|
void analyzeEntryExit() {
|
||
|
ControlFlowGraph cfg = new ControlFlowGraph();
|
||
|
cfg.addFallthroughEdge(cfg.getEntry(), cfg.getExit());
|
||
|
System.out.println(cfg);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertPathToRoot(t, Arrays.asList(cfg.getExit(), cfg.getEntry()));
|
||
|
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||
|
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||
|
assertTrue(t.getRoot().getOutEdges().stream().map(e->e.getTo()).anyMatch(d -> d==t.getNodeForBlock(cfg.getExit())), "Root must have child holding exit block");
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
void analyzeEntryNodeExit() {
|
||
|
ControlFlowGraph cfg = new ControlFlowGraph();
|
||
|
BasicBlock b0 = new BasicBlock(0);
|
||
|
cfg.addNode(b0);
|
||
|
cfg.addFallthroughEdge(cfg.getEntry(), b0);
|
||
|
cfg.addFallthroughEdge(b0, cfg.getExit());
|
||
|
System.out.println(cfg);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||
|
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||
|
assertSame(b0, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding b0 block");
|
||
|
assertEquals(1, t.getRoot().getOutEdges().get(0).getTo().getOutEdges().size(), "Root must have child that has one out edge");
|
||
|
assertSame(cfg.getExit(), t.getRoot().getOutEdges().get(0).getTo().getOutEdges().get(0).getTo().getBlock(), "Root must have child that has child holding exit block");
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
void analyzeIfThen() {
|
||
|
ControlFlowGraph cfg = new ControlFlowGraph();
|
||
|
BasicBlock bIf = new BasicBlock(0);
|
||
|
cfg.addNode(bIf);
|
||
|
BasicBlock bThen = new BasicBlock(1);
|
||
|
cfg.addNode(bThen);
|
||
|
cfg.addFallthroughEdge(cfg.getEntry(), bIf);
|
||
|
cfg.addFallthroughEdge(bIf, cfg.getExit());
|
||
|
cfg.addBranchTakenEdge(bIf, bThen);
|
||
|
cfg.addFallthroughEdge(bThen, cfg.getExit());
|
||
|
System.out.println(cfg);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||
|
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||
|
assertSame(bIf, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding bIf block");
|
||
|
assertEquals(2, t.getRoot().getOutEdges().get(0).getTo().getOutEdges().size(), "Root must have child that has two out edges");
|
||
|
assertTrue(t.getRoot().getOutEdges().get(0).getTo().getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bThen), "Root must have child that has child holding bThen block");
|
||
|
assertTrue(t.getRoot().getOutEdges().get(0).getTo().getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==cfg.getExit()), "Root must have child that has child holding exit block");
|
||
|
assertEquals(0, t.getNodeForBlock(bThen).getOutEdges().size());
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
void analyzeIfThenElse() {
|
||
|
ControlFlowGraph cfg = new ControlFlowGraph();
|
||
|
BasicBlock bIf = new BasicBlock(0);
|
||
|
cfg.addNode(bIf);
|
||
|
BasicBlock bThen = new BasicBlock(1);
|
||
|
cfg.addNode(bThen);
|
||
|
BasicBlock bElse = new BasicBlock(2);
|
||
|
cfg.addNode(bElse);
|
||
|
cfg.addFallthroughEdge(cfg.getEntry(), bIf);
|
||
|
cfg.addBranchTakenEdge(bIf, bElse);
|
||
|
cfg.addFallthroughEdge(bIf, bThen);
|
||
|
cfg.addFallthroughEdge(bElse, cfg.getExit());
|
||
|
cfg.addFallthroughEdge(bThen, cfg.getExit());
|
||
|
System.out.println(cfg);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||
|
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||
|
assertSame(bIf, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding bIf block");
|
||
|
assertEquals(3, t.getNodeForBlock(bIf).getOutEdges().size(), "bIf must have three out edges");
|
||
|
assertTrue(t.getNodeForBlock(bIf).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bThen), "bIf must have child holding bThen block");
|
||
|
assertTrue(t.getNodeForBlock(bIf).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bElse), "bIf must have child holding bElse block");
|
||
|
assertTrue(t.getNodeForBlock(bIf).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==cfg.getExit()), "bIf must have child holding exit block");
|
||
|
assertEquals(0, t.getNodeForBlock(bThen).getOutEdges().size());
|
||
|
assertEquals(0, t.getNodeForBlock(bElse).getOutEdges().size());
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
void analyzeLoop() {
|
||
|
ControlFlowGraph cfg = new ControlFlowGraph();
|
||
|
BasicBlock bHeader = new BasicBlock(0);
|
||
|
cfg.addNode(bHeader);
|
||
|
BasicBlock bBody = new BasicBlock(1);
|
||
|
cfg.addNode(bBody);
|
||
|
cfg.addFallthroughEdge(cfg.getEntry(), bHeader);
|
||
|
cfg.addBranchTakenEdge(bHeader, bBody);
|
||
|
cfg.addFallthroughEdge(bHeader, cfg.getExit());
|
||
|
cfg.addFallthroughEdge(bBody, bHeader);
|
||
|
System.out.println(cfg);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(cfg);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertSame(cfg.getEntry(), t.getRoot().getBlock(), "Root must hold entry block");
|
||
|
assertEquals(1, t.getRoot().getOutEdges().size(), "Root must have one out edge");
|
||
|
assertSame(bHeader, t.getRoot().getOutEdges().get(0).getTo().getBlock(), "Root must have child holding bHeader block");
|
||
|
assertEquals(2, t.getNodeForBlock(bHeader).getOutEdges().size(), "bHeader must have two out edges");
|
||
|
assertTrue(t.getNodeForBlock(bHeader).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==bBody), "bHeader must have child holding bBody block");
|
||
|
assertTrue(t.getNodeForBlock(bHeader).getOutEdges().stream().map(e->e.getTo()).anyMatch(d->d.getBlock()==cfg.getExit()), "bHeader must have child holding exit block");
|
||
|
assertEquals(0, t.getNodeForBlock(bBody).getOutEdges().size(), "bBody must have zero out edges");
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
void analyzeCooperFigure2() {
|
||
|
// This is an irreducible graph
|
||
|
// (and it doesn't have an exit edge, so we "abuse" the CFG a bit, which is fine)
|
||
|
ControlFlowGraph g = new ControlFlowGraph();
|
||
|
BasicBlock b5 = g.getEntry();
|
||
|
BasicBlock b4 = new BasicBlock(4);
|
||
|
g.addNode(b4);
|
||
|
BasicBlock b3 = new BasicBlock(3);
|
||
|
g.addNode(b3);
|
||
|
BasicBlock b2 = new BasicBlock(2);
|
||
|
g.addNode(b2);
|
||
|
BasicBlock b1 = g.getExit();
|
||
|
g.addFallthroughEdge(b5, b3);
|
||
|
g.addFallthroughEdge(b5, b4);
|
||
|
g.addFallthroughEdge(b3, b2);
|
||
|
g.addFallthroughEdge(b4, b1);
|
||
|
g.addFallthroughEdge(b2, b1);
|
||
|
g.addFallthroughEdge(b1, b2);
|
||
|
System.out.println(g);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(g);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertPathToRoot(t, Arrays.asList(b4, b5));
|
||
|
assertPathToRoot(t, Arrays.asList(b3, b5));
|
||
|
assertPathToRoot(t, Arrays.asList(b2, b5));
|
||
|
assertPathToRoot(t, Arrays.asList(b1, b5));
|
||
|
}
|
||
|
|
||
|
@Test
|
||
|
void analyzeCooperFigure4() {
|
||
|
// This is an irreducible graph
|
||
|
// (and it doesn't have an exit edge, so we "abuse" the CFG a bit, which is fine)
|
||
|
ControlFlowGraph g = new ControlFlowGraph();
|
||
|
BasicBlock b6 = g.getEntry();
|
||
|
BasicBlock b5 = new BasicBlock(5);
|
||
|
g.addNode(b5);
|
||
|
BasicBlock b4 = new BasicBlock(4);
|
||
|
g.addNode(b4);
|
||
|
BasicBlock b3 = new BasicBlock(3);
|
||
|
g.addNode(b3);
|
||
|
BasicBlock b2 = new BasicBlock(2);
|
||
|
g.addNode(b2);
|
||
|
BasicBlock b1 = g.getExit();
|
||
|
g.addFallthroughEdge(b6, b4);
|
||
|
g.addFallthroughEdge(b6, b5);
|
||
|
g.addFallthroughEdge(b4, b3);
|
||
|
g.addFallthroughEdge(b4, b2);
|
||
|
g.addFallthroughEdge(b3, b2);
|
||
|
g.addFallthroughEdge(b2, b1);
|
||
|
g.addFallthroughEdge(b2, b3);
|
||
|
g.addFallthroughEdge(b1, b2);
|
||
|
g.addFallthroughEdge(b5, b1);
|
||
|
System.out.println(g);
|
||
|
|
||
|
DominatorTree t = DominatorAnalyzer.analyze(g);
|
||
|
System.out.println(t);
|
||
|
assertAllNodesMapToTheirBlocks(t);
|
||
|
assertNodesHaveCorrectNumberOfParents(t);
|
||
|
assertChildrenPointToParent(t);
|
||
|
assertPathToRoot(t, Arrays.asList(b5, b6));
|
||
|
assertPathToRoot(t, Arrays.asList(b4, b6));
|
||
|
assertPathToRoot(t, Arrays.asList(b3, b6));
|
||
|
assertPathToRoot(t, Arrays.asList(b2, b6));
|
||
|
assertPathToRoot(t, Arrays.asList(b1, b6));
|
||
|
}
|
||
|
|
||
|
}
|