blob: e08aeae69af66f6ce8ff526b68db656158f6f555 [file] [log] [blame]
// Copyright (c) 2018, 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.optimize;
import static com.google.common.base.Predicates.alwaysTrue;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexField;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.Assume;
import com.android.tools.r8.ir.code.Assume.NonNullAssumption;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.DominatorTree;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.If;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionIterator;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.LazyDominatorTree;
import com.android.tools.r8.ir.code.Phi;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.TriConsumer;
import com.google.common.collect.Sets;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntListIterator;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Predicate;
public class NonNullTracker implements Assumer {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
public NonNullTracker(AppView<? extends AppInfoWithClassHierarchy> appView) {
this.appView = appView;
}
@Override
public void insertAssumeInstructions(IRCode code, Timing timing) {
insertAssumeInstructionsInBlocks(code, code.listIterator(), alwaysTrue(), timing);
}
@Override
public void insertAssumeInstructionsInBlocks(
IRCode code,
BasicBlockIterator blockIterator,
Predicate<BasicBlock> blockTester,
Timing timing) {
timing.begin("Insert assume not null instructions");
internalInsertAssumeInstructionsInBlocks(code, blockIterator, blockTester, timing);
timing.end();
}
private void internalInsertAssumeInstructionsInBlocks(
IRCode code,
BasicBlockIterator blockIterator,
Predicate<BasicBlock> blockTester,
Timing timing) {
timing.begin("Part 1: Compute non null values");
NonNullValues nonNullValues = computeNonNullValues(code, blockIterator, blockTester);
timing.end();
if (nonNullValues.isEmpty()) {
return;
}
timing.begin("Part 2: Remove redundant assume instructions");
removeRedundantAssumeInstructions(nonNullValues);
timing.end();
timing.begin("Part 3: Compute dominated users");
Map<Instruction, Set<Value>> redundantKeys =
computeDominanceForNonNullValues(code, nonNullValues);
timing.end();
if (nonNullValues.isEmpty()) {
return;
}
timing.begin("Part 4: Remove redundant dominated assume instructions");
removeRedundantDominatedAssumeInstructions(nonNullValues, redundantKeys);
timing.end();
if (nonNullValues.isEmpty()) {
return;
}
timing.begin("Part 5: Materialize assume instructions");
materializeAssumeInstructions(code, nonNullValues);
timing.end();
}
private NonNullValues computeNonNullValues(
IRCode code, BasicBlockIterator blockIterator, Predicate<BasicBlock> blockTester) {
NonNullValues.Builder nonNullValuesBuilder = new NonNullValues.Builder();
while (blockIterator.hasNext()) {
BasicBlock block = blockIterator.next();
if (blockTester.test(block)) {
computeNonNullValuesInBlock(code, blockIterator, block, nonNullValuesBuilder);
}
}
return nonNullValuesBuilder.build();
}
private void computeNonNullValuesInBlock(
IRCode code,
BasicBlockIterator blockIterator,
BasicBlock block,
NonNullValues.Builder nonNullValuesBuilder) {
// Add non-null after
// 1) instructions that implicitly indicate receiver/array is not null.
// 2) invocations that are guaranteed to return a non-null value.
// 3) parameters that are not null after the invocation.
// 4) field-get instructions that are guaranteed to read a non-null value.
InstructionListIterator instructionIterator = block.listIterator(code);
while (instructionIterator.hasNext()) {
Instruction current = instructionIterator.next();
boolean needsAssumeInstruction = false;
// Case (1), instructions that implicitly indicate receiver/array is not null.
if (current.throwsOnNullInput()) {
Value inValue = current.getNonNullInput();
if (nonNullValuesBuilder.isMaybeNull(inValue)
&& isNullableReferenceTypeWithOtherNonDebugUsers(inValue, current)) {
nonNullValuesBuilder.addNonNullValueWithUnknownDominance(current, inValue);
needsAssumeInstruction = true;
}
}
Value outValue = current.outValue();
if (current.isInvokeMethod()) {
InvokeMethod invoke = current.asInvokeMethod();
if (invoke.hasOutValue() || !invoke.getInvokedMethod().proto.parameters.isEmpty()) {
// Case (2) and (3).
needsAssumeInstruction |=
computeNonNullValuesFromSingleTarget(code, invoke, nonNullValuesBuilder);
}
} else if (current.isFieldGet()) {
// Case (4), field-get instructions that are guaranteed to read a non-null value.
FieldInstruction fieldInstruction = current.asFieldInstruction();
DexField field = fieldInstruction.getField();
if (isNullableReferenceTypeWithNonDebugUsers(outValue)) {
DexEncodedField encodedField = appView.appInfo().resolveField(field).getResolvedField();
if (encodedField != null) {
FieldOptimizationInfo optimizationInfo = encodedField.getOptimizationInfo();
if (optimizationInfo.getDynamicUpperBoundType() != null
&& optimizationInfo.getDynamicUpperBoundType().isDefinitelyNotNull()) {
nonNullValuesBuilder.addNonNullValueKnownToDominateAllUsers(current, outValue);
needsAssumeInstruction = true;
}
}
}
}
// If we need to insert an assume instruction into a block with catch handlers, we split the
// block such that the IR is ready for the insertion of the assume instruction.
//
// This splitting could in principle be deferred until we materialize the assume instructions,
// but then we would need to rewind the basic block iterator to the beginning, and scan over
// the instructions another time, splitting the blocks as needed.
if (block.hasCatchHandlers()) {
if (needsAssumeInstruction) {
BasicBlock insertionBlock = instructionIterator.split(code, blockIterator);
assert !instructionIterator.hasNext();
assert instructionIterator.peekPrevious().isGoto();
assert blockIterator.peekPrevious() == insertionBlock;
computeNonNullValuesInBlock(code, blockIterator, insertionBlock, nonNullValuesBuilder);
return;
}
if (current.instructionTypeCanThrow()) {
break;
}
}
}
If ifInstruction = block.exit().asIf();
if (ifInstruction != null && ifInstruction.isNonTrivialNullTest()) {
Value lhs = ifInstruction.lhs();
if (nonNullValuesBuilder.isMaybeNull(lhs)
&& isNullableReferenceTypeWithOtherNonDebugUsers(lhs, ifInstruction)
&& ifInstruction.targetFromNonNullObject().getPredecessors().size() == 1) {
nonNullValuesBuilder.addNonNullValueWithUnknownDominance(ifInstruction, lhs);
}
}
}
private boolean computeNonNullValuesFromSingleTarget(
IRCode code, InvokeMethod invoke, NonNullValues.Builder nonNullValuesBuilder) {
DexEncodedMethod singleTarget = invoke.lookupSingleTarget(appView, code.context());
if (singleTarget == null) {
return false;
}
boolean needsAssumeInstruction = false;
MethodOptimizationInfo optimizationInfo = singleTarget.getOptimizationInfo();
// Case (2), invocations that are guaranteed to return a non-null value.
Value outValue = invoke.outValue();
if (outValue != null
&& optimizationInfo.neverReturnsNull()
&& isNullableReferenceTypeWithNonDebugUsers(outValue)) {
nonNullValuesBuilder.addNonNullValueKnownToDominateAllUsers(invoke, outValue);
needsAssumeInstruction = true;
}
// Case (3), parameters that are not null after the invocation.
BitSet nonNullParamOnNormalExits = optimizationInfo.getNonNullParamOnNormalExits();
if (nonNullParamOnNormalExits != null) {
int start = invoke.isInvokeMethodWithReceiver() ? 1 : 0;
for (int i = start; i < invoke.arguments().size(); i++) {
if (nonNullParamOnNormalExits.get(i)) {
Value argument = invoke.getArgument(i);
if (nonNullValuesBuilder.isMaybeNull(argument)
&& isNullableReferenceTypeWithOtherNonDebugUsers(argument, invoke)) {
nonNullValuesBuilder.addNonNullValueWithUnknownDominance(invoke, argument);
needsAssumeInstruction = true;
}
}
}
}
return needsAssumeInstruction;
}
private void removeRedundantAssumeInstructions(NonNullValues nonNullValues) {
nonNullValues.removeIf(
(instruction, nonNullValue) -> {
if (nonNullValue.isPhi()) {
return false;
}
Instruction definition = nonNullValue.definition;
return definition != instruction && nonNullValues.contains(definition, nonNullValue);
});
}
private Map<Instruction, Set<Value>> computeDominanceForNonNullValues(
IRCode code, NonNullValues nonNullValues) {
Map<Instruction, Set<Value>> redundantKeys = new IdentityHashMap<>();
LazyDominatorTree lazyDominatorTree = new LazyDominatorTree(code);
Map<BasicBlock, Set<BasicBlock>> dominatedBlocksCache = new IdentityHashMap<>();
nonNullValues.computeDominance(
(instruction, nonNullValue) -> {
Set<Value> alreadyNonNullValues = redundantKeys.get(instruction);
if (alreadyNonNullValues != null && alreadyNonNullValues.contains(nonNullValue)) {
// Returning redundant() will cause the entry (instruction, nonNullValue) to be removed.
return NonNullDominance.redundant();
}
// If this value is non-null since its definition, then it is known to dominate all users.
if (nonNullValue == instruction.outValue()) {
return NonNullDominance.everything();
}
// If we learn that this value is known to be non-null in the same block as it is defined,
// and it is not used between its definition and the instruction that performs the null
// check, then the non-null-value is known to dominate all other users than the null check
// itself.
BasicBlock block = instruction.getBlock();
if (nonNullValue.getBlock() == block
&& block.exit().isGoto()
&& !instruction.getBlock().hasCatchHandlers()) {
InstructionIterator iterator = instruction.getBlock().iterator();
if (!nonNullValue.isPhi()) {
iterator.nextUntil(x -> x != nonNullValue.definition);
iterator.previous();
}
boolean isUsedBeforeInstruction = false;
while (iterator.hasNext()) {
Instruction current = iterator.next();
if (current == instruction) {
break;
}
if (current.inValues().contains(nonNullValue)
|| current.getDebugValues().contains(nonNullValue)) {
isUsedBeforeInstruction = true;
break;
}
}
if (!isUsedBeforeInstruction) {
return NonNullDominance.everythingElse();
}
}
// Otherwise, we need a dominator tree to determine which users are dominated.
BasicBlock insertionBlock = getInsertionBlock(instruction);
assert nonNullValue.hasPhiUsers()
|| nonNullValue.uniqueUsers().stream().anyMatch(user -> user != instruction)
|| nonNullValue.isArgument();
// Find all users of the original value that are dominated by either the current block
// or the new split-off block. Since NPE can be explicitly caught, nullness should be
// propagated through dominance.
DominatorTree dominatorTree = lazyDominatorTree.get();
Set<BasicBlock> dominatedBlocks =
dominatedBlocksCache.computeIfAbsent(
insertionBlock, x -> dominatorTree.dominatedBlocks(x, Sets.newIdentityHashSet()));
NonNullDominance.Builder dominance = NonNullDominance.builder(nonNullValue);
for (Instruction user : nonNullValue.uniqueUsers()) {
if (user != instruction && dominatedBlocks.contains(user.getBlock())) {
if (user.getBlock() == insertionBlock && insertionBlock == block) {
Instruction first = block.iterator().nextUntil(x -> x == instruction || x == user);
assert first != null;
if (first == user) {
continue;
}
}
dominance.addDominatedUser(user);
// Record that there is no need to insert an assume instruction for the non-null-value
// after the given user in case the user is also a null check for the non-null-value.
redundantKeys
.computeIfAbsent(user, ignore -> Sets.newIdentityHashSet())
.add(nonNullValue);
}
}
for (Phi user : nonNullValue.uniquePhiUsers()) {
IntList dominatedPredecessorIndices =
findDominatedPredecessorIndexesInPhi(user, nonNullValue, dominatedBlocks);
if (!dominatedPredecessorIndices.isEmpty()) {
dominance.addDominatedPhiUser(user, dominatedPredecessorIndices);
}
}
return dominance.build();
});
return redundantKeys;
}
private void removeRedundantDominatedAssumeInstructions(
NonNullValues nonNullValues, Map<Instruction, Set<Value>> redundantKeys) {
nonNullValues.removeAll(redundantKeys);
}
private void materializeAssumeInstructions(IRCode code, NonNullValues nonNullValues) {
Set<Value> affectedValues = Sets.newIdentityHashSet();
Map<BasicBlock, Map<Instruction, List<Instruction>>> pendingInsertions =
new IdentityHashMap<>();
nonNullValues.forEach(
(instruction, nonNullValue, dominance) -> {
BasicBlock block = instruction.getBlock();
BasicBlock insertionBlock = getInsertionBlock(instruction);
Value newValue =
code.createValue(
nonNullValue.getType().asReferenceType().asMeetWithNotNull(),
nonNullValue.getLocalInfo());
if (dominance.isEverything()) {
nonNullValue.replaceUsers(newValue);
} else if (dominance.isEverythingElse()) {
nonNullValue.replaceSelectiveInstructionUsers(newValue, user -> user != instruction);
nonNullValue.replacePhiUsers(newValue);
} else if (dominance.isSomething()) {
SomethingNonNullDominance somethingDominance = dominance.asSomething();
somethingDominance
.getDominatedPhiUsers()
.forEach(
(user, indices) -> {
IntListIterator iterator = indices.iterator();
while (iterator.hasNext()) {
Value operand = user.getOperand(iterator.nextInt());
if (operand != nonNullValue) {
assert operand.isDefinedByInstructionSatisfying(
Instruction::isAssumeNonNull);
iterator.remove();
}
}
});
nonNullValue.replaceSelectiveUsers(
newValue,
somethingDominance.getDominatedUsers(),
somethingDominance.getDominatedPhiUsers());
}
affectedValues.addAll(newValue.affectedValues());
Assume<NonNullAssumption> assumeInstruction =
Assume.createAssumeNonNullInstruction(newValue, nonNullValue, instruction, appView);
assumeInstruction.setPosition(instruction.getPosition());
if (insertionBlock != block) {
insertionBlock.listIterator(code).add(assumeInstruction);
} else {
pendingInsertions
.computeIfAbsent(block, ignore -> new IdentityHashMap<>())
.computeIfAbsent(instruction, ignore -> new ArrayList<>())
.add(assumeInstruction);
}
});
pendingInsertions.forEach(
(block, pendingInsertionsPerInstruction) -> {
InstructionListIterator instructionIterator = block.listIterator(code);
while (instructionIterator.hasNext() && !pendingInsertionsPerInstruction.isEmpty()) {
Instruction instruction = instructionIterator.next();
List<Instruction> pendingAssumeInstructions =
pendingInsertionsPerInstruction.remove(instruction);
if (pendingAssumeInstructions != null) {
pendingAssumeInstructions.forEach(instructionIterator::add);
}
}
});
if (!affectedValues.isEmpty()) {
new TypeAnalysis(appView).narrowing(affectedValues);
}
}
private BasicBlock getInsertionBlock(Instruction instruction) {
if (instruction.isIf()) {
return instruction.asIf().targetFromNonNullObject();
}
BasicBlock block = instruction.getBlock();
if (block.hasCatchHandlers()) {
return block.exit().asGoto().getTarget();
}
return block;
}
private IntList findDominatedPredecessorIndexesInPhi(
Phi user, Value knownToBeNonNullValue, Set<BasicBlock> dominatedBlocks) {
assert user.getOperands().contains(knownToBeNonNullValue);
List<Value> operands = user.getOperands();
List<BasicBlock> predecessors = user.getBlock().getPredecessors();
assert operands.size() == predecessors.size();
IntList predecessorIndexes = new IntArrayList();
int index = 0;
Iterator<Value> operandIterator = operands.iterator();
Iterator<BasicBlock> predecessorIterator = predecessors.iterator();
while (operandIterator.hasNext() && predecessorIterator.hasNext()) {
Value operand = operandIterator.next();
BasicBlock predecessor = predecessorIterator.next();
// When this phi is chosen to be known-to-be-non-null value,
// check if the corresponding predecessor is dominated by the block where non-null is added.
if (operand == knownToBeNonNullValue && dominatedBlocks.contains(predecessor)) {
predecessorIndexes.add(index);
}
index++;
}
return predecessorIndexes;
}
private static boolean isNullableReferenceType(Value value) {
TypeElement type = value.getType();
return type.isReferenceType() && type.asReferenceType().isNullable();
}
private static boolean isNullableReferenceTypeWithNonDebugUsers(Value value) {
return isNullableReferenceType(value) && value.numberOfAllNonDebugUsers() > 0;
}
private static boolean isNullableReferenceTypeWithOtherNonDebugUsers(
Value value, Instruction ignore) {
if (isNullableReferenceType(value)) {
if (value.hasPhiUsers()) {
return true;
}
for (Instruction user : value.uniqueUsers()) {
if (user != ignore) {
return true;
}
}
}
return false;
}
static class NonNullValues {
/**
* A mapping from each instruction to the (in and out) values that are guaranteed to be non-null
* by the instruction. Each non-null value is subsequently mapped to the set of users that it
* dominates.
*/
Map<Instruction, Map<Value, NonNullDominance>> nonNullValues;
public NonNullValues(Map<Instruction, Map<Value, NonNullDominance>> nonNullValues) {
this.nonNullValues = nonNullValues;
}
public static Builder builder() {
return new Builder();
}
void computeDominance(BiFunction<Instruction, Value, NonNullDominance> function) {
Iterator<Entry<Instruction, Map<Value, NonNullDominance>>> outerIterator =
nonNullValues.entrySet().iterator();
while (outerIterator.hasNext()) {
Entry<Instruction, Map<Value, NonNullDominance>> outerEntry = outerIterator.next();
Instruction instruction = outerEntry.getKey();
Map<Value, NonNullDominance> dominancePerValue = outerEntry.getValue();
Iterator<Entry<Value, NonNullDominance>> innerIterator =
dominancePerValue.entrySet().iterator();
while (innerIterator.hasNext()) {
Entry<Value, NonNullDominance> innerEntry = innerIterator.next();
Value nonNullValue = innerEntry.getKey();
NonNullDominance dominance = innerEntry.getValue();
if (dominance.isEverything()) {
assert nonNullValue.isDefinedByInstructionSatisfying(
definition -> definition.outValue() == nonNullValue);
continue;
}
assert dominance.isUnknown();
dominance = function.apply(instruction, nonNullValue);
if ((dominance.isNothing() && !nonNullValue.isArgument()) || dominance.isUnknown()) {
innerIterator.remove();
} else {
innerEntry.setValue(dominance);
}
}
if (dominancePerValue.isEmpty()) {
outerIterator.remove();
}
}
}
boolean contains(Instruction instruction, Value nonNullValue) {
Map<Value, NonNullDominance> dominancePerValue = nonNullValues.get(instruction);
return dominancePerValue != null && dominancePerValue.containsKey(nonNullValue);
}
boolean isEmpty() {
return nonNullValues.isEmpty();
}
void forEach(TriConsumer<Instruction, Value, NonNullDominance> consumer) {
nonNullValues.forEach(
(instruction, dominancePerValue) ->
dominancePerValue.forEach(
(nonNullValue, dominance) ->
consumer.accept(instruction, nonNullValue, dominance)));
}
void removeAll(Map<Instruction, Set<Value>> keys) {
keys.forEach(
(instruction, values) -> {
Map<Value, NonNullDominance> dominancePerValue = nonNullValues.get(instruction);
if (dominancePerValue != null) {
values.forEach(dominancePerValue::remove);
if (dominancePerValue.isEmpty()) {
nonNullValues.remove(instruction);
}
}
});
}
void removeIf(BiPredicate<Instruction, Value> predicate) {
Iterator<Entry<Instruction, Map<Value, NonNullDominance>>> outerIterator =
nonNullValues.entrySet().iterator();
while (outerIterator.hasNext()) {
Entry<Instruction, Map<Value, NonNullDominance>> outerEntry = outerIterator.next();
Instruction instruction = outerEntry.getKey();
Map<Value, NonNullDominance> dominancePerValue = outerEntry.getValue();
Iterator<Entry<Value, NonNullDominance>> innerIterator =
dominancePerValue.entrySet().iterator();
while (innerIterator.hasNext()) {
Value nonNullValue = innerIterator.next().getKey();
if (predicate.test(instruction, nonNullValue)) {
innerIterator.remove();
}
}
if (dominancePerValue.isEmpty()) {
outerIterator.remove();
}
}
}
static class Builder {
private final Map<Instruction, Map<Value, NonNullDominance>> nonNullValues =
new LinkedHashMap<>();
// Used to avoid unnecessary block splitting during phase 1.
private final Set<Value> nonNullValuesKnownToDominateAllUsers = Sets.newIdentityHashSet();
private void add(Instruction instruction, Value nonNullValue, NonNullDominance dominance) {
nonNullValues
.computeIfAbsent(instruction, ignore -> new LinkedHashMap<>())
.put(nonNullValue, dominance);
if (dominance.isEverything()) {
nonNullValuesKnownToDominateAllUsers.add(nonNullValue);
}
}
void addNonNullValueKnownToDominateAllUsers(Instruction instruction, Value nonNullValue) {
add(instruction, nonNullValue, NonNullDominance.everything());
}
void addNonNullValueWithUnknownDominance(Instruction instruction, Value nonNullValue) {
add(instruction, nonNullValue, NonNullDominance.unknown());
}
public boolean isMaybeNull(Value value) {
return !nonNullValuesKnownToDominateAllUsers.contains(value);
}
public NonNullValues build() {
return new NonNullValues(nonNullValues);
}
}
}
abstract static class NonNullDominance {
boolean isEverything() {
return false;
}
boolean isEverythingElse() {
return false;
}
boolean isNothing() {
return false;
}
boolean isSomething() {
return false;
}
SomethingNonNullDominance asSomething() {
return null;
}
boolean isUnknown() {
return false;
}
public static Builder builder(Value nonNullValue) {
return new Builder(nonNullValue);
}
public static EverythingNonNullDominance everything() {
return EverythingNonNullDominance.getInstance();
}
public static EverythingElseNonNullDominance everythingElse() {
return EverythingElseNonNullDominance.getInstance();
}
public static NothingNonNullDominance nothing() {
return NothingNonNullDominance.getInstance();
}
public static UnknownNonNullDominance redundant() {
return unknown();
}
public static SomethingNonNullDominance something(
Set<Instruction> dominatedUsers, Map<Phi, IntList> dominatedPhiUsers) {
return new SomethingNonNullDominance(dominatedUsers, dominatedPhiUsers);
}
public static UnknownNonNullDominance unknown() {
return UnknownNonNullDominance.getInstance();
}
static class Builder {
private final Value nonNullValue;
private final Set<Instruction> dominatedUsers = Sets.newIdentityHashSet();
private final Map<Phi, IntList> dominatedPhiUsers = new IdentityHashMap<>();
private Builder(Value nonNullValue) {
this.nonNullValue = nonNullValue;
}
void addDominatedUser(Instruction user) {
assert nonNullValue.uniqueUsers().contains(user);
assert !dominatedUsers.contains(user);
dominatedUsers.add(user);
}
void addDominatedPhiUser(Phi user, IntList dominatedPredecessorIndices) {
assert nonNullValue.uniquePhiUsers().contains(user);
assert !dominatedPhiUsers.containsKey(user);
dominatedPhiUsers.put(user, dominatedPredecessorIndices);
}
NonNullDominance build() {
if (dominatedUsers.isEmpty() && dominatedPhiUsers.isEmpty()) {
return nothing();
}
assert dominatedUsers.size() < nonNullValue.uniqueUsers().size()
|| dominatedPhiUsers.size() < nonNullValue.uniquePhiUsers().size();
return something(dominatedUsers, dominatedPhiUsers);
}
}
}
static class EverythingNonNullDominance extends NonNullDominance {
private static final EverythingNonNullDominance INSTANCE = new EverythingNonNullDominance();
private EverythingNonNullDominance() {}
public static EverythingNonNullDominance getInstance() {
return INSTANCE;
}
@Override
boolean isEverything() {
return true;
}
}
static class EverythingElseNonNullDominance extends NonNullDominance {
private static final EverythingElseNonNullDominance INSTANCE =
new EverythingElseNonNullDominance();
private EverythingElseNonNullDominance() {}
public static EverythingElseNonNullDominance getInstance() {
return INSTANCE;
}
@Override
boolean isEverythingElse() {
return true;
}
}
static class NothingNonNullDominance extends NonNullDominance {
private static final NothingNonNullDominance INSTANCE = new NothingNonNullDominance();
private NothingNonNullDominance() {}
public static NothingNonNullDominance getInstance() {
return INSTANCE;
}
@Override
boolean isNothing() {
return true;
}
}
static class SomethingNonNullDominance extends NonNullDominance {
private final Set<Instruction> dominatedUsers;
private final Map<Phi, IntList> dominatedPhiUsers;
SomethingNonNullDominance(
Set<Instruction> dominatedUsers, Map<Phi, IntList> dominatedPhiUsers) {
this.dominatedUsers = dominatedUsers;
this.dominatedPhiUsers = dominatedPhiUsers;
}
public Set<Instruction> getDominatedUsers() {
return dominatedUsers;
}
public Map<Phi, IntList> getDominatedPhiUsers() {
return dominatedPhiUsers;
}
@Override
boolean isSomething() {
return true;
}
@Override
SomethingNonNullDominance asSomething() {
return this;
}
}
static class UnknownNonNullDominance extends NonNullDominance {
private static final UnknownNonNullDominance INSTANCE = new UnknownNonNullDominance();
private UnknownNonNullDominance() {}
public static UnknownNonNullDominance getInstance() {
return INSTANCE;
}
@Override
boolean isUnknown() {
return true;
}
}
}