org.snapscript.dx.ssa.Dominators Maven / Gradle / Ivy
/*
* 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 org.snapscript.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 blocks;
/** indexed by basic block index */
private final DFSInfo[] info;
private final ArrayList 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();
}
/**
* 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 getSuccs(SsaBasicBlock block) {
if (postdom) {
return block.getPredecessors();
} else {
return block.getSuccessors();
}
}
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 worklist = new ArrayList();
HashSet visited = new HashSet();
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 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;
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 bucket;
public DFSInfo() {
bucket = new ArrayList();
}
}
}