blob: dc0c539547211ac12ad955e81ab364c406b7f8ef [file] [log] [blame]
// Copyright (c) 2022, 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.analysis.framework.intraprocedural.cf;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.StringUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
/** A basic block for {@link com.android.tools.r8.graph.CfCode}. */
public class CfBlock {
// The CfCode instruction index of the block's first instruction.
int firstInstructionIndex = -1;
// The CfCode instruction index of the block's first throwing instruction.
int firstThrowingInstructionIndex = -1;
// The CfCode instruction index of the block's last instruction.
int lastInstructionIndex = -1;
// The predecessors of the block. These are stored explicitly (unlike the successors) since they
// cannot efficiently be computed from the block.
final Set<CfBlock> predecessors = new LinkedHashSet<>();
// The exceptional predecessors of the block.
final List<CfBlock> exceptionalPredecessors = new ArrayList<>();
// The exceptional successors of the block (i.e., the catch handlers of the block).
final LinkedHashMap<DexType, CfBlock> exceptionalSuccessors = new LinkedHashMap<>();
public CfInstruction getFallthroughInstruction(CfCode code) {
int fallthroughInstructionIndex = getLastInstructionIndex() + 1;
return fallthroughInstructionIndex < code.getInstructions().size()
? code.getInstructions().get(fallthroughInstructionIndex)
: null;
}
public int getFirstInstructionIndex() {
return firstInstructionIndex;
}
public boolean hasThrowingInstruction() {
return firstThrowingInstructionIndex >= 0;
}
public int getFirstThrowingInstructionIndex() {
return firstThrowingInstructionIndex;
}
public CfInstruction getLastInstruction(CfCode code) {
return code.getInstructions().get(lastInstructionIndex);
}
public int getLastInstructionIndex() {
return lastInstructionIndex;
}
public Collection<CfBlock> getPredecessors() {
return predecessors;
}
public List<CfBlock> getExceptionalPredecessors() {
return exceptionalPredecessors;
}
public LinkedHashMap<DexType, CfBlock> getExceptionalSuccessors() {
return exceptionalSuccessors;
}
@Override
public String toString() {
List<String> predecessorStrings = new ArrayList<>();
predecessors.forEach(p -> predecessorStrings.add(p.getRangeString()));
exceptionalPredecessors.forEach(p -> predecessorStrings.add("*" + p.getRangeString()));
return "CfBlock(range="
+ getRangeString()
+ ", predecessors="
+ StringUtils.join(", ", predecessorStrings)
+ ")";
}
private String getRangeString() {
return firstInstructionIndex + "->" + lastInstructionIndex;
}
// A mutable interface for block construction.
static class MutableCfBlock extends CfBlock {
void addPredecessor(CfBlock block) {
predecessors.add(block);
}
void addExceptionalPredecessor(CfBlock block) {
exceptionalPredecessors.add(block);
}
void addExceptionalSuccessor(CfBlock block, DexType guard) {
assert !exceptionalSuccessors.containsKey(guard);
exceptionalSuccessors.put(guard, block);
}
void setFirstInstructionIndex(int firstInstructionIndex) {
this.firstInstructionIndex = firstInstructionIndex;
}
void setFirstThrowingInstructionIndex(int firstThrowingInstructionIndex) {
this.firstThrowingInstructionIndex = firstThrowingInstructionIndex;
}
void setLastInstructionIndex(int lastInstructionIndex) {
this.lastInstructionIndex = lastInstructionIndex;
}
boolean validate(CfControlFlowGraph cfg, InternalOptions options) {
assert 0 <= firstInstructionIndex;
assert firstInstructionIndex <= lastInstructionIndex;
assert firstThrowingInstructionIndex < 0
|| firstInstructionIndex <= firstThrowingInstructionIndex;
assert firstThrowingInstructionIndex < 0
|| firstThrowingInstructionIndex <= lastInstructionIndex;
assert SetUtils.newIdentityHashSet(predecessors).size() == predecessors.size();
assert this == cfg.getEntryBlock()
|| !predecessors.isEmpty()
|| !exceptionalPredecessors.isEmpty()
|| options.getCfCodeAnalysisOptions().isUnreachableCfBlocksAllowed();
return true;
}
}
}