Towards using a new abstraction for definitely set/unset bits
This cleans up the joining of abstract values by introducing a new AbstractValueJoiner class that encapsulates all logic related to joining (including which and when to use specific abstractions).
This also introduces a new DefiniteBitsNumberValue abstraction that can be used for integer values. Changing the join of abstract values to use this new abstraction is left for future work.
Bug: b/196017578
Fixes: b/196321452
Change-Id: I5fba6fdc8d04c185d9b932c08b354c0eff5f97a2
diff --git a/src/main/java/com/android/tools/r8/graph/AppView.java b/src/main/java/com/android/tools/r8/graph/AppView.java
index 5a20e3d..06deed3 100644
--- a/src/main/java/com/android/tools/r8/graph/AppView.java
+++ b/src/main/java/com/android/tools/r8/graph/AppView.java
@@ -26,6 +26,8 @@
import com.android.tools.r8.ir.analysis.proto.GeneratedMessageLiteShrinker;
import com.android.tools.r8.ir.analysis.proto.ProtoShrinker;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueFieldJoiner;
+import com.android.tools.r8.ir.analysis.value.AbstractValueJoiner.AbstractValueParameterJoiner;
import com.android.tools.r8.ir.desugar.TypeRewriter;
import com.android.tools.r8.ir.optimize.enums.EnumDataMap;
import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoFactory;
@@ -100,6 +102,8 @@
private KeepInfoCollection keepInfo = null;
private final AbstractValueFactory abstractValueFactory = new AbstractValueFactory();
+ private final AbstractValueFieldJoiner abstractValueFieldJoiner;
+ private final AbstractValueParameterJoiner abstractValueParameterJoiner;
private final InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory =
new InstanceFieldInitializationInfoFactory();
private final SimpleInliningConstraintFactory simpleInliningConstraintFactory =
@@ -170,13 +174,20 @@
this.context =
timing.time(
"Compilation context", () -> CompilationContext.createInitialContext(options()));
+ this.wholeProgramOptimizations = wholeProgramOptimizations;
+ if (enableWholeProgramOptimizations()) {
+ abstractValueFieldJoiner = new AbstractValueFieldJoiner(withClassHierarchy());
+ abstractValueParameterJoiner = new AbstractValueParameterJoiner(withClassHierarchy());
+ } else {
+ abstractValueFieldJoiner = null;
+ abstractValueParameterJoiner = null;
+ }
this.artProfileCollection = artProfileCollection;
this.startupProfile = startupProfile;
this.dontWarnConfiguration =
timing.time(
"Dont warn config",
() -> DontWarnConfiguration.create(options().getProguardConfiguration()));
- this.wholeProgramOptimizations = wholeProgramOptimizations;
this.initClassLens = timing.time("Init class lens", InitClassLens::getThrowingInstance);
this.typeRewriter = mapper;
timing.begin("Create argument propagator");
@@ -314,6 +325,14 @@
return abstractValueFactory;
}
+ public AbstractValueFieldJoiner getAbstractValueFieldJoiner() {
+ return abstractValueFieldJoiner;
+ }
+
+ public AbstractValueParameterJoiner getAbstractValueParameterJoiner() {
+ return abstractValueParameterJoiner;
+ }
+
public InstanceFieldInitializationInfoFactory instanceFieldInitializationInfoFactory() {
return instanceFieldInitializationInfoFactory;
}
diff --git a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
index 1aa866f..fb50d0e 100644
--- a/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexProgramClass.java
@@ -415,6 +415,18 @@
.traverse((field, value) -> fn.apply(field.asProgramField(), value), initialValue);
}
+ public TraversalContinuation<?, ?> traverseProgramInstanceFields(
+ Function<? super ProgramField, TraversalContinuation<?, ?>> fn) {
+ return getFieldCollection().traverseInstanceFields(field -> fn.apply(field.asProgramField()));
+ }
+
+ public <BT, CT> TraversalContinuation<BT, CT> traverseProgramInstanceFields(
+ BiFunction<? super ProgramField, CT, TraversalContinuation<BT, CT>> fn, CT initialValue) {
+ return getFieldCollection()
+ .traverseInstanceFields(
+ (field, value) -> fn.apply(field.asProgramField(), value), initialValue);
+ }
+
public TraversalContinuation<?, ?> traverseProgramMethods(
Function<? super ProgramMethod, TraversalContinuation<?, ?>> fn) {
return getMethodCollection().traverse(method -> fn.apply(new ProgramMethod(this, method)));
diff --git a/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java b/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
index dee7446..ec0758a 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldArrayBacking.java
@@ -72,15 +72,61 @@
@Override
<BT, CT> TraversalContinuation<BT, CT> traverse(
DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
- TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
- for (DexEncodedField definition : staticFields) {
- DexClassAndField field = DexClassAndField.create(holder, definition);
- traversalContinuation = fn.apply(field);
- if (traversalContinuation.shouldBreak()) {
- return traversalContinuation;
- }
+ TraversalContinuation<BT, CT> traversalContinuation = traverseStaticFields(holder, fn);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
}
- for (DexEncodedField definition : instanceFields) {
+ return traverseInstanceFields(holder, fn);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverse(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ TraversalContinuation<BT, CT> traversalContinuation =
+ traverseStaticFields(holder, fn, initialValue);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ return traverseInstanceFields(
+ holder, fn, traversalContinuation.asContinue().getValueOrDefault(null));
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return traverseInstanceOrStaticFields(holder, instanceFields, fn);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return traverseInstanceOrStaticFields(holder, instanceFields, fn, initialValue);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return traverseInstanceOrStaticFields(holder, staticFields, fn);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return traverseInstanceOrStaticFields(holder, staticFields, fn, initialValue);
+ }
+
+ private static <BT, CT> TraversalContinuation<BT, CT> traverseInstanceOrStaticFields(
+ DexClass holder,
+ DexEncodedField[] fields,
+ Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
+ for (DexEncodedField definition : fields) {
DexClassAndField field = DexClassAndField.create(holder, definition);
traversalContinuation = fn.apply(field);
if (traversalContinuation.shouldBreak()) {
@@ -90,21 +136,14 @@
return traversalContinuation;
}
- @Override
- <BT, CT> TraversalContinuation<BT, CT> traverse(
+ private static <BT, CT> TraversalContinuation<BT, CT> traverseInstanceOrStaticFields(
DexClass holder,
+ DexEncodedField[] fields,
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
TraversalContinuation<BT, CT> traversalContinuation =
TraversalContinuation.doContinue(initialValue);
- for (DexEncodedField definition : staticFields) {
- DexClassAndField field = DexClassAndField.create(holder, definition);
- traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
- if (traversalContinuation.shouldBreak()) {
- return traversalContinuation;
- }
- }
- for (DexEncodedField definition : instanceFields) {
+ for (DexEncodedField definition : fields) {
DexClassAndField field = DexClassAndField.create(holder, definition);
traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
if (traversalContinuation.shouldBreak()) {
diff --git a/src/main/java/com/android/tools/r8/graph/FieldCollection.java b/src/main/java/com/android/tools/r8/graph/FieldCollection.java
index 729b238..9b3c09b 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldCollection.java
@@ -73,6 +73,28 @@
return backing.traverse(holder, fn, initialValue);
}
+ public <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return backing.traverseInstanceFields(holder, fn);
+ }
+
+ public <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return backing.traverseInstanceFields(holder, fn, initialValue);
+ }
+
+ public <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return backing.traverseStaticFields(holder, fn);
+ }
+
+ public <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return backing.traverseStaticFields(holder, fn, initialValue);
+ }
+
public boolean verify() {
forEachField(
field -> {
diff --git a/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java b/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
index 522748e..cce38fd 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldCollectionBacking.java
@@ -34,6 +34,22 @@
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue);
+ abstract <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn);
+
+ abstract <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue);
+
+ abstract <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn);
+
+ abstract <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue);
+
// Collection methods.
abstract int size();
diff --git a/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java b/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
index 6b74673..a6d9b57 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldMapBacking.java
@@ -3,6 +3,8 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.graph;
+import static com.google.common.base.Predicates.alwaysTrue;
+
import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.TraversalContinuation;
import it.unimi.dsi.fastutil.objects.Object2ReferenceLinkedOpenHashMap;
@@ -49,15 +51,7 @@
@Override
<BT, CT> TraversalContinuation<BT, CT> traverse(
DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
- TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
- for (DexEncodedField definition : fieldMap.values()) {
- DexClassAndField field = DexClassAndField.create(holder, definition);
- traversalContinuation = fn.apply(field);
- if (traversalContinuation.shouldBreak()) {
- return traversalContinuation;
- }
- }
- return traversalContinuation;
+ return traverseFieldsThatMatches(holder, fn, alwaysTrue());
}
@Override
@@ -65,13 +59,68 @@
DexClass holder,
BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
CT initialValue) {
+ return traverseFieldsThatMatches(holder, fn, alwaysTrue(), initialValue);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return traverseFieldsThatMatches(holder, fn, DexEncodedField::isInstance);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseInstanceFields(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return traverseFieldsThatMatches(holder, fn, DexEncodedField::isInstance, initialValue);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ DexClass holder, Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn) {
+ return traverseFieldsThatMatches(holder, fn, DexEncodedField::isStatic);
+ }
+
+ @Override
+ <BT, CT> TraversalContinuation<BT, CT> traverseStaticFields(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ CT initialValue) {
+ return traverseFieldsThatMatches(holder, fn, DexEncodedField::isStatic, initialValue);
+ }
+
+ private <BT, CT> TraversalContinuation<BT, CT> traverseFieldsThatMatches(
+ DexClass holder,
+ Function<? super DexClassAndField, TraversalContinuation<BT, CT>> fn,
+ Predicate<? super DexEncodedField> predicate) {
+ TraversalContinuation<BT, CT> traversalContinuation = TraversalContinuation.doContinue();
+ for (DexEncodedField definition : fieldMap.values()) {
+ if (predicate.test(definition)) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field);
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
+ }
+ }
+ return traversalContinuation;
+ }
+
+ private <BT, CT> TraversalContinuation<BT, CT> traverseFieldsThatMatches(
+ DexClass holder,
+ BiFunction<? super DexClassAndField, ? super CT, TraversalContinuation<BT, CT>> fn,
+ Predicate<? super DexEncodedField> predicate,
+ CT initialValue) {
TraversalContinuation<BT, CT> traversalContinuation =
TraversalContinuation.doContinue(initialValue);
for (DexEncodedField definition : fieldMap.values()) {
- DexClassAndField field = DexClassAndField.create(holder, definition);
- traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
- if (traversalContinuation.shouldBreak()) {
- return traversalContinuation;
+ if (predicate.test(definition)) {
+ DexClassAndField field = DexClassAndField.create(holder, definition);
+ traversalContinuation = fn.apply(field, traversalContinuation.asContinue().getValue());
+ if (traversalContinuation.shouldBreak()) {
+ return traversalContinuation;
+ }
}
}
return traversalContinuation;
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerUtils.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerUtils.java
index bd83a89..6eecb7a 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerUtils.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMergerUtils.java
@@ -7,9 +7,14 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.ProgramField;
public class HorizontalClassMergerUtils {
+ public static boolean isClassIdField(AppView<?> appView, ProgramField field) {
+ return isClassIdField(appView, field.getDefinition());
+ }
+
public static boolean isClassIdField(AppView<?> appView, DexEncodedField field) {
DexField classIdField = appView.dexItemFactory().objectMembers.classIdField;
if (field.isD8R8Synthesized() && field.getType().isIntType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
index d871616..e6ac132 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAccessAnalysis.java
@@ -85,7 +85,7 @@
appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
if (field != null) {
if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field, code.context());
+ fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field);
}
if (fieldBitAccessAnalysis != null) {
fieldBitAccessAnalysis.recordFieldAccess(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
index 0781787..0f47d7e 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
@@ -45,12 +45,13 @@
import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.KeepFieldInfo;
+import com.android.tools.r8.utils.TraversalContinuation;
+import com.android.tools.r8.utils.collections.ProgramFieldMap;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import it.unimi.dsi.fastutil.objects.Reference2IntOpenHashMap;
import java.util.ArrayList;
import java.util.IdentityHashMap;
-import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@@ -76,7 +77,7 @@
// has been seen to the field.
private final Map<DexEncodedField, FieldState> fieldStates = new ConcurrentHashMap<>();
- private final Map<DexProgramClass, Map<DexEncodedField, AbstractValue>>
+ private final Map<DexProgramClass, ProgramFieldMap<AbstractValue>>
abstractFinalInstanceFieldValues = new ConcurrentHashMap<>();
FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
@@ -116,15 +117,14 @@
// No instance fields to track.
return;
}
- Map<DexEncodedField, AbstractValue> abstractFinalInstanceFieldValuesForClass =
- new IdentityHashMap<>();
+ ProgramFieldMap<AbstractValue> abstractFinalInstanceFieldValuesForClass =
+ ProgramFieldMap.create();
clazz.forEachProgramInstanceField(
field -> {
if (field.isFinalOrEffectivelyFinal(appView)) {
FieldAccessInfo fieldAccessInfo = fieldAccessInfos.get(field.getReference());
if (fieldAccessInfo != null && !fieldAccessInfo.hasReflectiveAccess()) {
- abstractFinalInstanceFieldValuesForClass.put(
- field.getDefinition(), BottomValue.getInstance());
+ abstractFinalInstanceFieldValuesForClass.put(field, BottomValue.getInstance());
}
}
});
@@ -185,13 +185,13 @@
});
}
- void recordFieldAccess(FieldInstruction instruction, ProgramField field, ProgramMethod context) {
+ void recordFieldAccess(FieldInstruction instruction, ProgramField field) {
if (instruction.isFieldPut()) {
- recordFieldPut(field, instruction.value(), context);
+ recordFieldPut(field, instruction.value());
}
}
- private void recordFieldPut(ProgramField field, Value value, ProgramMethod context) {
+ private void recordFieldPut(ProgramField field, Value value) {
// For now only attempt to prove that fields are definitely null. In order to prove a single
// value for fields that are not definitely null, we need to prove that the given field is never
// read before it is written.
@@ -225,12 +225,12 @@
if (fieldState.isArray()) {
ConcreteArrayTypeFieldState arrayFieldState = fieldState.asArray();
- return arrayFieldState.mutableJoin(appView, abstractValue);
+ return arrayFieldState.mutableJoin(appView, field, abstractValue);
}
if (fieldState.isPrimitive()) {
ConcretePrimitiveTypeFieldState primitiveFieldState = fieldState.asPrimitive();
- return primitiveFieldState.mutableJoin(abstractValue, abstractValueFactory);
+ return primitiveFieldState.mutableJoin(appView, field, abstractValue);
}
assert fieldState.isClass();
@@ -242,7 +242,7 @@
}
void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
- Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
+ ProgramFieldMap<AbstractValue> abstractInstanceFieldValuesForClass =
abstractFinalInstanceFieldValues.get(clazz);
if (abstractInstanceFieldValuesForClass == null) {
// We are not tracking the value of any of clazz' instance fields.
@@ -273,75 +273,59 @@
// Synchronize on the lattice element (abstractInstanceFieldValuesForClass) in case we process
// another allocation site of `clazz` concurrently.
synchronized (abstractInstanceFieldValuesForClass) {
- Iterator<Map.Entry<DexEncodedField, AbstractValue>> iterator =
- abstractInstanceFieldValuesForClass.entrySet().iterator();
- while (iterator.hasNext()) {
- Map.Entry<DexEncodedField, AbstractValue> entry = iterator.next();
- DexEncodedField field = entry.getKey();
- AbstractValue abstractValue = entry.getValue();
-
- // The power set lattice is an expensive abstraction, so use it with caution.
- boolean isClassIdField = HorizontalClassMergerUtils.isClassIdField(appView, field);
-
- InstanceFieldInitializationInfo initializationInfo =
- initializationInfoCollection.get(field);
- if (initializationInfo.isArgumentInitializationInfo()) {
- InstanceFieldArgumentInitializationInfo argumentInitializationInfo =
- initializationInfo.asArgumentInitializationInfo();
- Value argument = invoke.arguments().get(argumentInitializationInfo.getArgumentIndex());
- AbstractValue argumentAbstractValue = argument.getAbstractValue(appView, context);
- abstractValue =
- abstractValue.join(
- argumentAbstractValue,
- appView.abstractValueFactory(),
- field.getType().isReferenceType(),
- isClassIdField);
- assert !abstractValue.isBottom();
- } else if (initializationInfo.isSingleValue()) {
- SingleValue singleValueInitializationInfo = initializationInfo.asSingleValue();
- abstractValue =
- abstractValue.join(
- singleValueInitializationInfo,
- appView.abstractValueFactory(),
- field.getType().isReferenceType(),
- isClassIdField);
- } else if (initializationInfo.isTypeInitializationInfo()) {
- // TODO(b/149732532): Not handled, for now.
- abstractValue = UnknownValue.getInstance();
- } else {
- assert initializationInfo.isUnknown();
- abstractValue = UnknownValue.getInstance();
- }
-
- assert !abstractValue.isBottom();
-
- // When approximating the possible values for the $r8$classId fields from horizontal class
- // merging, give up if the set of possible values equals the size of the merge group. In
- // this case, the information is useless.
- if (isClassIdField && abstractValue.isNonConstantNumberValue()) {
- NonConstantNumberValue initialAbstractValue =
- field.getOptimizationInfo().getAbstractValue().asNonConstantNumberValue();
- if (initialAbstractValue != null) {
- if (abstractValue.asNonConstantNumberValue().getAbstractionSize()
- >= initialAbstractValue.getAbstractionSize()) {
+ abstractInstanceFieldValuesForClass.removeIf(
+ (field, abstractValue, entry) -> {
+ InstanceFieldInitializationInfo initializationInfo =
+ initializationInfoCollection.get(field);
+ if (initializationInfo.isArgumentInitializationInfo()) {
+ InstanceFieldArgumentInitializationInfo argumentInitializationInfo =
+ initializationInfo.asArgumentInitializationInfo();
+ Value argument =
+ invoke.arguments().get(argumentInitializationInfo.getArgumentIndex());
+ AbstractValue argumentAbstractValue = argument.getAbstractValue(appView, context);
+ abstractValue =
+ appView
+ .getAbstractValueFieldJoiner()
+ .join(abstractValue, argumentAbstractValue, field);
+ } else if (initializationInfo.isSingleValue()) {
+ SingleValue singleValueInitializationInfo = initializationInfo.asSingleValue();
+ abstractValue =
+ appView
+ .getAbstractValueFieldJoiner()
+ .join(abstractValue, singleValueInitializationInfo, field);
+ } else if (initializationInfo.isTypeInitializationInfo()) {
+ // TODO(b/149732532): Not handled, for now.
+ abstractValue = UnknownValue.getInstance();
+ } else {
+ assert initializationInfo.isUnknown();
abstractValue = UnknownValue.getInstance();
}
- }
- }
- if (!abstractValue.isUnknown()) {
- entry.setValue(abstractValue);
- continue;
- }
+ assert !abstractValue.isBottom();
- // We just lost track for this field.
- iterator.remove();
- }
+ // When approximating the possible values for the $r8$classId fields from horizontal
+ // class
+ // merging, give up if the set of possible values equals the size of the merge group. In
+ // this case, the information is useless.
+ if (abstractValue.isNonConstantNumberValue()) {
+ assert HorizontalClassMergerUtils.isClassIdField(appView, field);
+ NonConstantNumberValue initialAbstractValue =
+ field.getOptimizationInfo().getAbstractValue().asNonConstantNumberValue();
+ if (initialAbstractValue != null
+ && abstractValue.asNonConstantNumberValue().getAbstractionSize()
+ >= initialAbstractValue.getAbstractionSize()) {
+ abstractValue = UnknownValue.getInstance();
+ }
+ }
+
+ entry.setValue(abstractValue);
+ return abstractValue.isUnknown();
+ });
}
}
private void recordAllFieldPutsProcessed(
- ProgramField field, ProgramMethod context, OptimizationFeedbackDelayed feedback) {
+ ProgramField field, OptimizationFeedbackDelayed feedback) {
FieldState fieldState = fieldStates.getOrDefault(field.getDefinition(), FieldState.bottom());
AbstractValue abstractValue = fieldState.getAbstractValue(appView.abstractValueFactory());
if (abstractValue.isNonTrivial()) {
@@ -384,10 +368,9 @@
.get(field);
if (fieldInitializationInfo.isSingleValue()) {
abstractValue =
- abstractValue.join(
- fieldInitializationInfo.asSingleValue(),
- appView.abstractValueFactory(),
- field.getType());
+ appView
+ .getAbstractValueFieldJoiner()
+ .join(abstractValue, fieldInitializationInfo.asSingleValue(), field);
if (abstractValue.isUnknown()) {
break;
}
@@ -413,24 +396,25 @@
private void recordAllAllocationsSitesProcessed(
DexProgramClass clazz, OptimizationFeedbackDelayed feedback) {
- Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
+ ProgramFieldMap<AbstractValue> abstractInstanceFieldValuesForClass =
abstractFinalInstanceFieldValues.get(clazz);
if (abstractInstanceFieldValuesForClass == null) {
return;
}
- for (DexEncodedField field : clazz.instanceFields()) {
- AbstractValue abstractValue =
- abstractInstanceFieldValuesForClass.getOrDefault(field, UnknownValue.getInstance());
- if (abstractValue.isBottom()) {
- feedback.modifyAppInfoWithLiveness(modifier -> modifier.removeInstantiatedType(clazz));
- break;
- }
- if (abstractValue.isUnknown()) {
- continue;
- }
- feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
- }
+ clazz.traverseProgramInstanceFields(
+ field -> {
+ AbstractValue abstractValue =
+ abstractInstanceFieldValuesForClass.getOrDefault(field, UnknownValue.getInstance());
+ if (abstractValue.isBottom()) {
+ feedback.modifyAppInfoWithLiveness(modifier -> modifier.removeInstantiatedType(clazz));
+ return TraversalContinuation.doBreak();
+ }
+ if (abstractValue.isNonTrivial()) {
+ feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
+ }
+ return TraversalContinuation.doContinue();
+ });
}
public void waveDone(ProgramMethodSet wave, OptimizationFeedbackDelayed feedback) {
@@ -438,8 +422,7 @@
// therefore important that the optimization info has been flushed in advance.
assert feedback.noUpdatesLeft();
for (ProgramMethod method : wave) {
- fieldAccessGraph.markProcessed(
- method, field -> recordAllFieldPutsProcessed(field, method, feedback));
+ fieldAccessGraph.markProcessed(method, field -> recordAllFieldPutsProcessed(field, feedback));
objectAllocationGraph.markProcessed(
method, clazz -> recordAllAllocationsSitesProcessed(clazz, feedback));
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java
index 1b82558..fef8935 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteArrayTypeFieldState.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.analysis.fieldaccess.state;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -36,9 +37,10 @@
return this;
}
- public FieldState mutableJoin(AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
+ public FieldState mutableJoin(
+ AppView<AppInfoWithLiveness> appView, ProgramField field, AbstractValue abstractValue) {
this.abstractValue =
- this.abstractValue.joinReference(abstractValue, appView.abstractValueFactory());
+ appView.getAbstractValueFieldJoiner().join(this.abstractValue, abstractValue, field);
return isEffectivelyUnknown() ? unknown() : this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java
index 3f51c25..a323ba6 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcreteClassTypeFieldState.java
@@ -48,7 +48,7 @@
ProgramField field) {
assert field.getType().isClassType();
this.abstractValue =
- this.abstractValue.joinReference(abstractValue, appView.abstractValueFactory());
+ appView.getAbstractValueFieldJoiner().join(this.abstractValue, abstractValue, field);
this.dynamicType =
WideningUtils.widenDynamicNonReceiverType(
appView, this.dynamicType.join(appView, dynamicType), field.getType());
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java
index c2eb778..1f329f5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/state/ConcretePrimitiveTypeFieldState.java
@@ -4,8 +4,11 @@
package com.android.tools.r8.ir.analysis.fieldaccess.state;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
/** The information that we track for fields whose type is a primitive type. */
public class ConcretePrimitiveTypeFieldState extends ConcreteFieldState {
@@ -38,11 +41,12 @@
}
public FieldState mutableJoin(
- AbstractValue abstractValue, AbstractValueFactory abstractValueFactory) {
+ AppView<AppInfoWithLiveness> appView, ProgramField field, AbstractValue abstractValue) {
if (abstractValue.isUnknown()) {
return FieldState.unknown();
}
- this.abstractValue = this.abstractValue.joinPrimitive(abstractValue, abstractValueFactory);
+ this.abstractValue =
+ appView.getAbstractValueFieldJoiner().join(this.abstractValue, abstractValue, field);
return isEffectivelyUnknown() ? unknown() : this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
index c4e0a83..fccb291 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValue.java
@@ -5,7 +5,6 @@
package com.android.tools.r8.ir.analysis.value;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.lens.GraphLens;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -176,71 +175,12 @@
return null;
}
- public AbstractValue join(AbstractValue other, AbstractValueFactory factory, DexType type) {
- return join(other, factory, type.isReferenceType(), false);
+ public boolean isDefiniteBitsNumberValue() {
+ return false;
}
- public AbstractValue joinPrimitive(AbstractValue other, AbstractValueFactory factory) {
- return join(other, factory, false, false);
- }
-
- public AbstractValue joinReference(AbstractValue other, AbstractValueFactory factory) {
- return join(other, factory, true, false);
- }
-
- // TODO(b/196321452): Clean this up, in particular, replace the "allow" parameters by a
- // configuration object.
- public AbstractValue join(
- AbstractValue other,
- AbstractValueFactory factory,
- boolean allowNullOrAbstractValue,
- boolean allowNonConstantNumbers) {
- if (isBottom() || other.isUnknown()) {
- return other;
- }
- if (isUnknown() || other.isBottom()) {
- return this;
- }
- if (equals(other)) {
- return this;
- }
- if (allowNullOrAbstractValue) {
- if (isNull()) {
- return NullOrAbstractValue.create(other);
- }
- if (other.isNull()) {
- return NullOrAbstractValue.create(this);
- }
- if (isNullOrAbstractValue() && asNullOrAbstractValue().getNonNullValue().equals(other)) {
- return this;
- }
- if (other.isNullOrAbstractValue()
- && other.asNullOrAbstractValue().getNonNullValue().equals(this)) {
- return other;
- }
- return unknown();
- }
- assert !isNullOrAbstractValue();
- assert !other.isNullOrAbstractValue();
- if (allowNonConstantNumbers
- && isConstantOrNonConstantNumberValue()
- && other.isConstantOrNonConstantNumberValue()) {
- NumberFromSetValue.Builder numberFromSetValueBuilder;
- if (isSingleNumberValue()) {
- numberFromSetValueBuilder = NumberFromSetValue.builder(asSingleNumberValue());
- } else {
- assert isNumberFromSetValue();
- numberFromSetValueBuilder = asNumberFromSetValue().instanceBuilder();
- }
- if (other.isSingleNumberValue()) {
- numberFromSetValueBuilder.addInt(other.asSingleNumberValue().getIntValue());
- } else {
- assert other.isNumberFromSetValue();
- numberFromSetValueBuilder.addInts(other.asNumberFromSetValue());
- }
- return numberFromSetValueBuilder.build(factory);
- }
- return unknown();
+ public DefiniteBitsNumberValue asDefiniteBitsNumberValue() {
+ return null;
}
public abstract AbstractValue rewrittenWithLens(
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java
new file mode 100644
index 0000000..836ccd5
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/AbstractValueJoiner.java
@@ -0,0 +1,174 @@
+// Copyright (c) 2023, 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.value;
+
+import static com.android.tools.r8.ir.analysis.value.AbstractValue.unknown;
+
+import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramField;
+import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerUtils;
+
+public abstract class AbstractValueJoiner {
+
+ protected final AppView<? extends AppInfoWithClassHierarchy> appView;
+
+ private AbstractValueJoiner(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ this.appView = appView;
+ }
+
+ final AbstractValue internalJoin(
+ AbstractValue abstractValue,
+ AbstractValue otherAbstractValue,
+ AbstractValueJoinerConfig config,
+ DexType type) {
+ if (abstractValue.isBottom() || otherAbstractValue.isUnknown()) {
+ return otherAbstractValue;
+ }
+ if (abstractValue.isUnknown()
+ || otherAbstractValue.isBottom()
+ || abstractValue.equals(otherAbstractValue)) {
+ return abstractValue;
+ }
+ return type.isReferenceType()
+ ? joinReference(abstractValue, otherAbstractValue)
+ : joinPrimitive(abstractValue, otherAbstractValue, config);
+ }
+
+ private AbstractValue joinPrimitive(
+ AbstractValue abstractValue,
+ AbstractValue otherAbstractValue,
+ AbstractValueJoinerConfig config) {
+ assert !abstractValue.isNullOrAbstractValue();
+ assert !otherAbstractValue.isNullOrAbstractValue();
+
+ if (config.canUseDefiniteBitsAbstraction()
+ && abstractValue.isConstantOrNonConstantNumberValue()
+ && otherAbstractValue.isConstantOrNonConstantNumberValue()) {
+ // TODO(b/196017578): Implement join.
+ }
+
+ if (config.canUseNumberIntervalAndNumberSetAbstraction()
+ && abstractValue.isConstantOrNonConstantNumberValue()
+ && otherAbstractValue.isConstantOrNonConstantNumberValue()) {
+ NumberFromSetValue.Builder numberFromSetValueBuilder;
+ if (abstractValue.isSingleNumberValue()) {
+ numberFromSetValueBuilder = NumberFromSetValue.builder(abstractValue.asSingleNumberValue());
+ } else {
+ assert abstractValue.isNumberFromSetValue();
+ numberFromSetValueBuilder = abstractValue.asNumberFromSetValue().instanceBuilder();
+ }
+ if (otherAbstractValue.isSingleNumberValue()) {
+ numberFromSetValueBuilder.addInt(otherAbstractValue.asSingleNumberValue().getIntValue());
+ } else {
+ assert otherAbstractValue.isNumberFromSetValue();
+ numberFromSetValueBuilder.addInts(otherAbstractValue.asNumberFromSetValue());
+ }
+ return numberFromSetValueBuilder.build(appView.abstractValueFactory());
+ }
+
+ return unknown();
+ }
+
+ private AbstractValue joinReference(
+ AbstractValue abstractValue, AbstractValue otherAbstractValue) {
+ if (abstractValue.isNull()) {
+ return NullOrAbstractValue.create(otherAbstractValue);
+ }
+ if (otherAbstractValue.isNull()) {
+ return NullOrAbstractValue.create(abstractValue);
+ }
+ if (abstractValue.isNullOrAbstractValue()
+ && abstractValue.asNullOrAbstractValue().getNonNullValue().equals(otherAbstractValue)) {
+ return abstractValue;
+ }
+ if (otherAbstractValue.isNullOrAbstractValue()
+ && otherAbstractValue.asNullOrAbstractValue().getNonNullValue().equals(abstractValue)) {
+ return otherAbstractValue;
+ }
+ return unknown();
+ }
+
+ public static class AbstractValueFieldJoiner extends AbstractValueJoiner {
+
+ public AbstractValueFieldJoiner(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
+ }
+
+ public AbstractValue join(
+ AbstractValue abstractValue, AbstractValue otherAbstractValue, ProgramField field) {
+ AbstractValueJoinerConfig config = getConfig(field);
+ AbstractValue result =
+ internalJoin(abstractValue, otherAbstractValue, config, field.getType());
+ assert result.equals(
+ internalJoin(otherAbstractValue, abstractValue, config, field.getType()));
+ return result;
+ }
+
+ private AbstractValueJoinerConfig getConfig(ProgramField field) {
+ if (HorizontalClassMergerUtils.isClassIdField(appView, field)) {
+ return AbstractValueJoinerConfig.getClassIdFieldConfig();
+ }
+ return AbstractValueJoinerConfig.getDefaultConfig();
+ }
+ }
+
+ public static class AbstractValueParameterJoiner extends AbstractValueJoiner {
+
+ public AbstractValueParameterJoiner(AppView<? extends AppInfoWithClassHierarchy> appView) {
+ super(appView);
+ }
+
+ public AbstractValue join(
+ AbstractValue abstractValue, AbstractValue otherAbstractValue, DexType type) {
+ // TODO(b/196017578): Use a config that allows the definite bits abstraction for parameters
+ // used in bitwise operations.
+ AbstractValueJoinerConfig config = AbstractValueJoinerConfig.getDefaultConfig();
+ AbstractValue result = internalJoin(abstractValue, otherAbstractValue, config, type);
+ assert result.equals(internalJoin(otherAbstractValue, abstractValue, config, type));
+ return result;
+ }
+ }
+
+ private static class AbstractValueJoinerConfig {
+
+ // The power set lattice is an expensive abstraction, so use it with caution.
+ private static final AbstractValueJoinerConfig CLASS_ID_FIELD_CONFIG =
+ new AbstractValueJoinerConfig().setCanUseNumberIntervalAndNumberSetAbstraction();
+
+ private static final AbstractValueJoinerConfig DEFAULT_CONFIG = new AbstractValueJoinerConfig();
+
+ public static AbstractValueJoinerConfig getClassIdFieldConfig() {
+ return CLASS_ID_FIELD_CONFIG;
+ }
+
+ public static AbstractValueJoinerConfig getDefaultConfig() {
+ return DEFAULT_CONFIG;
+ }
+
+ private boolean canUseDefiniteBitsAbstraction;
+ private boolean canUseNumberIntervalAndNumberSetAbstraction;
+
+ boolean canUseDefiniteBitsAbstraction() {
+ return canUseDefiniteBitsAbstraction;
+ }
+
+ @SuppressWarnings("UnusedMethod")
+ AbstractValueJoinerConfig setCanUseDefiniteBitsAbstraction() {
+ canUseDefiniteBitsAbstraction = true;
+ return this;
+ }
+
+ boolean canUseNumberIntervalAndNumberSetAbstraction() {
+ return canUseNumberIntervalAndNumberSetAbstraction;
+ }
+
+ AbstractValueJoinerConfig setCanUseNumberIntervalAndNumberSetAbstraction() {
+ canUseNumberIntervalAndNumberSetAbstraction = true;
+ return this;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java b/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java
new file mode 100644
index 0000000..b713cef
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/value/DefiniteBitsNumberValue.java
@@ -0,0 +1,93 @@
+// Copyright (c) 2023, 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.value;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.lens.GraphLens;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.OptionalBool;
+import java.util.Objects;
+
+public class DefiniteBitsNumberValue extends NonConstantNumberValue {
+
+ private final int definitelySetBits;
+ private final int definitelyUnsetBits;
+
+ public DefiniteBitsNumberValue(int definitelySetBits, int definitelyUnsetBits) {
+ assert (definitelySetBits & definitelyUnsetBits) == 0;
+ this.definitelySetBits = definitelySetBits;
+ this.definitelyUnsetBits = definitelyUnsetBits;
+ }
+
+ @Override
+ public boolean containsInt(int value) {
+ return false;
+ }
+
+ @Override
+ public long getAbstractionSize() {
+ return Long.MAX_VALUE;
+ }
+
+ @Override
+ public boolean isDefiniteBitsNumberValue() {
+ return true;
+ }
+
+ @Override
+ public DefiniteBitsNumberValue asDefiniteBitsNumberValue() {
+ return this;
+ }
+
+ @Override
+ public boolean isNonTrivial() {
+ return true;
+ }
+
+ @Override
+ public OptionalBool isSubsetOf(int[] values) {
+ return OptionalBool.unknown();
+ }
+
+ @Override
+ public boolean mayOverlapWith(ConstantOrNonConstantNumberValue other) {
+ return true;
+ }
+
+ @Override
+ public AbstractValue rewrittenWithLens(
+ AppView<AppInfoWithLiveness> appView, GraphLens lens, GraphLens codeLens) {
+ return this;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || o.getClass() != getClass()) {
+ return false;
+ }
+ DefiniteBitsNumberValue definiteBitsNumberValue = (DefiniteBitsNumberValue) o;
+ return definitelySetBits == definiteBitsNumberValue.definitelySetBits
+ && definitelyUnsetBits == definiteBitsNumberValue.definitelyUnsetBits;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 31 * (31 * (31 + definitelySetBits) + definitelyUnsetBits);
+ assert hash == Objects.hash(definitelySetBits, definitelyUnsetBits);
+ return hash;
+ }
+
+ @Override
+ public String toString() {
+ return "DefiniteBitsNumberValue(set: "
+ + Integer.toBinaryString(definitelySetBits)
+ + "; unset: "
+ + Integer.toBinaryString(definitelyUnsetBits)
+ + ")";
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
index fab9f6f..43da39b 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteClassTypeParameterState.java
@@ -105,15 +105,11 @@
DexType parameterType,
Action onChangedAction) {
assert parameterType.isClassType();
- boolean allowNullOrAbstractValue = true;
- boolean allowNonConstantNumbers = false;
AbstractValue oldAbstractValue = abstractValue;
abstractValue =
- abstractValue.join(
- parameterState.getAbstractValue(appView),
- appView.abstractValueFactory(),
- allowNullOrAbstractValue,
- allowNonConstantNumbers);
+ appView
+ .getAbstractValueParameterJoiner()
+ .join(abstractValue, parameterState.getAbstractValue(appView), parameterType);
DynamicType oldDynamicType = dynamicType;
DynamicType joinedDynamicType = dynamicType.join(appView, parameterState.getDynamicType());
DynamicType widenedDynamicType =
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
index 429e514..3b3f3cb 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcretePrimitiveTypeParameterState.java
@@ -56,15 +56,11 @@
DexType parameterType,
Action onChangedAction) {
assert parameterType.isPrimitiveType();
- boolean allowNullOrAbstractValue = false;
- boolean allowNonConstantNumbers = false;
AbstractValue oldAbstractValue = abstractValue;
abstractValue =
- abstractValue.join(
- parameterState.abstractValue,
- appView.abstractValueFactory(),
- allowNullOrAbstractValue,
- allowNonConstantNumbers);
+ appView
+ .getAbstractValueParameterJoiner()
+ .join(abstractValue, parameterState.abstractValue, parameterType);
if (abstractValue.isUnknown()) {
return unknown();
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
index 2a044e5..bbc27a2 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMemberMap.java
@@ -5,8 +5,10 @@
package com.android.tools.r8.utils.collections;
import com.android.tools.r8.graph.DexClassAndMember;
+import com.android.tools.r8.utils.TriPredicate;
import com.google.common.base.Equivalence.Wrapper;
import java.util.Map;
+import java.util.Map.Entry;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
@@ -77,6 +79,12 @@
.removeIf(entry -> predicate.test(entry.getKey().get(), entry.getValue()));
}
+ public boolean removeIf(TriPredicate<K, V, Entry<?, V>> predicate) {
+ return backing
+ .entrySet()
+ .removeIf(entry -> predicate.test(entry.getKey().get(), entry.getValue(), entry));
+ }
+
public int size() {
return backing.size();
}