Home | History | Annotate | Download | only in dominators
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.ahat.dominators;
     18 
     19 import java.util.ArrayDeque;
     20 import java.util.ArrayList;
     21 import java.util.Deque;
     22 import java.util.List;
     23 import java.util.Queue;
     24 
     25 /**
     26  * Provides a static method for computing the immediate dominators of a
     27  * directed graph. It can be used with any directed graph data structure
     28  * that implements the {@link DominatorsComputation.Node} interface and has
     29  * some root node with no incoming edges.
     30  */
     31 public class DominatorsComputation {
     32   private DominatorsComputation() {
     33   }
     34 
     35   /**
     36    * Interface for a directed graph to perform immediate dominators
     37    * computation on.
     38    * The dominators computation can be used with directed graph data
     39    * structures that implement this <code>Node</code> interface. To use the
     40    * dominators computation on your graph, you must make the following
     41    * functionality available to the dominators computation:
     42    * <ul>
     43    * <li>Efficiently mapping from node to associated internal dominators
     44    *     computation state using the
     45    *     {@link #setDominatorsComputationState setDominatorsComputationState} and
     46    *     {@link #getDominatorsComputationState getDominatorsComputationState} methods.
     47    * <li>Iterating over all outgoing edges of an node using the
     48    *     {@link #getReferencesForDominators getReferencesForDominators} method.
     49    * <li>Setting the computed dominator for a node using the
     50    *     {@link #setDominator setDominator} method.
     51    * </ul>
     52    */
     53   public interface Node {
     54     /**
     55      * Associates the given dominator state with this node. Subsequent calls to
     56      * {@link #getDominatorsComputationState getDominatorsComputationState} on
     57      * this node should return the state given here. At the conclusion of the
     58      * dominators computation, this method will be called for
     59      * each node with <code>state</code> set to null.
     60      *
     61      * @param state the dominator state to associate with this node
     62      */
     63     void setDominatorsComputationState(Object state);
     64 
     65     /**
     66      * Returns the dominator state most recently associated with this node
     67      * by a call to {@link #setDominatorsComputationState setDominatorsComputationState}.
     68      * If <code>setDominatorsComputationState</code> has not yet been called
     69      * on this node for this dominators computation, this method should return
     70      * null.
     71      *
     72      * @return the associated dominator state
     73      */
     74     Object getDominatorsComputationState();
     75 
     76     /**
     77      * Returns a collection of nodes referenced from this node, for the
     78      * purposes of computing dominators. This method will be called at most
     79      * once for each node reachable from the root node of the dominators
     80      * computation.
     81      *
     82      * @return an iterable collection of the nodes with an incoming edge from
     83      *         this node.
     84      */
     85     Iterable<? extends Node> getReferencesForDominators();
     86 
     87     /**
     88      * Sets the dominator for this node based on the results of the dominators
     89      * computation.
     90      *
     91      * @param dominator the computed immediate dominator of this node
     92      */
     93     void setDominator(Node dominator);
     94   }
     95 
     96   // NodeS is information associated with a particular node for the
     97   // purposes of computing dominators.
     98   // By convention we use the suffix 'S' to name instances of NodeS.
     99   private static class NodeS {
    100     // The node that this NodeS is associated with.
    101     public Node node;
    102 
    103     // Unique identifier for this node, in increasing order based on the order
    104     // this node was visited in a depth first search from the root. In
    105     // particular, given nodes A and B, if A.id > B.id, then A cannot be a
    106     // dominator of B.
    107     public long id;
    108 
    109     // Upper bound on the id of this node's dominator.
    110     // The true immediate dominator of this node must have id <= domid.
    111     // This upper bound is slowly tightened as part of the dominators
    112     // computation.
    113     public long domid;
    114 
    115     // The current candidate dominator for this node.
    116     // Invariant: (domid < domS.id) implies this node is on the queue of
    117     // nodes to be revisited.
    118     public NodeS domS;
    119 
    120     // A node with a reference to this node that is one step closer to the
    121     // root than this node.
    122     // Invariant: srcS.id < this.id
    123     public NodeS srcS;
    124 
    125     // The largest id of the nodes we have seen so far on a path from the root
    126     // to this node. Used to keep track of which nodes we have already seen
    127     // and avoid processing them again.
    128     public long seenid;
    129 
    130     // The set of nodes X reachable by 'this' on a path of nodes from the
    131     // root with increasing ids (possibly excluding X) that this node does not
    132     // dominate (this.id > X.domid).
    133     // We can use a List instead of a Set for this because we guarentee based
    134     // on seenid that we don't add the same node more than once to the list.
    135     public List<NodeS> undom = new ArrayList<NodeS>();
    136   }
    137 
    138   private static class Link {
    139     public NodeS srcS;
    140     public Node dst;
    141 
    142     public Link(NodeS srcS, Node dst) {
    143       this.srcS = srcS;
    144       this.dst = dst;
    145     }
    146   }
    147 
    148   /**
    149    * Computes the immediate dominators of all nodes reachable from the <code>root</code> node.
    150    * There must not be any incoming references to the <code>root</code> node.
    151    * <p>
    152    * The result of this function is to call the {@link Node#setDominator}
    153    * function on every node reachable from the root node.
    154    *
    155    * @param root the root node of the dominators computation
    156    * @see Node
    157    */
    158   public static void computeDominators(Node root) {
    159     long id = 0;
    160 
    161     // List of all nodes seen. We keep track of this here to update all the
    162     // dominators once we are done.
    163     List<NodeS> nodes = new ArrayList<NodeS>();
    164 
    165     // The set of nodes N such that N.domid < N.domS.id. These nodes need
    166     // to be revisisted because their dominator is clearly wrong.
    167     // Use a Queue instead of a Set because performance will be better. We
    168     // avoid adding nodes already on the queue by checking whether it was
    169     // already true that N.domid < N.domS.id, in which case the node is
    170     // already on the queue.
    171     Queue<NodeS> revisit = new ArrayDeque<NodeS>();
    172 
    173     // Set up the root node specially.
    174     NodeS rootS = new NodeS();
    175     rootS.node = root;
    176     rootS.id = id++;
    177     root.setDominatorsComputationState(rootS);
    178 
    179     // 1. Do a depth first search of the nodes, label them with ids and come
    180     // up with intial candidate dominators for them.
    181     Deque<Link> dfs = new ArrayDeque<Link>();
    182     for (Node child : root.getReferencesForDominators()) {
    183       dfs.push(new Link(rootS, child));
    184     }
    185 
    186     while (!dfs.isEmpty()) {
    187       Link link = dfs.pop();
    188       NodeS dstS = (NodeS)link.dst.getDominatorsComputationState();
    189       if (dstS == null) {
    190         // This is the first time we have seen the node. The candidate
    191         // dominator is link src.
    192         dstS = new NodeS();
    193         dstS.node = link.dst;
    194         dstS.id = id++;
    195         dstS.domid = link.srcS.id;
    196         dstS.domS = link.srcS;
    197         dstS.srcS = link.srcS;
    198         dstS.seenid = dstS.domid;
    199         nodes.add(dstS);
    200         link.dst.setDominatorsComputationState(dstS);
    201 
    202         for (Node child : link.dst.getReferencesForDominators()) {
    203           dfs.push(new Link(dstS, child));
    204         }
    205       } else {
    206         // We have seen the node already. Update the state based on the new
    207         // potential dominator.
    208         NodeS srcS = link.srcS;
    209         boolean revisiting = dstS.domid < dstS.domS.id;
    210 
    211         while (srcS.id > dstS.seenid) {
    212           srcS.undom.add(dstS);
    213           srcS = srcS.srcS;
    214         }
    215         dstS.seenid = link.srcS.id;
    216 
    217         if (srcS.id < dstS.domid) {
    218           // In this case, dstS.domid must be wrong, because we just found a
    219           // path to dstS that does not go through dstS.domid:
    220           // All nodes from root to srcS have id < domid, and all nodes from
    221           // srcS to dstS had id > domid, so dstS.domid cannot be on this path
    222           // from root to dstS.
    223           dstS.domid = srcS.id;
    224           if (!revisiting) {
    225             revisit.add(dstS);
    226           }
    227         }
    228       }
    229     }
    230 
    231     // 2. Continue revisiting nodes until they all satisfy the requirement
    232     // that domS.id <= domid.
    233     while (!revisit.isEmpty()) {
    234       NodeS nodeS = revisit.poll();
    235       NodeS domS = nodeS.domS;
    236       assert nodeS.domid < domS.id;
    237       while (domS.id > nodeS.domid) {
    238         if (domS.domS.id < nodeS.domid) {
    239           // In this case, nodeS.domid must be wrong, because there is a path
    240           // from root to nodeS that does not go through nodeS.domid:
    241           //  * We can go from root to domS without going through nodeS.domid,
    242           //    because otherwise nodeS.domid would dominate domS, not
    243           //    domS.domS.
    244           //  * We can go from domS to nodeS without going through nodeS.domid
    245           //    because we know nodeS is reachable from domS on a path of nodes
    246           //    with increases ids, which cannot include nodeS.domid, which
    247           //    has a smaller id than domS.
    248           nodeS.domid = domS.domS.id;
    249         }
    250         domS.undom.add(nodeS);
    251         domS = domS.srcS;
    252       }
    253       nodeS.domS = domS;
    254       nodeS.domid = domS.id;
    255 
    256       for (NodeS xS : nodeS.undom) {
    257         if (domS.id < xS.domid) {
    258           // In this case, xS.domid must be wrong, because there is a path
    259           // from the root to xX that does not go through xS.domid:
    260           //  * We can go from root to nodeS without going through xS.domid,
    261           //    because otherwise xS.domid would dominate nodeS, not domS.
    262           //  * We can go from nodeS to xS without going through xS.domid
    263           //    because we know xS is reachable from nodeS on a path of nodes
    264           //    with increasing ids, which cannot include xS.domid, which has
    265           //    a smaller id than nodeS.
    266           boolean revisiting = xS.domid < xS.domS.id;
    267           xS.domid = domS.id;
    268           if (!revisiting) {
    269             revisit.add(xS);
    270           }
    271         }
    272       }
    273     }
    274 
    275     // 3. Update the dominators of the nodes.
    276     root.setDominatorsComputationState(null);
    277     for (NodeS nodeS : nodes) {
    278       nodeS.node.setDominator(nodeS.domS.node);
    279       nodeS.node.setDominatorsComputationState(null);
    280     }
    281   }
    282 }
    283