blob: 20eadc9d53ccbe37b81084f0dd813363309b9983 [file] [log] [blame]
// Copyright (c) 2016, the R8 project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.code;
import static com.android.tools.r8.ir.code.DominatorTree.Assumption.MAY_HAVE_UNREACHABLE_BLOCKS;
import com.android.tools.r8.ir.code.BasicBlock.BasicBlockChangeListener;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class DominatorTree implements BasicBlockChangeListener {
public enum Assumption {
NO_UNREACHABLE_BLOCKS,
MAY_HAVE_UNREACHABLE_BLOCKS
}
public enum Inclusive {
YES,
NO
}
private final BasicBlock[] sorted;
private BasicBlock[] doms;
private final BasicBlock normalExitBlock = new BasicBlock();
private final int unreachableStartIndex;
private boolean obsolete = false;
public DominatorTree(IRCode code) {
this(code, Assumption.NO_UNREACHABLE_BLOCKS);
}
public DominatorTree(IRCode code, Assumption assumption) {
assert assumption != null;
assert assumption == MAY_HAVE_UNREACHABLE_BLOCKS || code.getUnreachableBlocks().isEmpty();
ImmutableList<BasicBlock> blocks = code.topologicallySortedBlocks();
// Add the internal exit block to the block list.
for (BasicBlock block : blocks) {
if (block.exit().isReturn()) {
normalExitBlock.getMutablePredecessors().add(block);
}
}
int numberOfBlocks = code.blocks.size();
if (assumption == MAY_HAVE_UNREACHABLE_BLOCKS) {
// Unreachable blocks have been removed implicitly by the topological sort.
sorted = new BasicBlock[numberOfBlocks + 1];
// Move topologically sorted blocks into `sorted`.
int color = code.reserveMarkingColor();
int i = 0;
for (BasicBlock block : blocks) {
sorted[i] = block;
block.mark(color);
i++;
}
sorted[i] = normalExitBlock;
i++;
// Move unreachable blocks into the end of `sorted`.
unreachableStartIndex = i;
for (BasicBlock block : code.blocks) {
if (!block.isMarked(color)) {
sorted[i] = block;
i++;
}
}
code.returnMarkingColor(color);
} else {
sorted = blocks.toArray(new BasicBlock[numberOfBlocks + 1]);
sorted[numberOfBlocks] = normalExitBlock;
unreachableStartIndex = numberOfBlocks + 1;
}
numberBlocks();
build();
// This is intentionally implemented via an `assert` so that we do not attach listeners to all
// basic blocks when running without assertions.
assert recordChangesToControlFlowEdges(code.blocks);
}
/**
* Get the immediate dominator block for a block.
*/
public BasicBlock immediateDominator(BasicBlock block) {
assert !obsolete;
return doms[block.getNumber()];
}
/**
* Check if one basic block is dominated by another basic block.
*
* @param subject subject to check for domination by {@param dominator}
* @param dominator dominator to check against
* @return whether {@param subject} is dominated by {@param dominator}
*/
public boolean dominatedBy(BasicBlock subject, BasicBlock dominator) {
assert !obsolete;
if (subject == dominator) {
return true;
}
return strictlyDominatedBy(subject, dominator);
}
/**
* Checks if one basic block dominates a collection of other basic blocks.
*
* @param dominator dominator to check against
* @param subjects subjects to check for domination by {@param dominator}
* @return whether {@param subjects} are all dominated by {@param dominator}
*/
public boolean dominatesAllOf(BasicBlock dominator, Iterable<BasicBlock> subjects) {
for (BasicBlock subject : subjects) {
if (!dominatedBy(subject, dominator)) {
return false;
}
}
return true;
}
/**
* Check if one basic block is strictly dominated by another basic block.
*
* @param subject subject to check for domination by {@param dominator}
* @param dominator dominator to check against
* @return whether {@param subject} is strictly dominated by {@param dominator}
*/
public boolean strictlyDominatedBy(BasicBlock subject, BasicBlock dominator) {
assert !obsolete;
if (subject.getNumber() == 0 || subject == normalExitBlock) {
return false;
}
while (true) {
BasicBlock idom = immediateDominator(subject);
if (idom.getNumber() < dominator.getNumber()) {
return false;
}
if (idom == dominator) {
return true;
}
subject = idom;
}
}
/**
* Use the dominator tree to find the dominating block that is closest to a set of blocks.
*
* @param blocks the block for which to find a dominator
* @return the closest dominator for the collection of blocks
*/
public BasicBlock closestDominator(Collection<BasicBlock> blocks) {
assert !obsolete;
if (blocks.size() == 0) {
return null;
}
Iterator<BasicBlock> it = blocks.iterator();
BasicBlock dominator = it.next();
while (it.hasNext()) {
dominator = intersect(dominator, it.next());
}
return dominator;
}
/** Returns the blocks dominated by dominator, including dominator itself. */
public List<BasicBlock> dominatedBlocks(BasicBlock dominator) {
return dominatedBlocks(dominator, new ArrayList<>());
}
public <T extends Collection<BasicBlock>> T dominatedBlocks(
BasicBlock dominator, T dominatedBlocks) {
assert !obsolete;
for (int i = dominator.getNumber(); i < unreachableStartIndex; ++i) {
BasicBlock block = sorted[i];
if (dominatedBy(block, dominator)) {
dominatedBlocks.add(block);
}
}
return dominatedBlocks;
}
/**
* Returns an iterator over all dominator blocks of <code>dominated</code>.
*
* Iteration order is always the immediate dominator of the previously returned block. The
* iteration starts by returning <code>dominated</code>.
*/
public Iterable<BasicBlock> dominatorBlocks(BasicBlock dominated, Inclusive inclusive) {
assert !obsolete;
return () -> {
Iterator<BasicBlock> iterator =
new Iterator<BasicBlock>() {
private BasicBlock current = dominated;
@Override
public boolean hasNext() {
return current != null;
}
@Override
public BasicBlock next() {
if (!hasNext()) {
return null;
} else {
BasicBlock result = current;
if (current.getNumber() == 0) {
current = null;
} else {
current = immediateDominator(current);
assert current != result;
}
return result;
}
}
};
if (inclusive == Inclusive.NO) {
BasicBlock block = iterator.next();
assert block == dominated;
}
return iterator;
};
}
public Iterable<BasicBlock> normalExitDominatorBlocks() {
assert !obsolete;
// Do not include the `normalExitBlock`, since this is a synthetic block created here in the
// DominatorTree.
return dominatorBlocks(normalExitBlock, Inclusive.NO);
}
public BasicBlock[] getSortedBlocks() {
return sorted;
}
private void numberBlocks() {
for (int i = 0; i < sorted.length; i++) {
sorted[i].setNumber(i);
}
}
private boolean postorderCompareLess(BasicBlock b1, BasicBlock b2) {
// The topological sort is reverse postorder.
return b1.getNumber() > b2.getNumber();
}
// Build dominator tree based on the algorithm described in this paper:
//
// A Simple, Fast Dominance Algorithm
// Cooper, Keith D.; Harvey, Timothy J.; and Kennedy, Ken (2001).
// http://www.cs.rice.edu/~keith/EMBED/dom.pdf
private void build() {
doms = new BasicBlock[sorted.length];
doms[0] = sorted[0];
boolean changed = true;
while (changed) {
changed = false;
// Run through all nodes in reverse postorder (except start node).
for (int i = 1; i < sorted.length; i++) {
BasicBlock b = sorted[i];
// Pick one processed predecessor.
BasicBlock newIDom = null;
int picked = -1;
for (int j = 0; newIDom == null && j < b.getPredecessors().size(); j++) {
BasicBlock p = b.getPredecessors().get(j);
if (doms[p.getNumber()] != null) {
picked = j;
newIDom = p;
}
}
// Run through all other predecessors.
for (int j = 0; j < b.getPredecessors().size(); j++) {
BasicBlock p = b.getPredecessors().get(j);
if (j == picked) {
continue;
}
if (doms[p.getNumber()] != null) {
newIDom = intersect(p, newIDom);
}
}
if (doms[b.getNumber()] != newIDom) {
doms[b.getNumber()] = newIDom;
changed = true;
}
}
}
}
private BasicBlock intersect(BasicBlock b1, BasicBlock b2) {
BasicBlock finger1 = b1;
BasicBlock finger2 = b2;
while (finger1 != finger2) {
while (postorderCompareLess(finger1, finger2)) {
finger1 = doms[finger1.getNumber()];
}
while (postorderCompareLess(finger2, finger1)) {
finger2 = doms[finger2.getNumber()];
}
}
return finger1;
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("Dominators\n");
for (BasicBlock block : sorted) {
builder.append(block.getNumber());
builder.append(": ");
builder.append(doms[block.getNumber()].getNumber());
builder.append("\n");
}
return builder.toString();
}
private boolean recordChangesToControlFlowEdges(List<BasicBlock> blocks) {
for (BasicBlock block : blocks) {
block.addControlFlowEdgesMayChangeListener(this);
}
return true;
}
@Override
public void onSuccessorsMayChange(BasicBlock block) {
obsolete = true;
}
@Override
public void onPredecessorsMayChange(BasicBlock block) {
obsolete = true;
}
}