summaryrefslogtreecommitdiffstats
path: root/dx/src/com/android/jack/dx/ssa/Dominators.java
blob: 3311efdbb4419b365a4546e92cc8543815d8d22e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.jack.dx.ssa;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashSet;

/**
 * This class computes dominator and post-dominator information using the
 * Lengauer-Tarjan method.
 *
 * See A Fast Algorithm for Finding Dominators in a Flowgraph
 * T. Lengauer & R. Tarjan, ACM TOPLAS July 1979, pgs 121-141.
 *
 * This implementation runs in time O(n log n).  The time bound
 * could be changed to O(n * ack(n)) with a small change to the link and eval,
 * and an addition of a child field to the DFS info. In reality, the constant
 * overheads are high enough that the current method is faster in all but the
 * strangest artificially constructed examples.
 *
 * The basic idea behind this algorithm is to perform a DFS walk, keeping track
 * of various info about parents.  We then use this info to calculate the
 * dominators, using union-find structures to link together the DFS info,
 * then finally evaluate the union-find results to get the dominators.
 * This implementation is m log n because it does not perform union by
 * rank to keep the union-find tree balanced.
 */
public final class Dominators {
  /* postdom is true if we want post dominators */
  private final boolean postdom;

  /* {@code non-null;} method being processed */
  private final SsaMethod meth;

  /* Method's basic blocks. */
  private final ArrayList<SsaBasicBlock> blocks;

  /** indexed by basic block index */
  private final DFSInfo[] info;

  private final ArrayList<SsaBasicBlock> vertex;

  /** {@code non-null;} the raw dominator info */
  private final DomFront.DomInfo domInfos[];

  /**
   * Constructs an instance.
   *
   * @param meth {@code non-null;} method to process
   * @param domInfos {@code non-null;} the raw dominator info
   * @param postdom true for postdom information, false for normal dom info
   */
  private Dominators(SsaMethod meth, DomFront.DomInfo[] domInfos, boolean postdom) {
    this.meth = meth;
    this.domInfos = domInfos;
    this.postdom = postdom;
    this.blocks = meth.getBlocks();
    this.info = new DFSInfo[blocks.size() + 2];
    this.vertex = new ArrayList<SsaBasicBlock>();
  }

  /**
   * Constructs a fully-initialized instance. (This method exists so as
   * to avoid calling a large amount of code in the constructor.)
   *
   * @param meth {@code non-null;} method to process
   * @param domInfos {@code non-null;} the raw dominator info
   * @param postdom true for postdom information, false for normal dom info
   */
  public static Dominators make(SsaMethod meth, DomFront.DomInfo[] domInfos, boolean postdom) {
    Dominators result = new Dominators(meth, domInfos, postdom);

    result.run();
    return result;
  }

  private BitSet getPreds(SsaBasicBlock block) {
    if (postdom) {
      return block.getSuccessors();
    } else {
      return block.getPredecessors();
    }
  }

  /**
   * Performs path compress on the DFS info.
   *
   * @param in Basic block whose DFS info we are path compressing.
   */
  private void compress(SsaBasicBlock in) {
    DFSInfo bbInfo = info[in.getIndex()];
    DFSInfo ancestorbbInfo = info[bbInfo.ancestor.getIndex()];

    if (ancestorbbInfo.ancestor != null) {
      ArrayList<SsaBasicBlock> worklist = new ArrayList<SsaBasicBlock>();
      HashSet<SsaBasicBlock> visited = new HashSet<SsaBasicBlock>();
      worklist.add(in);

      while (!worklist.isEmpty()) {
        int wsize = worklist.size();
        SsaBasicBlock v = worklist.get(wsize - 1);
        DFSInfo vbbInfo = info[v.getIndex()];
        SsaBasicBlock vAncestor = vbbInfo.ancestor;
        DFSInfo vabbInfo = info[vAncestor.getIndex()];

        // Make sure we process our ancestor before ourselves.
        if (visited.add(vAncestor) && vabbInfo.ancestor != null) {
          worklist.add(vAncestor);
          continue;
        }
        worklist.remove(wsize - 1);

        // Update based on ancestor info.
        if (vabbInfo.ancestor == null) {
          continue;
        }
        SsaBasicBlock vAncestorRep = vabbInfo.rep;
        SsaBasicBlock vRep = vbbInfo.rep;
        if (info[vAncestorRep.getIndex()].semidom < info[vRep.getIndex()].semidom) {
          vbbInfo.rep = vAncestorRep;
        }
        vbbInfo.ancestor = vabbInfo.ancestor;
      }
    }
  }

  private SsaBasicBlock eval(SsaBasicBlock v) {
    DFSInfo bbInfo = info[v.getIndex()];

    if (bbInfo.ancestor == null) {
      return v;
    }

    compress(v);
    return bbInfo.rep;
  }

  /**
   * Performs dominator/post-dominator calculation for the control
   * flow graph.
   *
   * @param meth {@code non-null;} method to analyze
   */
  private void run() {
    SsaBasicBlock root = postdom ? meth.getExitBlock() : meth.getEntryBlock();

    if (root != null) {
      vertex.add(root);
      domInfos[root.getIndex()].idom = root.getIndex();
    }

    /*
     * First we perform a DFS numbering of the blocks, by
     * numbering the dfs tree roots.
     */

DfsWalker walker = new DfsWalker();
    meth.forEachBlockDepthFirst(postdom, walker);

    // the largest semidom number assigned
    int dfsMax = vertex.size() - 1;

    // Now calculate semidominators.
    for (int i = dfsMax; i >= 2; --i) {
      SsaBasicBlock w = vertex.get(i);
      DFSInfo wInfo = info[w.getIndex()];

      BitSet preds = getPreds(w);
      for (int j = preds.nextSetBit(0); j >= 0; j = preds.nextSetBit(j + 1)) {
        SsaBasicBlock predBlock = blocks.get(j);
        DFSInfo predInfo = info[predBlock.getIndex()];

        /*
         * PredInfo may not exist in case the predecessor is
         * not reachable.
         */
        if (predInfo != null) {
          int predSemidom = info[eval(predBlock).getIndex()].semidom;
          if (predSemidom < wInfo.semidom) {
            wInfo.semidom = predSemidom;
          }
        }
      }
      info[vertex.get(wInfo.semidom).getIndex()].bucket.add(w);

      /*
       * Normally we would call link here, but in our O(m log n)
       * implementation this is equivalent to the following
       * single line.
       */
      wInfo.ancestor = wInfo.parent;

      // Implicity define idom for each vertex.
      ArrayList<SsaBasicBlock> wParentBucket;
      wParentBucket = info[wInfo.parent.getIndex()].bucket;

      while (!wParentBucket.isEmpty()) {
        int lastItem = wParentBucket.size() - 1;
        SsaBasicBlock last = wParentBucket.remove(lastItem);
        SsaBasicBlock u = eval(last);
        if (info[u.getIndex()].semidom < info[last.getIndex()].semidom) {
          domInfos[last.getIndex()].idom = u.getIndex();
        } else {
          domInfos[last.getIndex()].idom = wInfo.parent.getIndex();
        }
      }
    }

    // Now explicitly define the immediate dominator of each vertex
    for (int i = 2; i <= dfsMax; ++i) {
      SsaBasicBlock w = vertex.get(i);
      if (domInfos[w.getIndex()].idom != vertex.get(info[w.getIndex()].semidom).getIndex()) {
        domInfos[w.getIndex()].idom = domInfos[domInfos[w.getIndex()].idom].idom;
      }
    }
  }

  /**
   * Callback for depth-first walk through control flow graph (either
   * from the entry block or the exit block). Records the traversal order
   * in the {@code info}list.
   */
  private class DfsWalker implements SsaBasicBlock.Visitor {
    private int dfsNum = 0;

    @Override
    public void visitBlock(SsaBasicBlock v, SsaBasicBlock parent) {
      DFSInfo bbInfo = new DFSInfo();
      bbInfo.semidom = ++dfsNum;
      bbInfo.rep = v;
      bbInfo.parent = parent;
      vertex.add(v);
      info[v.getIndex()] = bbInfo;
    }
  }

  private static final class DFSInfo {
    public int semidom;
    public SsaBasicBlock parent;

    /**
     * rep(resentative) is known as "label" in the paper. It is the node
     * that our block's DFS info has been unioned to.
     */
    public SsaBasicBlock rep;

    public SsaBasicBlock ancestor;
    public ArrayList<SsaBasicBlock> bucket;

    public DFSInfo() {
      bucket = new ArrayList<SsaBasicBlock>();
    }
  }
}