/* * @(#)Dominators.java */ package org.jf.dexlib.Code.Analysis.ssa.dom; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.jgrapht.DirectedGraph; /** * Computation of dominators in a flow graph. Algorithm see "A fast algorithm * for finding dominators in a flowgraph" from Lengauer and Tarjan, TOPLAS 1979 * * The dominators (DOM) of a node n in a directed graph with a unique start node (called flowgraph) * are all nodes that lie on every path from the start node to node n. Including the node n itself. * So when you cut the graph at any of node n's dominators, n cannot be reached from the * start anymore. * * Strict dominators (SDOM) of a node n are the same as dominators of node n, excluding the node n itself. * * As all nodes in the flowgraph have to be reachable from the start node (per definition), every node * has at least a single strict dominator - except the start node itself. * * So for each node n the set of strict dominantors SDOM(n) always contains a single dominator * called immediate dominator (IDOM). This is the only node in SDOM(n) that does not dominate any other * node in SDOM(n). There is always exactly one node that fulfills this property (theres a proof -> google) * This node the dominator that is "closest" to n. * * E.g. * <pre> * start -> n1 -> n2 -> n -\ * \-> n3 -> n4 -> n5 * * DOM(n) = {start, n1, n2, n} * SDOM(n) = {start, n1, n2} * IDOM(n) = n2 * * SDOM(n5) = {start, n1} * IDOM(n5) = n1 * </pre> * * As every node has a single immediate dominator, we can construct a graph in tree form where the * parent of each node is its immediate dominator. This tree is called dominator tree. * * E.g. * <pre> * start -> n1 -> n2 -> n * | \-> n3 -> n4 * \-> n5 * </pre> * * @param <V> Type of the nodes in the flowgraph * @param <E> Type of the edges in the flowgraph * * @author Patrick Kuhn */ public final class Dominators<V, E> { // Input stuff /** The CFG. */ protected final DirectedGraph<V, E> graph; /** Start node. */ protected final V start; // DOM Stuff /** The DOM Tree. */ protected final DomTree<V> domTree; /** Number of nodes. */ protected int N; protected final Map<V, Integer> dfnum; protected final Map<V, Set<V>> bucket; protected final Map<V, V> semi; protected final Map<V, V> idom; protected final Map<V, V> samedom; protected final Map<Integer, V> vertex; protected final Map<V, V> parent; protected final Map<V, V> ancestor; /** The DF. */ protected Map<V, Set<V>> domFrontier; private Set<V> visited; /** * Compute the DOM. * @param <Y> type of vertices * @param <Z> type of edges * @param graph the control flow graph to convert * @param entry first node of CFG * @return the DOM */ public static <Y, Z> Dominators<Y, Z> compute(DirectedGraph<Y, Z> graph, Y entry) { if (graph == null || entry == null) { throw new IllegalArgumentException("Must not be null!"); } Dominators<Y, Z> dom = new Dominators<Y, Z>(graph, entry); dom.compute(); return dom; } /** * Constructor. <tt>compute</tt> must be called afterwards! * @param graph the CFG * @param start the first node */ @SuppressWarnings("unchecked") private Dominators(final DirectedGraph<V, E> graph, final V start) { assert graph != null && start != null; this.graph = graph; this.start = start; this.domTree = new DomTree<V>(); this.N = 0; // forall n : dfnum[n] := 0 // forall n : bucket[n] := { } this.dfnum = new HashMap<V, Integer>(graph.vertexSet().size()); this.bucket = new HashMap<V, Set<V>>(graph.vertexSet().size()); this.semi = new HashMap<V, V>(graph.vertexSet().size()); this.idom = new HashMap<V, V>(graph.vertexSet().size()); this.samedom = new HashMap<V, V>(graph.vertexSet().size()); this.vertex = new HashMap<Integer, V>(graph.vertexSet().size()); this.parent = new HashMap<V, V>(graph.vertexSet().size()); this.ancestor = new HashMap<V, V>(graph.vertexSet().size()); int i = 0; for (V node : graph.vertexSet()) { dfnum.put(node, 0); bucket.put(node, new HashSet<V>()); idom.put(node, null); samedom.put(node, null); vertex.put(i, node); ++i; } } //<editor-fold defaultstate="collapsed" desc="DOM calculation"> /** * Compute the DOM. */ @SuppressWarnings("unchecked") protected void compute() { // correct dfs(null, start); V n = null; for (int i = N - 1; i >= 1; --i) { n = vertex.get(i); V p = parent.get(n); V s = p; // Calculate semidominator of n // for each predecessor v of n for (E e : graph.incomingEdgesOf(n)) { V v = graph.getEdgeSource(e); V sn; if (dfnum.get(v) <= dfnum.get(n)) { sn = v; } else { sn = semi.get(ancestorWithLowestSemi(v)); } assert sn != null; if (dfnum.get(sn) < dfnum.get(s)) { s = sn; } } // link path from s to n to the forest semi.put(n, s); bucket.get(s).add(n); link(p, n); for (V v : bucket.get(p)) { V y = ancestorWithLowestSemi(v); if (semi.get(y) == semi.get(v)) { idom.put(v, p); } else { samedom.put(v, y); } } bucket.get(p).clear(); } for (int i = 1; i < N; ++i) { n = vertex.get(i); if (samedom.get(n) != null) { idom.put(n, idom.get(samedom.get(n))); } } // now build the tree for (V v : idom.keySet()) { V id = idom.get(v); addEdge(id, v); } // calculate DF domFrontier = new HashMap<V, Set<V>>(); visited = new HashSet<V>(); computeDF(start); } /** * In the forest, find the non-root ancestor of <tt>v</tt> that has * the lowest-numbered semidominator. * @param node the vertex * @return non-root ancestor of <tt>v</tt> that has the lowest-numbered semidominator. */ protected V ancestorWithLowestSemi(final V node) { // correct V u = node; V v = node; // while ancestor[v] != none while (ancestor.get(v) != null) { V semiV = semi.get(v); V semiU = semi.get(u); if (dfnum.get(semiV) < dfnum.get(semiU)) { u = v; } v = ancestor.get(v); } return u; } private void dfs(V p, V n) { assert n != null; if (dfnum.get(n) == 0) { dfnum.put(n, N); vertex.put(N, n); parent.put(n, p); ++N; for (E e : graph.outgoingEdgesOf(n)) { V w = graph.getEdgeTarget(e); dfs(n, w); } } } /** * Compute the DF for a node. One should start with the start node!! * @param node the node to compute the DF for */ private void computeDF(V node) { assert node != null; visited.add(node); Set<V> set = new HashSet<V>(); // compute DFlocal[n] for (E e : graph.outgoingEdgesOf(node)) { V y = graph.getEdgeTarget(e); // if idom[y] != n if (!idom.get(y).equals(node)) { set.add(y); } } // compute DFup[n] // children[n] == nodes whose immediate dominator n is for (V child : getChildren(node)) { if (!visited.contains(child)) { computeDF(child); } for (V w : domFrontier.get(child)) { if (node.equals(w) || !dominates(node, w)) { set.add(w); } } } domFrontier.put(node, set); } private Set<V> getChildren(final V n) { Set<V> result = new HashSet<V>(); for (DomEdge e : domTree.outgoingEdgesOf(n)) { V c = domTree.getEdgeTarget(e); result.add(c); result.addAll(getChildren(c)); } return result; } /** * Check whether n dominates w. * @param n possible dominator to test * @param w node which is dominated * @return <tt>true</tt> if <tt>n dom w</tt> or <tt>n == w</tt> */ public boolean dominates(final V n, final V w) { if (w == null) { return false; } else if (n.equals(w)) { return true; } return dominates(n, getIDom(w)); } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="helper"> /** * Add an Edge to the Dominator Tree. * If one or both of the vertices does not exist in this graph, they are added. * @param p source node * @param n target node */ protected void addEdge(final V p, final V n) { if (p == null || n == null) { return; } if (!domTree.containsVertex(n)) { domTree.addVertex(n); } if (!domTree.containsVertex(p)) { domTree.addVertex(p); } domTree.addEdge(p, n); } /** * Link two nodes. * @param p source node * @param n destination node */ protected void link(final V p, final V n) { assert p != null && n != null; ancestor.put(n, p); } //</editor-fold> //<editor-fold defaultstate="collapsed" desc="getter"> public V getIDom(final V node) { return idom.get(node); } public Iterable<V> getNodesWithIDom(final V node) { //TODO: getNodesWithIDOM throw new UnsupportedOperationException("getNodesWithIDom not yet implemented!"); } public DomTree<V> getDominationTree() { return domTree; } public Map<V, Set<V>> getDF() { return Collections.unmodifiableMap(domFrontier); } public V getStart() { return start; } //</editor-fold> }