blob: d17ff72ecfe2fef5e2f4284e272a04178222f09f [file] [log] [blame]
// Copyright (c) 2021, 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.optimize.argumentpropagation.codescanner;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexMethodSignature;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.Function;
public class ConcretePolymorphicMethodState extends ConcreteMethodState
implements ConcretePolymorphicMethodStateOrBottom, ConcretePolymorphicMethodStateOrUnknown {
private final Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState;
private ConcretePolymorphicMethodState(
Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState) {
this.receiverBoundsToState = receiverBoundsToState;
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
}
private ConcretePolymorphicMethodState(
DynamicType receiverBounds, ConcreteMonomorphicMethodStateOrUnknown methodState) {
this.receiverBoundsToState = new HashMap<>(1);
receiverBoundsToState.put(receiverBounds, methodState);
assert !isEffectivelyUnknown();
}
public static ConcretePolymorphicMethodStateOrUnknown create(
DynamicType receiverBounds, ConcreteMonomorphicMethodStateOrUnknown methodState) {
return receiverBounds.isUnknown() && methodState.isUnknown()
? MethodState.unknown()
: new ConcretePolymorphicMethodState(receiverBounds, methodState);
}
private ConcretePolymorphicMethodStateOrUnknown add(
AppView<AppInfoWithLiveness> appView,
DexMethodSignature methodSignature,
DynamicType bounds,
ConcreteMonomorphicMethodStateOrUnknown methodState,
StateCloner cloner) {
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
if (methodState.isUnknown()) {
if (bounds.isUnknown()) {
return unknown();
} else {
receiverBoundsToState.put(bounds, methodState);
return this;
}
} else {
assert methodState.isMonomorphic();
ConcreteMonomorphicMethodStateOrUnknown newMethodStateForBounds =
joinInner(
appView, methodSignature, receiverBoundsToState.get(bounds), methodState, cloner);
if (bounds.isUnknown() && newMethodStateForBounds.isUnknown()) {
return unknown();
} else {
receiverBoundsToState.put(bounds, newMethodStateForBounds);
return this;
}
}
}
@SuppressWarnings("unchecked")
private static ConcreteMonomorphicMethodStateOrUnknown joinInner(
AppView<AppInfoWithLiveness> appView,
DexMethodSignature methodSignature,
ConcreteMonomorphicMethodStateOrUnknown methodState,
ConcreteMonomorphicMethodStateOrUnknown other,
StateCloner cloner) {
if (methodState == null) {
return (ConcreteMonomorphicMethodStateOrUnknown) cloner.mutableCopy(other);
}
if (methodState.isUnknown() || other.isUnknown()) {
return unknown();
}
assert methodState.isMonomorphic();
return methodState
.asMonomorphic()
.mutableJoin(appView, methodSignature, other.asMonomorphic(), cloner);
}
public void forEach(
BiConsumer<? super DynamicType, ? super ConcreteMonomorphicMethodStateOrUnknown> consumer) {
receiverBoundsToState.forEach(consumer);
}
public MethodState getMethodStateForBounds(DynamicType dynamicType) {
ConcreteMonomorphicMethodStateOrUnknown methodStateForBounds =
receiverBoundsToState.get(dynamicType);
if (methodStateForBounds != null) {
return methodStateForBounds;
}
return MethodState.bottom();
}
public boolean isEffectivelyBottom() {
return receiverBoundsToState.isEmpty();
}
public boolean isEffectivelyUnknown() {
return getMethodStateForBounds(DynamicType.unknown()).isUnknown();
}
@Override
public MethodState mutableCopy() {
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> receiverBoundsToState =
new HashMap<>();
forEach((bounds, methodState) -> receiverBoundsToState.put(bounds, methodState.mutableCopy()));
return new ConcretePolymorphicMethodState(receiverBoundsToState);
}
public MethodState mutableCopyWithRewrittenBounds(
AppView<AppInfoWithLiveness> appView,
Function<DynamicType, DynamicType> boundsRewriter,
DexMethodSignature methodSignature,
StateCloner cloner) {
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
Map<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> rewrittenReceiverBoundsToState =
new HashMap<>();
for (Entry<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> entry :
receiverBoundsToState.entrySet()) {
DynamicType rewrittenBounds = boundsRewriter.apply(entry.getKey());
if (rewrittenBounds == null) {
continue;
}
ConcreteMonomorphicMethodStateOrUnknown existingMethodStateForBounds =
rewrittenReceiverBoundsToState.get(rewrittenBounds);
ConcreteMonomorphicMethodStateOrUnknown newMethodStateForBounds =
joinInner(
appView, methodSignature, existingMethodStateForBounds, entry.getValue(), cloner);
if (rewrittenBounds.isUnknown() && newMethodStateForBounds.isUnknown()) {
return unknown();
}
rewrittenReceiverBoundsToState.put(rewrittenBounds, newMethodStateForBounds);
}
return rewrittenReceiverBoundsToState.isEmpty()
? bottom()
: new ConcretePolymorphicMethodState(rewrittenReceiverBoundsToState);
}
public MethodState mutableJoin(
AppView<AppInfoWithLiveness> appView,
DexMethodSignature methodSignature,
ConcretePolymorphicMethodState methodState,
StateCloner cloner) {
assert !isEffectivelyBottom();
assert !isEffectivelyUnknown();
assert !methodState.isEffectivelyBottom();
assert !methodState.isEffectivelyUnknown();
for (Entry<DynamicType, ConcreteMonomorphicMethodStateOrUnknown> entry :
methodState.receiverBoundsToState.entrySet()) {
ConcretePolymorphicMethodStateOrUnknown result =
add(appView, methodSignature, entry.getKey(), entry.getValue(), cloner);
if (result.isUnknown()) {
return result;
}
assert result == this;
}
assert !isEffectivelyUnknown();
return this;
}
public Collection<ConcreteMonomorphicMethodStateOrUnknown> values() {
return receiverBoundsToState.values();
}
@Override
public boolean isPolymorphic() {
return true;
}
@Override
public ConcretePolymorphicMethodState asPolymorphic() {
return this;
}
@Override
public ConcretePolymorphicMethodStateOrBottom asPolymorphicOrBottom() {
return this;
}
}