Remove old field optimizations
Bug: b/330674939
Change-Id: Icf2bd4807f0e1f17d2a7da64cd7b1ac4934acd2b
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 b9f074c..d87ade4 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
@@ -4,19 +4,14 @@
package com.android.tools.r8.ir.analysis.fieldaccess;
-import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
-
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.ProgramField;
import com.android.tools.r8.graph.bytecodemetadata.BytecodeMetadataProvider;
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.Instruction;
-import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.conversion.MethodProcessor;
-import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.InternalOptions;
@@ -25,7 +20,6 @@
public class FieldAccessAnalysis {
private final AppView<? extends AppInfoWithClassHierarchy> appView;
- private final FieldAssignmentTracker fieldAssignmentTracker;
private final FieldBitAccessAnalysis fieldBitAccessAnalysis;
private final FieldReadForInvokeReceiverAnalysis fieldReadForInvokeReceiverAnalysis;
private final FieldReadForWriteAnalysis fieldReadForWriteAnalysis;
@@ -35,8 +29,6 @@
this.appView = appView;
this.fieldBitAccessAnalysis =
options.enableFieldBitAccessAnalysis ? new FieldBitAccessAnalysis() : null;
- this.fieldAssignmentTracker =
- options.enableFieldAssignmentTracker ? new FieldAssignmentTracker(appView) : null;
this.fieldReadForInvokeReceiverAnalysis = new FieldReadForInvokeReceiverAnalysis(appView);
this.fieldReadForWriteAnalysis = new FieldReadForWriteAnalysis(appView);
}
@@ -44,28 +36,15 @@
@VisibleForTesting
public FieldAccessAnalysis(
AppView<? extends AppInfoWithClassHierarchy> appView,
- FieldAssignmentTracker fieldAssignmentTracker,
FieldBitAccessAnalysis fieldBitAccessAnalysis,
FieldReadForInvokeReceiverAnalysis fieldReadForInvokeReceiverAnalysis,
FieldReadForWriteAnalysis fieldReadForWriteAnalysis) {
this.appView = appView;
- this.fieldAssignmentTracker = fieldAssignmentTracker;
this.fieldBitAccessAnalysis = fieldBitAccessAnalysis;
this.fieldReadForInvokeReceiverAnalysis = fieldReadForInvokeReceiverAnalysis;
this.fieldReadForWriteAnalysis = fieldReadForWriteAnalysis;
}
- public FieldAssignmentTracker fieldAssignmentTracker() {
- return fieldAssignmentTracker;
- }
-
- public void acceptClassInitializerDefaultsResult(
- ClassInitializerDefaultsResult classInitializerDefaultsResult) {
- if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
- }
- }
-
public void recordFieldAccesses(
IRCode code,
BytecodeMetadataProvider.Builder bytecodeMetadataProviderBuilder,
@@ -85,9 +64,6 @@
ProgramField field =
appView.appInfo().resolveField(fieldInstruction.getField()).getProgramField();
if (field != null) {
- if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordFieldAccess(fieldInstruction, field);
- }
if (fieldBitAccessAnalysis != null) {
fieldBitAccessAnalysis.recordFieldAccess(
fieldInstruction, field.getDefinition(), feedback);
@@ -101,14 +77,6 @@
fieldInstruction, field, bytecodeMetadataProviderBuilder);
}
}
- } else if (instruction.isNewInstance()) {
- NewInstance newInstance = instruction.asNewInstance();
- DexProgramClass clazz = asProgramClassOrNull(appView.definitionFor(newInstance.clazz));
- if (clazz != null) {
- if (fieldAssignmentTracker != null) {
- fieldAssignmentTracker.recordAllocationSite(newInstance, clazz, code.context());
- }
- }
}
}
}
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
deleted file mode 100644
index af58cdc..0000000
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldAssignmentTracker.java
+++ /dev/null
@@ -1,531 +0,0 @@
-// Copyright (c) 2020, 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.fieldaccess;
-
-import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
-
-import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.DexClassAndMethod;
-import com.android.tools.r8.graph.DexEncodedField;
-import com.android.tools.r8.graph.DexEncodedMethod;
-import com.android.tools.r8.graph.DexItemFactory;
-import com.android.tools.r8.graph.DexProgramClass;
-import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.FieldAccessInfo;
-import com.android.tools.r8.graph.FieldAccessInfoCollection;
-import com.android.tools.r8.graph.ObjectAllocationInfoCollection;
-import com.android.tools.r8.graph.ProgramField;
-import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.analysis.type.ClassTypeElement;
-import com.android.tools.r8.ir.analysis.type.DynamicType;
-import com.android.tools.r8.ir.analysis.type.DynamicTypeWithUpperBound;
-import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.ir.analysis.value.AbstractValue;
-import com.android.tools.r8.ir.analysis.value.AbstractValueFactory;
-import com.android.tools.r8.ir.analysis.value.BottomValue;
-import com.android.tools.r8.ir.analysis.value.SingleValue;
-import com.android.tools.r8.ir.analysis.value.UnknownValue;
-import com.android.tools.r8.ir.code.FieldInstruction;
-import com.android.tools.r8.ir.code.InvokeDirect;
-import com.android.tools.r8.ir.code.NewInstance;
-import com.android.tools.r8.ir.code.Value;
-import com.android.tools.r8.ir.optimize.ClassInitializerDefaultsOptimization.ClassInitializerDefaultsResult;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
-import com.android.tools.r8.ir.optimize.info.field.InstanceFieldArgumentInitializationInfo;
-import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfo;
-import com.android.tools.r8.ir.optimize.info.field.InstanceFieldInitializationInfoCollection;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteArrayTypeValueState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeValueState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeValueState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ValueState;
-import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
-import com.android.tools.r8.shaking.AppInfoWithLiveness;
-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.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.function.Consumer;
-
-// TODO(b/330674939): Remove legacy field optimizations.
-public class FieldAssignmentTracker {
-
- private final AbstractValueFactory abstractValueFactory;
- private final AppView<AppInfoWithLiveness> appView;
- private final DexItemFactory dexItemFactory;
-
- // A field access graph with edges from methods to the fields that they access. Edges are removed
- // from the graph as we process methods, such that we can conclude that all field writes have been
- // processed when a field no longer has any incoming edges.
- private final FieldAccessGraph fieldAccessGraph;
-
- // An object allocation graph with edges from methods to the classes they instantiate. Edges are
- // removed from the graph as we process methods, such that we can conclude that all allocation
- // sites have been seen when a class no longer has any incoming edges.
- private final ObjectAllocationGraph objectAllocationGraph;
-
- // Information about the fields in the program. If a field is not a key in the map then no writes
- // has been seen to the field.
- private final Map<DexEncodedField, ValueState> fieldStates = new ConcurrentHashMap<>();
-
- private final Map<DexProgramClass, ProgramFieldMap<AbstractValue>>
- abstractFinalInstanceFieldValues = new ConcurrentHashMap<>();
-
- private final Set<DexProgramClass> classesWithPrunedInstanceInitializers =
- ConcurrentHashMap.newKeySet();
-
- FieldAssignmentTracker(AppView<AppInfoWithLiveness> appView) {
- this.abstractValueFactory = appView.abstractValueFactory();
- this.appView = appView;
- this.dexItemFactory = appView.dexItemFactory();
- this.fieldAccessGraph = new FieldAccessGraph();
- this.objectAllocationGraph = new ObjectAllocationGraph();
- }
-
- public void initialize() {
- fieldAccessGraph.initialize(appView);
- objectAllocationGraph.initialize(appView);
- initializeAbstractInstanceFieldValues();
- }
-
- /**
- * For each class with known allocation sites, adds a mapping from clazz -> instance field ->
- * bottom.
- *
- * <p>If an entry (clazz, instance field) is missing in {@link #abstractFinalInstanceFieldValues},
- * it is interpreted as if we known nothing about the value of the field.
- */
- private void initializeAbstractInstanceFieldValues() {
- FieldAccessInfoCollection<?> fieldAccessInfos =
- appView.appInfo().getFieldAccessInfoCollection();
- ObjectAllocationInfoCollection objectAllocationInfos =
- appView.appInfo().getObjectAllocationInfoCollection();
- objectAllocationInfos.forEachClassWithKnownAllocationSites(
- (clazz, allocationSites) -> {
- if (appView.appInfo().isInstantiatedIndirectly(clazz)) {
- // TODO(b/147652121): Handle classes that are instantiated indirectly.
- return;
- }
- List<DexEncodedField> instanceFields = clazz.instanceFields();
- if (instanceFields.isEmpty()) {
- // No instance fields to track.
- return;
- }
- 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, BottomValue.getInstance());
- }
- }
- });
- if (!abstractFinalInstanceFieldValuesForClass.isEmpty()) {
- abstractFinalInstanceFieldValues.put(clazz, abstractFinalInstanceFieldValuesForClass);
- }
- });
- for (DexProgramClass clazz : appView.appInfo().classes()) {
- clazz.forEachProgramField(
- field -> {
- FieldAccessInfo accessInfo = fieldAccessInfos.get(field.getReference());
- if (!appView.getKeepInfo(field).isValuePropagationAllowed(appView, field)
- || (accessInfo != null && accessInfo.isWrittenFromMethodHandle())) {
- fieldStates.put(field.getDefinition(), ValueState.unknown());
- }
- });
- }
- }
-
- @SuppressWarnings("ReferenceEquality")
- void acceptClassInitializerDefaultsResult(
- ClassInitializerDefaultsResult classInitializerDefaultsResult) {
- classInitializerDefaultsResult.forEachOptimizedField(
- (field, value) -> {
- DexType fieldType = field.getType();
- if (value.isDefault(field.getType())) {
- return;
- }
- assert fieldType.isClassType() || fieldType.isPrimitiveType();
- fieldStates.compute(
- field,
- (f, fieldState) -> {
- if (fieldState == null) {
- AbstractValue abstractValue = value.toAbstractValue(abstractValueFactory);
- if (fieldType.isClassType()) {
- assert abstractValue.isSingleStringValue()
- || abstractValue.isSingleDexItemBasedStringValue();
- if (fieldType == dexItemFactory.stringType) {
- return ConcreteClassTypeValueState.create(
- abstractValue, DynamicType.definitelyNotNull());
- } else {
- ClassTypeElement nonNullableStringType =
- dexItemFactory
- .stringType
- .toTypeElement(appView, definitelyNotNull())
- .asClassType();
- return ConcreteClassTypeValueState.create(
- abstractValue, DynamicType.createExact(nonNullableStringType));
- }
- } else {
- assert fieldType.isPrimitiveType();
- return ConcretePrimitiveTypeValueState.create(abstractValue);
- }
- }
- // If the field is already assigned outside the class initializer then just give up.
- return ValueState.unknown();
- });
- });
- }
-
- void recordFieldAccess(FieldInstruction instruction, ProgramField field) {
- if (instruction.isFieldPut()) {
- recordFieldPut(field, instruction.value());
- }
- }
-
- 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.
- AbstractValue abstractValue =
- value.isZero()
- ? abstractValueFactory.createDefaultValue(field.getType())
- : AbstractValue.unknown();
- Nullability nullability = value.getType().nullability();
- fieldStates.compute(
- field.getDefinition(),
- (f, fieldState) -> {
- if (fieldState == null || fieldState.isBottom()) {
- DexType fieldType = field.getType();
- if (fieldType.isArrayType()) {
- return ConcreteArrayTypeValueState.create(nullability);
- }
- if (fieldType.isPrimitiveType()) {
- return ConcretePrimitiveTypeValueState.create(abstractValue);
- }
- assert fieldType.isClassType();
- DynamicType dynamicType =
- WideningUtils.widenDynamicNonReceiverType(
- appView,
- value.getDynamicType(appView).withNullability(Nullability.maybeNull()),
- field.getType());
- return ConcreteClassTypeValueState.create(abstractValue, dynamicType);
- }
-
- if (fieldState.isUnknown()) {
- return fieldState;
- }
-
- assert fieldState.isConcrete();
-
- if (fieldState.isArrayState()) {
- ConcreteArrayTypeValueState arrayFieldState = fieldState.asArrayState();
- return arrayFieldState.mutableJoin(appView, field, nullability);
- }
-
- if (fieldState.isPrimitiveState()) {
- ConcretePrimitiveTypeValueState primitiveFieldState = fieldState.asPrimitiveState();
- return primitiveFieldState.mutableJoin(appView, field, abstractValue);
- }
-
- assert fieldState.isClassState();
-
- ConcreteClassTypeValueState classFieldState = fieldState.asClassState();
- DexType inStaticType = null;
- return classFieldState.mutableJoin(
- appView, abstractValue, value.getDynamicType(appView), inStaticType, field);
- });
- }
-
- void recordAllocationSite(NewInstance instruction, DexProgramClass clazz, ProgramMethod context) {
- ProgramFieldMap<AbstractValue> abstractInstanceFieldValuesForClass =
- abstractFinalInstanceFieldValues.get(clazz);
- if (abstractInstanceFieldValuesForClass == null) {
- // We are not tracking the value of any of clazz' instance fields.
- return;
- }
-
- InvokeDirect invoke = instruction.getUniqueConstructorInvoke(dexItemFactory);
- if (invoke == null) {
- // We just lost track.
- abstractFinalInstanceFieldValues.remove(clazz);
- return;
- }
-
- DexClassAndMethod singleTarget = invoke.lookupSingleTarget(appView, context);
- if (singleTarget == null) {
- // We just lost track.
- abstractFinalInstanceFieldValues.remove(clazz);
- return;
- }
-
- InstanceFieldInitializationInfoCollection initializationInfoCollection =
- singleTarget
- .getDefinition()
- .getOptimizationInfo()
- .getInstanceInitializerInfo(invoke)
- .fieldInitializationInfos();
-
- // Synchronize on the lattice element (abstractInstanceFieldValuesForClass) in case we process
- // another allocation site of `clazz` concurrently.
- synchronized (abstractInstanceFieldValuesForClass) {
- 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();
- }
-
- assert !abstractValue.isBottom();
- entry.setValue(abstractValue);
- return abstractValue.isUnknown();
- });
- }
- }
-
- private void recordAllFieldPutsProcessed(
- ProgramField field, OptimizationFeedbackDelayed feedback) {
- ValueState fieldState =
- fieldStates.getOrDefault(field.getDefinition(), ValueState.bottom(field));
- AbstractValue abstractValue =
- fieldState.isBottom()
- ? appView.abstractValueFactory().createDefaultValue(field.getType())
- : fieldState.getAbstractValue(appView);
- if (abstractValue.isNonTrivial()) {
- feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
- }
-
- if (fieldState.isClassState() && field.getOptimizationInfo().getDynamicType().isUnknown()) {
- ConcreteClassTypeValueState classFieldState = fieldState.asClassState();
- DynamicType dynamicType = classFieldState.getDynamicType();
- if (!dynamicType.isUnknown()) {
- assert WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, field.getType())
- .equals(dynamicType);
- if (dynamicType.isNotNullType()) {
- feedback.markFieldHasDynamicType(field, dynamicType);
- } else {
- DynamicTypeWithUpperBound staticType = field.getType().toDynamicType(appView);
- if (dynamicType.asDynamicTypeWithUpperBound().strictlyLessThan(staticType, appView)) {
- feedback.markFieldHasDynamicType(field, dynamicType);
- }
- }
- }
- }
-
- if (!field.getAccessFlags().isStatic()) {
- recordAllInstanceFieldPutsProcessed(field, feedback);
- }
- }
-
- private void recordAllInstanceFieldPutsProcessed(
- ProgramField field, OptimizationFeedbackDelayed feedback) {
- if (!appView.appInfo().isInstanceFieldWrittenOnlyInInstanceInitializers(field)) {
- return;
- }
- DexProgramClass clazz = field.getHolder();
- if (classesWithPrunedInstanceInitializers.contains(clazz)) {
- // The current method is analyzing the instance-puts to the field in the instance initializers
- // of the holder class. Due to single caller inlining of instance initializers some of the
- // methods needed for the analysis may have been pruned from the app, in which case the
- // analysis result will not be conservative.
- return;
- }
- AbstractValue abstractValue = BottomValue.getInstance();
- for (DexEncodedMethod method : clazz.directMethods(DexEncodedMethod::isInstanceInitializer)) {
- InstanceFieldInitializationInfo fieldInitializationInfo =
- method
- .getOptimizationInfo()
- .getContextInsensitiveInstanceInitializerInfo()
- .fieldInitializationInfos()
- .get(field);
- if (fieldInitializationInfo.isSingleValue()) {
- abstractValue =
- appView
- .getAbstractValueFieldJoiner()
- .join(abstractValue, fieldInitializationInfo.asSingleValue(), field);
- if (abstractValue.isUnknown()) {
- break;
- }
- } else if (fieldInitializationInfo.isTypeInitializationInfo()) {
- // TODO(b/149732532): Not handled, for now.
- abstractValue = UnknownValue.getInstance();
- break;
- } else {
- assert fieldInitializationInfo.isArgumentInitializationInfo()
- || fieldInitializationInfo.isUnknown();
- abstractValue = UnknownValue.getInstance();
- break;
- }
- }
-
- assert !abstractValue.isBottom();
-
- if (!abstractValue.isUnknown()) {
- feedback.recordFieldHasAbstractValue(field, appView, abstractValue);
- }
- }
-
- private void recordAllAllocationsSitesProcessed(
- DexProgramClass clazz, OptimizationFeedbackDelayed feedback) {
- ProgramFieldMap<AbstractValue> abstractInstanceFieldValuesForClass =
- abstractFinalInstanceFieldValues.get(clazz);
- if (abstractInstanceFieldValuesForClass == null) {
- return;
- }
-
- 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 onMethodPruned(ProgramMethod method) {
- onMethodCodePruned(method);
- }
-
- public void onMethodCodePruned(ProgramMethod method) {
- if (method.getDefinition().isInstanceInitializer()) {
- classesWithPrunedInstanceInitializers.add(method.getHolder());
- }
- }
-
- public void waveDone(ProgramMethodSet wave, OptimizationFeedbackDelayed feedback) {
- // This relies on the instance initializer info in the method optimization feedback. It is
- // therefore important that the optimization info has been flushed in advance.
- assert feedback.noUpdatesLeft();
- for (ProgramMethod method : wave) {
- fieldAccessGraph.markProcessed(method, field -> recordAllFieldPutsProcessed(field, feedback));
- objectAllocationGraph.markProcessed(
- method, clazz -> recordAllAllocationsSitesProcessed(clazz, feedback));
- }
- feedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness());
- feedback.updateVisibleOptimizationInfo();
- }
-
- static class FieldAccessGraph {
-
- // The fields written by each method.
- private final Map<DexEncodedMethod, List<ProgramField>> fieldWrites = new IdentityHashMap<>();
-
- // The number of writes that have not yet been processed per field.
- private final Reference2IntMap<DexEncodedField> pendingFieldWrites =
- new Reference2IntOpenHashMap<>();
-
- FieldAccessGraph() {}
-
- public void initialize(AppView<AppInfoWithLiveness> appView) {
- FieldAccessInfoCollection<?> fieldAccessInfoCollection =
- appView.appInfo().getFieldAccessInfoCollection();
- fieldAccessInfoCollection.forEach(
- info -> {
- ProgramField field =
- appView.appInfo().resolveField(info.getField()).getSingleProgramField();
- if (field == null) {
- return;
- }
- if (!info.hasReflectiveAccess() && !info.isWrittenFromMethodHandle()) {
- info.forEachWriteContext(
- context ->
- fieldWrites
- .computeIfAbsent(context.getDefinition(), ignore -> new ArrayList<>())
- .add(field));
- pendingFieldWrites.put(field.getDefinition(), info.getNumberOfWriteContexts());
- }
- });
- }
-
- void markProcessed(ProgramMethod method, Consumer<ProgramField> allWritesSeenConsumer) {
- List<ProgramField> fieldWritesInMethod = fieldWrites.get(method.getDefinition());
- if (fieldWritesInMethod != null) {
- for (ProgramField field : fieldWritesInMethod) {
- int numberOfPendingFieldWrites = pendingFieldWrites.removeInt(field.getDefinition()) - 1;
- if (numberOfPendingFieldWrites > 0) {
- pendingFieldWrites.put(field.getDefinition(), numberOfPendingFieldWrites);
- } else {
- allWritesSeenConsumer.accept(field);
- }
- }
- }
- }
- }
-
- static class ObjectAllocationGraph {
-
- // The classes instantiated by each method.
- private final Map<DexEncodedMethod, List<DexProgramClass>> objectAllocations =
- new IdentityHashMap<>();
-
- // The number of allocation sites that have not yet been processed per class.
- private final Reference2IntMap<DexProgramClass> pendingObjectAllocations =
- new Reference2IntOpenHashMap<>();
-
- ObjectAllocationGraph() {}
-
- public void initialize(AppView<AppInfoWithLiveness> appView) {
- ObjectAllocationInfoCollection objectAllocationInfos =
- appView.appInfo().getObjectAllocationInfoCollection();
- objectAllocationInfos.forEachClassWithKnownAllocationSites(
- (clazz, contexts) -> {
- for (DexEncodedMethod context : contexts) {
- objectAllocations.computeIfAbsent(context, ignore -> new ArrayList<>()).add(clazz);
- }
- pendingObjectAllocations.put(clazz, contexts.size());
- });
- }
-
- void markProcessed(
- ProgramMethod method, Consumer<DexProgramClass> allAllocationsSitesSeenConsumer) {
- List<DexProgramClass> allocationSitesInMethod = objectAllocations.get(method.getDefinition());
- if (allocationSitesInMethod != null) {
- for (DexProgramClass type : allocationSitesInMethod) {
- int numberOfPendingAllocationSites = pendingObjectAllocations.removeInt(type) - 1;
- if (numberOfPendingAllocationSites > 0) {
- pendingObjectAllocations.put(type, numberOfPendingAllocationSites);
- } else {
- allAllocationsSitesSeenConsumer.accept(type);
- }
- }
- }
- }
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
index 23bebb5..b18d828 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -135,7 +135,7 @@
private void computeFieldsWithNonTrivialValue() {
for (DexProgramClass clazz : appView.appInfo().classes()) {
- for (DexEncodedField field : clazz.instanceFields()) {
+ for (DexEncodedField field : clazz.fields()) {
FieldClassification fieldClassification = classifyField(field, appView);
switch (fieldClassification) {
case CONSTANT:
@@ -143,7 +143,7 @@
constantFields.add(field);
break;
case NON_CONSTANT:
- // Only reprocess writes, to allow branch pruning.
+ // Reprocess reads to allow branch pruning and value propagation.
nonConstantFields.add(field);
break;
default:
@@ -151,17 +151,6 @@
break;
}
}
- if (appView.canUseInitClass() || !clazz.classInitializationMayHaveSideEffects(appView)) {
- for (DexEncodedField field : clazz.staticFields()) {
- FieldClassification fieldClassification = classifyField(field, appView);
- if (fieldClassification == FieldClassification.CONSTANT) {
- constantFields.add(field);
- } else {
- assert fieldClassification == FieldClassification.NON_CONSTANT
- || fieldClassification == FieldClassification.UNKNOWN;
- }
- }
- }
}
assert verifyNoConstantFieldsOnSynthesizedClasses(appView);
}
@@ -203,7 +192,6 @@
});
}
- @SuppressWarnings("ReferenceEquality")
private static FieldClassification classifyField(
DexEncodedField field, AppView<AppInfoWithLiveness> appView) {
FieldAccessInfo fieldAccessInfo =
@@ -226,10 +214,13 @@
if (singleValue.isSingleFieldValue()) {
SingleFieldValue singleFieldValue = singleValue.asSingleFieldValue();
DexField singleField = singleFieldValue.getField();
- if (singleField != field.getReference()
+ if (singleField.isNotIdenticalTo(field.getReference())
&& !singleFieldValue.mayHaveFinalizeMethodDirectlyOrIndirectly(appView)) {
return FieldClassification.CONSTANT;
}
+ if (singleFieldValue.hasObjectState()) {
+ return FieldClassification.NON_CONSTANT;
+ }
}
return FieldClassification.UNKNOWN;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
index 971a56e..5f0acbe 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/DynamicType.java
@@ -206,6 +206,11 @@
@Override
public abstract int hashCode();
+ public DynamicType uncanonicalizeNotNullType(
+ AppView<AppInfoWithLiveness> appView, DexType staticType) {
+ return this;
+ }
+
private static boolean verifyNotEffectivelyFinalClassType(
AppView<AppInfoWithLiveness> appView, TypeElement type) {
if (type.isClassType()) {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java b/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java
index 7645341..d5f7fe1 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/NotNullDynamicType.java
@@ -75,4 +75,10 @@
public String toString() {
return "NotNullDynamicType";
}
+
+ @Override
+ public DynamicType uncanonicalizeNotNullType(
+ AppView<AppInfoWithLiveness> appView, DexType staticType) {
+ return DynamicType.create(appView, staticType.toNonNullTypeElement(appView));
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index d5da488..36d72fc 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -596,6 +596,16 @@
previous = printMethod(code, "IR after identifier-name strings (SSA)", previous);
}
+ timing.begin("Run proto shrinking tasks");
+ appView.withGeneratedExtensionRegistryShrinker(shrinker -> shrinker.rewriteCode(method, code));
+
+ previous = printMethod(code, "IR after generated extension registry shrinking (SSA)", previous);
+
+ appView.withGeneratedMessageLiteShrinker(shrinker -> shrinker.run(code));
+ timing.end();
+
+ previous = printMethod(code, "IR after generated message lite shrinking (SSA)", previous);
+
if (memberValuePropagation != null) {
timing.begin("Propagate member values");
memberValuePropagation.run(code);
@@ -632,16 +642,6 @@
previous = printMethod(code, "IR after inserting assume instructions (SSA)", previous);
- timing.begin("Run proto shrinking tasks");
- appView.withGeneratedExtensionRegistryShrinker(shrinker -> shrinker.rewriteCode(method, code));
-
- previous = printMethod(code, "IR after generated extension registry shrinking (SSA)", previous);
-
- appView.withGeneratedMessageLiteShrinker(shrinker -> shrinker.run(code));
- timing.end();
-
- previous = printMethod(code, "IR after generated message lite shrinking (SSA)", previous);
-
if (!isDebugMode && options.inlinerOptions().enableInlining && inliner != null) {
timing.begin("Inlining");
inliner.performInlining(code.context(), code, feedback, methodProcessor, timing);
@@ -899,9 +899,6 @@
timing.begin("Analyze field accesses");
fieldAccessAnalysis.recordFieldAccesses(
code, bytecodeMetadataProviderBuilder, feedback, methodProcessor);
- if (classInitializerDefaultsResult != null) {
- fieldAccessAnalysis.acceptClassInitializerDefaultsResult(classInitializerDefaultsResult);
- }
timing.end();
}
@@ -1092,7 +1089,6 @@
assert method.getHolder().lookupMethod(method.getReference()) == null;
appView.withArgumentPropagator(argumentPropagator -> argumentPropagator.onMethodPruned(method));
enumUnboxer.onMethodPruned(method);
- fieldAccessAnalysis.fieldAssignmentTracker().onMethodPruned(method);
numberUnboxer.onMethodPruned(method);
outliner.onMethodPruned(method);
if (inliner != null) {
@@ -1110,7 +1106,6 @@
appView.withArgumentPropagator(
argumentPropagator -> argumentPropagator.onMethodCodePruned(method));
enumUnboxer.onMethodCodePruned(method);
- fieldAccessAnalysis.fieldAssignmentTracker().onMethodCodePruned(method);
numberUnboxer.onMethodCodePruned(method);
outliner.onMethodCodePruned(method);
if (inliner != null) {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
index 035690f..9215753 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PrimaryR8IRConverter.java
@@ -73,10 +73,6 @@
numberUnboxer.prepareForPrimaryOptimizationPass(timing, executorService);
outliner.prepareForPrimaryOptimizationPass(graphLensForPrimaryOptimizationPass);
- if (fieldAccessAnalysis != null && fieldAccessAnalysis.fieldAssignmentTracker() != null) {
- fieldAccessAnalysis.fieldAssignmentTracker().initialize();
- }
-
// Process the application identifying outlining candidates.
OptimizationFeedbackDelayed feedback = delayedOptimizationFeedback;
PostMethodProcessor.Builder postMethodProcessorBuilder =
@@ -255,9 +251,6 @@
public void waveDone(ProgramMethodSet wave, ExecutorService executorService) {
delayedOptimizationFeedback.refineAppInfoWithLiveness(appView.appInfo().withLiveness());
delayedOptimizationFeedback.updateVisibleOptimizationInfo();
- if (fieldAccessAnalysis.fieldAssignmentTracker() != null) {
- fieldAccessAnalysis.fieldAssignmentTracker().waveDone(wave, delayedOptimizationFeedback);
- }
appView.withArgumentPropagator(ArgumentPropagator::publishDelayedReprocessingCriteria);
if (appView.options().protoShrinking().enableRemoveProtoEnumSwitchMap()) {
appView.protoShrinker().protoEnumSwitchMapRemover.updateVisibleStaticFieldValues();
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
index 23ae714..23517ba 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/RedundantFieldLoadAndStoreElimination.java
@@ -5,6 +5,7 @@
package com.android.tools.r8.ir.optimize;
import static com.android.tools.r8.graph.ProgramField.asProgramFieldOrNull;
+import static com.android.tools.r8.ir.optimize.info.OptimizationFeedback.getSimpleFeedback;
import static com.android.tools.r8.utils.MapUtils.ignoreKey;
import static com.android.tools.r8.utils.PredicateUtils.not;
@@ -686,6 +687,9 @@
if (replacement != null) {
if (isRedundantFieldLoadEliminationAllowed(field)) {
replacement.eliminateRedundantRead(it, instanceGet);
+ if (field.isProgramField()) {
+ getSimpleFeedback().markFieldAsPropagated(field.getDefinition());
+ }
}
return;
}
@@ -774,6 +778,9 @@
FieldValue replacement = activeState.getStaticFieldValue(field.getReference());
if (replacement != null) {
replacement.eliminateRedundantRead(instructionIterator, staticGet);
+ if (field.isProgramField()) {
+ getSimpleFeedback().markFieldAsPropagated(field.getDefinition());
+ }
return;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
index 65487d8..7661efc 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxerImpl.java
@@ -967,14 +967,14 @@
unboxedValues.put(field.getReference(), ordinalToUnboxedInt(ordinal));
ordinalToObjectState.put(ordinal, enumState);
if (isEnumWithSubtypes) {
- DynamicType dynamicType = field.getOptimizationInfo().getDynamicType();
- // If the dynamic type is a NotNull dynamic type, then de-canonicalize the dynamic
+ // If the dynamic type is a NotNull dynamic type, then uncanonicalize the dynamic
// type. If the static type is an effectively final class then this yields an
// exact dynamic type.
- if (dynamicType.isNotNullType()) {
- dynamicType =
- DynamicType.create(appView, field.getType().toNonNullTypeElement(appView));
- }
+ DynamicType dynamicType =
+ field
+ .getOptimizationInfo()
+ .getDynamicType()
+ .uncanonicalizeNotNullType(appView, field.getType());
if (dynamicType.isExactClassType()) {
valueTypes.put(ordinal, dynamicType.getExactClassType().getClassType());
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
index 9f3a782..0284378 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumValueOptimizer.java
@@ -151,7 +151,11 @@
// Since the value is a single field value, the type should be exact.
assert abstractValue.isSingleFieldValue();
- ClassTypeElement enumFieldType = optimizationInfo.getDynamicType().getExactClassType();
+ ClassTypeElement enumFieldType =
+ optimizationInfo
+ .getDynamicType()
+ .uncanonicalizeNotNullType(appView.withLiveness(), field.getType())
+ .getExactClassType();
if (enumFieldType == null) {
assert false : "Expected to have an exact dynamic type for enum instance";
continue;
diff --git a/src/main/java/com/android/tools/r8/utils/InternalOptions.java b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
index 163e2e1..60f2cbd 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -404,7 +404,6 @@
// Optimization-related flags. These should conform to -dontoptimize and disableAllOptimizations.
public boolean enableFieldBitAccessAnalysis =
System.getProperty("com.android.tools.r8.fieldBitAccessAnalysis") != null;
- public boolean enableFieldAssignmentTracker = true;
public boolean enableFieldValueAnalysis = true;
public boolean enableUnusedInterfaceRemoval = true;
public boolean enableDevirtualization = true;
diff --git a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
index 64e4c42..270116a 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/Proto2ShrinkingTest.java
@@ -80,7 +80,6 @@
.addProgramFiles(PROGRAM_FILES)
.addKeepMainRule("proto2.TestClass")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
- .addKeepRules(allGeneratedMessageLiteSubtypesAreInstantiatedRule())
// TODO(b/173340579): This rule should not be needed to allow shrinking of
// PartiallyUsed$Enum.
.addNoHorizontalClassMergingRule(PARTIALLY_USED + "$Enum$1")
@@ -407,7 +406,6 @@
.addKeepMainRule("proto2.TestClass")
.addKeepRuleFiles(PROTOBUF_LITE_PROGUARD_RULES)
.addKeepRules(findLiteExtensionByNumberInDuplicateCalledRule())
- .addKeepRules(allGeneratedMessageLiteSubtypesAreInstantiatedRule())
// TODO(b/173340579): This rule should not be needed to allow shrinking of
// PartiallyUsed$Enum.
.addNoHorizontalClassMergingRule(PARTIALLY_USED + "$Enum$1")
diff --git a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
index 9bd5a74..f0db90b 100644
--- a/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
+++ b/src/test/java/com/android/tools/r8/internal/proto/ProtoShrinkingTestBase.java
@@ -75,14 +75,6 @@
}
}
- static String allGeneratedMessageLiteSubtypesAreInstantiatedRule() {
- return StringUtils.lines(
- "-if class * extends com.google.protobuf.GeneratedMessageLite",
- "-keep,allowobfuscation class <1> {",
- " <init>(...);",
- "}");
- }
-
static String findLiteExtensionByNumberInDuplicateCalledRule() {
return StringUtils.lines(
"-keep class com.google.protobuf.proto2_registryGeneratedExtensionRegistryLiteDuplicate {",
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
index ea825ed..1c8a244 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/fieldaccess/FieldBitAccessInfoTest.java
@@ -87,7 +87,7 @@
OptimizationFeedbackMock feedback = new OptimizationFeedbackMock();
FieldBitAccessAnalysis fieldBitAccessAnalysis = new FieldBitAccessAnalysis();
FieldAccessAnalysis fieldAccessAnalysis =
- new FieldAccessAnalysis(appView, null, fieldBitAccessAnalysis, null, null);
+ new FieldAccessAnalysis(appView, fieldBitAccessAnalysis, null, null);
DexProgramClass clazz = appView.appInfo().classes().iterator().next();
assertEquals(TestClass.class.getTypeName(), clazz.type.toSourceString());
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
index a603d83..16060c2 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/FieldWriteBeforeFieldReadTest.java
@@ -4,14 +4,13 @@
package com.android.tools.r8.ir.optimize.membervaluepropagation;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
-import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
-import com.android.tools.r8.NeverReprocessMethod;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -24,49 +23,47 @@
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
public class FieldWriteBeforeFieldReadTest extends TestBase {
- private final TestParameters parameters;
+ @Parameter(0)
+ public TestParameters parameters;
@Parameters(name = "{0}")
public static TestParametersCollection data() {
return getTestParameters().withAllRuntimesAndApiLevels().build();
}
- public FieldWriteBeforeFieldReadTest(TestParameters parameters) {
- this.parameters = parameters;
- }
-
@Test
public void test() throws Exception {
testForR8(parameters.getBackend())
.addInnerClasses(FieldWriteBeforeFieldReadTest.class)
.addKeepMainRule(TestClass.class)
.addOptionsModification(
- options -> {
- options.testing.waveModifier =
- (waves) -> {
- Function<String, Predicate<ProgramMethodSet>> wavePredicate =
- methodName ->
- wave ->
- wave.stream()
- .anyMatch(
- method -> method.toSourceString().contains(methodName));
- int readFieldsWaveIndex =
- IterableUtils.firstIndexMatching(waves, wavePredicate.apply("readFields"));
- assertTrue(readFieldsWaveIndex >= 0);
- int writeFieldsWaveIndex =
- IterableUtils.firstIndexMatching(waves, wavePredicate.apply("writeFields"));
- assertTrue(writeFieldsWaveIndex >= 0);
- assertTrue(writeFieldsWaveIndex < readFieldsWaveIndex);
- };
- })
+ options ->
+ options.testing.waveModifier =
+ (waves) -> {
+ Function<String, Predicate<ProgramMethodSet>> wavePredicate =
+ methodName ->
+ wave ->
+ wave.stream()
+ .anyMatch(
+ method -> method.toSourceString().contains(methodName));
+ int readFieldsWaveIndex =
+ IterableUtils.firstIndexMatching(
+ waves, wavePredicate.apply("readFields"));
+ assertTrue(readFieldsWaveIndex >= 0);
+ int writeFieldsWaveIndex =
+ IterableUtils.firstIndexMatching(
+ waves, wavePredicate.apply("writeFields"));
+ assertTrue(writeFieldsWaveIndex >= 0);
+ assertTrue(writeFieldsWaveIndex < readFieldsWaveIndex);
+ })
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
- .enableNeverReprocessMethodAnnotations()
.setMinApi(parameters)
.compile()
.inspect(this::inspect)
@@ -78,7 +75,7 @@
ClassSubject testClassSubject = inspector.clazz(TestClass.class);
assertThat(testClassSubject, isPresent());
assertThat(testClassSubject.uniqueMethodWithOriginalName("live"), isPresent());
- assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), not(isPresent()));
+ assertThat(testClassSubject.uniqueMethodWithOriginalName("dead"), isAbsent());
}
static class TestClass {
@@ -100,8 +97,10 @@
static void increaseDistanceToNearestLeaf() {}
+ // By processing writeFields() before readFields() and publishing the fact that alwaysFalse is
+ // the constant false after processing writeFields(), it would be possible to optimize
+ // readFields() in just a single optimization pass.
@NeverInline
- @NeverReprocessMethod
static void readFields(A obj) {
if (alwaysFalse || obj.alwaysFalse) {
dead();
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
index 9fdff7c..2f716b7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/InstanceFieldValuePropagationTest.java
@@ -6,6 +6,8 @@
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
@@ -74,10 +76,12 @@
MethodSubject testMaybeNullMethodSubject =
testClassSubject.uniqueMethodWithOriginalName("testMaybeNull");
assertThat(testMaybeNullMethodSubject, isPresent());
- assertTrue(
+ // TODO(b/330674939): Should not have any instance-gets.
+ assertEquals(
+ parameters.canInitNewInstanceUsingSuperclassConstructor(),
testMaybeNullMethodSubject
.streamInstructions()
- .noneMatch(InstructionSubject::isInstanceGet));
+ .anyMatch(InstructionSubject::isInstanceGet));
// TODO(b/125282093): Should be able to remove the new-instance instruction since the instance
// ends up being unused.
assertTrue(
@@ -87,7 +91,10 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- assertTrue(aClassSubject.allInstanceFields().isEmpty());
+ // TODO(b/330674939): Should never have any instance fields.
+ assertNotEquals(
+ parameters.canInitNewInstanceUsingSuperclassConstructor(),
+ aClassSubject.allInstanceFields().isEmpty());
}
static class TestClass {
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java
index 6272631..b79e37a 100644
--- a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/FieldStateArgumentPropagationTest.java
@@ -37,7 +37,6 @@
.addKeepMainRule(Main.class)
.addOptionsModification(
options -> {
- options.enableFieldAssignmentTracker = false;
options.enableFieldValueAnalysis = false;
})
.enableInliningAnnotations()