Dominators.java revision 2ad60cfc28e14ee8f0bb038720836a4696c478ad
1/*
2 * Copyright (C) 2007 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
17package com.android.dx.ssa;
18
19import java.util.ArrayList;
20import java.util.BitSet;
21import java.util.HashSet;
22
23/**
24 * This class computes dominator and post-dominator information using the
25 * Lengauer-Tarjan method.
26 *
27 * See A Fast Algorithm for Finding Dominators in a Flowgraph
28 * T. Lengauer & R. Tarjan, ACM TOPLAS July 1979, pgs 121-141.
29 *
30 * This implementation runs in time O(n log n).  The time bound
31 * could be changed to O(n * ack(n)) with a small change to the link and eval,
32 * and an addition of a child field to the DFS info. In reality, the constant
33 * overheads are high enough that the current method is faster in all but the
34 * strangest artificially constructed examples.
35 *
36 * The basic idea behind this algorithm is to perform a DFS walk, keeping track
37 * of various info about parents.  We then use this info to calculate the
38 * dominators, using union-find structures to link together the DFS info,
39 * then finally evaluate the union-find results to get the dominators.
40 * This implementation is m log n because it does not perform union by
41 * rank to keep the union-find tree balanced.
42 */
43public final class Dominators {
44    /* postdom is true if we want post dominators. */
45    private boolean postdom;
46    /* Method's basic blocks. */
47    private ArrayList<SsaBasicBlock> blocks;
48
49    private static final class DFSInfo {
50        int semidom;
51        SsaBasicBlock parent;
52        // rep(resentative) is known as "label" in the paper. It is the node
53        // that our block's DFS info has been unioned to.
54        SsaBasicBlock rep;
55        SsaBasicBlock ancestor;
56        ArrayList<SsaBasicBlock> bucket;
57
58        public DFSInfo() {
59            bucket = new ArrayList<SsaBasicBlock>();
60        }
61
62    }
63
64    /** Indexed by basic block index */
65    private DFSInfo[] info;
66    private ArrayList<SsaBasicBlock> vertex;
67
68    private DomFront.DomInfo domInfos[];
69
70    private BitSet getSuccs(SsaBasicBlock block) {
71        if (postdom) {
72            return block.getPredecessors();
73        } else {
74            return block.getSuccessors();
75        }
76    }
77
78    private BitSet getPreds(SsaBasicBlock block) {
79        if (postdom) {
80            return block.getSuccessors();
81        } else {
82            return block.getPredecessors();
83        }
84    }
85
86    /**
87     * Performs path compress on the DFS info.
88     * @param in Basic block whose DFS info we are path compressing.
89     */
90    private void compress(SsaBasicBlock in) {
91        DFSInfo bbInfo = info[in.getIndex()];
92        DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()];
93
94        if (ancestorbbInfo.ancestor != null) {
95            ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>();
96            HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>();
97            worklist.add(in);
98
99            while (!worklist.isEmpty()) {
100                int wsize = worklist.size();
101                SsaBasicBlock v = worklist.get(wsize - 1);
102                DFSInfo vbbInfo = info[v.getIndex()];
103                SsaBasicBlock vAncestor = vbbInfo.ancestor;
104                DFSInfo vabbInfo = info[vAncestor.getIndex()];
105
106                // Make sure we process our ancestor before ourselves.
107                if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
108                    worklist.add(vAncestor);
109                    continue;
110                }
111                worklist.remove(wsize - 1);
112
113                // Update based on ancestor info
114                if (vabbInfo.ancestor == null) {
115                    continue;
116                }
117                SsaBasicBlock vAncestorRep = vabbInfo.rep;
118                SsaBasicBlock vRep = vbbInfo.rep;
119                if (info[vAncestorRep.getIndex()].semidom
120                        < info[vRep.getIndex()].semidom) {
121                    vbbInfo.rep = vAncestorRep;
122                }
123                vbbInfo.ancestor = vabbInfo.ancestor;
124            }
125        }
126    }
127    private SsaBasicBlock eval(SsaBasicBlock v) {
128        DFSInfo bbInfo = info[v.getIndex()];
129        if (bbInfo.ancestor == null) {
130            return v;
131        }
132        compress(v);
133        return bbInfo.rep;
134    }
135
136    /**
137     * Callback for depth-first walk through control flow graph (either
138     * from the entry block or the exit block). Records the traversal order
139     * in the <code>info</code>list.
140     */
141    private class DfsWalker implements SsaBasicBlock.Visitor {
142        int dfsNum = 0;
143
144        public void visitBlock (SsaBasicBlock v, SsaBasicBlock parent) {
145            DFSInfo bbInfo = new DFSInfo();
146            bbInfo.semidom = ++dfsNum;
147            bbInfo.rep = v;
148            bbInfo.parent = parent;
149            vertex.add(v);
150            info[v.getIndex()] = bbInfo;
151        }
152    }
153
154    /**
155     * Performs dominator/post-dominator calculation for the control flow graph.
156     * @param meth Method to analyze
157     */
158    public void run(SsaMethod meth) {
159
160        this.blocks = meth.getBlocks();
161        this.info = new DFSInfo[blocks.size() + 2];
162        this.vertex = new ArrayList<SsaBasicBlock>();
163        SsaBasicBlock root = postdom
164                ? meth.getExitBlock() : meth.getEntryBlock();
165
166        if (root != null) {
167            vertex.add(root);
168            domInfos[root.getIndex()].idom = root.getIndex();
169        }
170
171        // First we perform a DFS numbering of the blocks, by numbering the dfs
172        // tree roots
173
174        DfsWalker walker = new DfsWalker();
175        meth.forEachBlockDepthFirst(postdom, walker);
176
177        // the largest semidom number assigned
178        int dfsMax = vertex.size() - 1;
179
180        // Now calculate semidominators.
181        for (int i = dfsMax; i >= 2; --i) {
182            SsaBasicBlock w = vertex.get(i);
183            DFSInfo wInfo = info[w.getIndex()];
184
185            BitSet preds = getPreds(w);
186            for (int j = preds.nextSetBit(0);
187                    j >= 0;
188                    j = preds.nextSetBit(j + 1)) {
189                SsaBasicBlock predBlock = blocks.get(j);
190                DFSInfo predInfo = info[predBlock.getIndex()];
191                // PredInfo may not exist in case the predecessor is not
192                // reachable
193                if (predInfo != null) {
194                    int predSemidom = info[eval(predBlock).getIndex()].semidom;
195                    if (predSemidom < wInfo.semidom) {
196                        wInfo.semidom = predSemidom;
197                    }
198                }
199            }
200            info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w);
201
202            // Normally we would call link here, but in our  m log n
203            // implementation this is equivalent to the following single line
204            wInfo.ancestor = wInfo.parent;
205
206            // Implicity define idom for each vertex
207            ArrayList<SsaBasicBlock> wParentBucket;
208            wParentBucket = info[wInfo.parent.getIndex()].bucket;
209
210            while (!wParentBucket.isEmpty()) {
211                int lastItem = wParentBucket.size() - 1;
212                SsaBasicBlock last = wParentBucket.remove(lastItem);
213                SsaBasicBlock U = eval(last);
214                if (info[U.getIndex()].semidom
215                        < info[last.getIndex()].semidom) {
216                    domInfos[last.getIndex()].idom = U.getIndex();
217                } else {
218                    domInfos[last.getIndex()].idom = wInfo.parent.getIndex();
219                }
220            }
221        }
222        // Now explicitly define the immediate dominator of each vertex
223        for (int i =  2; i <= dfsMax; ++i) {
224            SsaBasicBlock w = vertex.get(i);
225            if (domInfos[w.getIndex()].idom
226                    != vertex.get(info[w.getIndex()].semidom).getIndex()) {
227                domInfos[w.getIndex()].idom
228                        = domInfos[domInfos[w.getIndex()].idom].idom;
229            }
230        }
231    }
232
233    /**
234     * @param postdom true for postdom information, false for normal dom info
235     */
236    public Dominators(DomFront.DomInfo[] domInfos, boolean postdom) {
237        this.domInfos = domInfos;
238        this.postdom = postdom;
239    }
240}
241