Reprocess trivial field accesses
This CL enqueues all methods that access a constant field for reprocessing with the purpose of:
- removing all field writes to constant fields and
- replacing all constant field reads by the constant.
Change-Id: Ib53b32252ebb91e368b4a6963c2c7f84f1023c7f
Bug: 150349055, 125282093, 147799448
diff --git a/src/main/java/com/android/tools/r8/R8.java b/src/main/java/com/android/tools/r8/R8.java
index 96eb447..c036f5f 100644
--- a/src/main/java/com/android/tools/r8/R8.java
+++ b/src/main/java/com/android/tools/r8/R8.java
@@ -646,6 +646,7 @@
TreePruner pruner = new TreePruner(appViewWithLiveness, treePrunerConfiguration);
application = pruner.run(application);
+
if (options.usageInformationConsumer != null) {
ExceptionUtils.withFinishedResourceHandler(
options.reporter, options.usageInformationConsumer);
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfo.java b/src/main/java/com/android/tools/r8/graph/AppInfo.java
index a791cfc..640b7a1 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfo.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfo.java
@@ -98,7 +98,7 @@
assert previous == null || previous == clazz;
}
- public Collection<DexProgramClass> getSynthesizedClassesForSanityCheck() {
+ public Collection<DexProgramClass> synthesizedClasses() {
assert checkIfObsolete();
return Collections.unmodifiableCollection(synthesizedClasses.values());
}
diff --git a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
index 6b507da..d1e70ed 100644
--- a/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
+++ b/src/main/java/com/android/tools/r8/graph/AppInfoWithSubtyping.java
@@ -481,9 +481,8 @@
}
public boolean mayHaveFinalizeMethodDirectlyOrIndirectly(ClassTypeLatticeElement type) {
- Set<DexType> interfaces = type.getInterfaces();
- if (!interfaces.isEmpty()) {
- for (DexType interfaceType : interfaces) {
+ if (type.getClassType() == dexItemFactory().objectType && !type.getInterfaces().isEmpty()) {
+ for (DexType interfaceType : type.getInterfaces()) {
if (computeMayHaveFinalizeMethodDirectlyOrIndirectlyIfAbsent(interfaceType, false)) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
index ddab792..9d19406 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollection.java
@@ -12,6 +12,8 @@
void flattenAccessContexts();
+ boolean contains(DexField field);
+
T get(DexField field);
void forEach(Consumer<T> consumer);
diff --git a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
index 7ef10ac..5634d5b 100644
--- a/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
+++ b/src/main/java/com/android/tools/r8/graph/FieldAccessInfoCollectionImpl.java
@@ -21,6 +21,11 @@
}
@Override
+ public boolean contains(DexField field) {
+ return infos.containsKey(field);
+ }
+
+ @Override
public FieldAccessInfoImpl get(DexField field) {
return infos.get(field);
}
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 ef36b5b..d5bd10a 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
@@ -10,6 +10,7 @@
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
import com.android.tools.r8.graph.DexProgramClass;
+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.ir.analysis.value.AbstractValue;
@@ -75,6 +76,8 @@
* 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(
@@ -91,7 +94,10 @@
Map<DexEncodedField, AbstractValue> abstractInstanceFieldValuesForClass =
new IdentityHashMap<>();
for (DexEncodedField field : clazz.instanceFields()) {
- abstractInstanceFieldValuesForClass.put(field, BottomValue.getInstance());
+ FieldAccessInfo fieldAccessInfo = fieldAccessInfos.get(field.field);
+ if (fieldAccessInfo != null && !fieldAccessInfo.hasReflectiveAccess()) {
+ abstractInstanceFieldValuesForClass.put(field, BottomValue.getInstance());
+ }
}
abstractInstanceFieldValues.put(clazz, abstractInstanceFieldValuesForClass);
});
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
new file mode 100644
index 0000000..31ead7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/analysis/fieldaccess/TrivialFieldAccessReprocessor.java
@@ -0,0 +1,242 @@
+// 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.maybeNull;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexEncodedField;
+import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.DexMethod;
+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.UseRegistry;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
+import com.android.tools.r8.ir.analysis.value.SingleFieldValue;
+import com.android.tools.r8.ir.analysis.value.SingleValue;
+import com.android.tools.r8.ir.conversion.PostMethodProcessor;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
+import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.ThreadUtils;
+import com.android.tools.r8.utils.Timing;
+import com.google.common.collect.Sets;
+import java.util.Set;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+
+public class TrivialFieldAccessReprocessor {
+
+ private final AppView<AppInfoWithLiveness> appView;
+ private final PostMethodProcessor.Builder postMethodProcessorBuilder;
+
+ /** Updated concurrently from {@link #processClass(DexProgramClass)}. */
+ private final Set<DexEncodedField> fieldsOfInterest = Sets.newConcurrentHashSet();
+
+ /** Updated concurrently from {@link #processClass(DexProgramClass)}. */
+ private final Set<DexEncodedMethod> methodsToReprocess = Sets.newConcurrentHashSet();
+
+ public TrivialFieldAccessReprocessor(
+ AppView<AppInfoWithLiveness> appView,
+ PostMethodProcessor.Builder postMethodProcessorBuilder) {
+ this.appView = appView;
+ this.postMethodProcessorBuilder = postMethodProcessorBuilder;
+ }
+
+ public void run(
+ ExecutorService executorService, OptimizationFeedbackDelayed feedback, Timing timing)
+ throws ExecutionException {
+ AppInfoWithLiveness appInfo = appView.appInfo();
+
+ timing.begin("Trivial field accesses analysis");
+ assert feedback.noUpdatesLeft();
+
+ timing.begin("Compute fields of interest");
+ computeFieldsOfInterest(appInfo);
+ timing.end(); // Compute fields of interest
+
+ if (fieldsOfInterest.isEmpty()) {
+ timing.end(); // Trivial field accesses analysis
+ return;
+ }
+
+ timing.begin("Clear reads from fields of interest");
+ clearReadsFromFieldsOfInterest(appInfo);
+ timing.end(); // Clear reads from fields of interest
+
+ timing.begin("Enqueue methods for reprocessing");
+ enqueueMethodsForReprocessing(appInfo, executorService);
+ timing.end(); // Enqueue methods for reprocessing
+ timing.end(); // Trivial field accesses analysis
+
+ fieldsOfInterest.forEach(OptimizationFeedbackSimple.getInstance()::markFieldAsDead);
+ }
+
+ private void computeFieldsOfInterest(AppInfoWithLiveness appInfo) {
+ for (DexProgramClass clazz : appInfo.classes()) {
+ Iterable<DexEncodedField> fields =
+ clazz.classInitializationMayHaveSideEffects(appView)
+ ? clazz.instanceFields()
+ : clazz.fields();
+ for (DexEncodedField field : fields) {
+ if (canOptimizeField(field, appView) && appInfo.mayPropagateValueFor(field.field)) {
+ fieldsOfInterest.add(field);
+ }
+ }
+ }
+ assert verifyNoConstantFieldsOnSynthesizedClasses(appView);
+ }
+
+ private void clearReadsFromFieldsOfInterest(AppInfoWithLiveness appInfo) {
+ FieldAccessInfoCollection<?> fieldAccessInfoCollection = appInfo.getFieldAccessInfoCollection();
+ for (DexEncodedField field : fieldsOfInterest) {
+ fieldAccessInfoCollection.get(field.field).asMutable().clearReads();
+ }
+ }
+
+ private void enqueueMethodsForReprocessing(
+ AppInfoWithLiveness appInfo, ExecutorService executorService) throws ExecutionException {
+ ThreadUtils.processItems(appInfo.classes(), this::processClass, executorService);
+ ThreadUtils.processItems(appInfo.synthesizedClasses(), this::processClass, executorService);
+ postMethodProcessorBuilder.put(methodsToReprocess);
+ }
+
+ private void processClass(DexProgramClass clazz) {
+ for (DexEncodedMethod method : clazz.methods()) {
+ if (method.hasCode()) {
+ method.getCode().registerCodeReferences(method, new TrivialFieldAccessUseRegistry(method));
+ }
+ }
+ }
+
+ private static boolean canOptimizeField(
+ DexEncodedField field, AppView<AppInfoWithLiveness> appView) {
+ FieldAccessInfo fieldAccessInfo =
+ appView.appInfo().getFieldAccessInfoCollection().get(field.field);
+ if (fieldAccessInfo == null || fieldAccessInfo.isAccessedFromMethodHandle()) {
+ return false;
+ }
+ AbstractValue abstractValue = field.getOptimizationInfo().getAbstractValue();
+ if (abstractValue.isSingleValue()) {
+ SingleValue singleValue = abstractValue.asSingleValue();
+ if (!singleValue.isMaterializableInAllContexts(appView)) {
+ return false;
+ }
+ if (singleValue.isSingleConstValue()) {
+ return true;
+ }
+ if (singleValue.isSingleFieldValue()) {
+ SingleFieldValue singleFieldValue = singleValue.asSingleFieldValue();
+ DexField singleField = singleFieldValue.getField();
+ if (singleField == field.field) {
+ return false;
+ }
+ if (singleField.type.isClassType()) {
+ ClassTypeLatticeElement fieldType =
+ TypeLatticeElement.fromDexType(singleFieldValue.getField().type, maybeNull(), appView)
+ .asClassTypeLatticeElement();
+ return !appView.appInfo().mayHaveFinalizeMethodDirectlyOrIndirectly(fieldType);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static boolean verifyNoConstantFieldsOnSynthesizedClasses(
+ AppView<AppInfoWithLiveness> appView) {
+ for (DexProgramClass clazz : appView.appInfo().synthesizedClasses()) {
+ for (DexEncodedField field : clazz.fields()) {
+ assert field.getOptimizationInfo().getAbstractValue().isUnknown();
+ }
+ }
+ return true;
+ }
+
+ class TrivialFieldAccessUseRegistry extends UseRegistry {
+
+ private final DexEncodedMethod method;
+
+ TrivialFieldAccessUseRegistry(DexEncodedMethod method) {
+ super(appView.dexItemFactory());
+ this.method = method;
+ }
+
+ private boolean registerFieldAccess(DexField field, boolean isStatic) {
+ DexEncodedField encodedField = appView.appInfo().resolveField(field);
+ if (encodedField != null) {
+ if (encodedField.isStatic() == isStatic) {
+ if (fieldsOfInterest.contains(encodedField)) {
+ methodsToReprocess.add(method);
+ }
+ } else {
+ // Should generally not happen.
+ fieldsOfInterest.remove(encodedField);
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public boolean registerInstanceFieldWrite(DexField field) {
+ return registerFieldAccess(field, false);
+ }
+
+ @Override
+ public boolean registerInstanceFieldRead(DexField field) {
+ return registerFieldAccess(field, false);
+ }
+
+ @Override
+ public boolean registerStaticFieldRead(DexField field) {
+ return registerFieldAccess(field, true);
+ }
+
+ @Override
+ public boolean registerStaticFieldWrite(DexField field) {
+ return registerFieldAccess(field, true);
+ }
+
+ @Override
+ public boolean registerInvokeVirtual(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeDirect(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeStatic(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeInterface(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerInvokeSuper(DexMethod method) {
+ return false;
+ }
+
+ @Override
+ public boolean registerNewInstance(DexType type) {
+ return false;
+ }
+
+ @Override
+ public boolean registerTypeReference(DexType type) {
+ return false;
+ }
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
index a8e99cf..d00ff3c 100644
--- a/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/FieldInstruction.java
@@ -192,34 +192,38 @@
* default finalize() method in a field. In that case, it is not safe to remove this instruction,
* since that could change the lifetime of the value.
*/
- boolean isStoringObjectWithFinalizer(AppInfoWithLiveness appInfo) {
+ boolean isStoringObjectWithFinalizer(AppInfoWithLiveness appInfo, DexEncodedField field) {
assert isFieldPut();
+
TypeLatticeElement type = value().getTypeLattice();
TypeLatticeElement baseType =
type.isArrayType() ? type.asArrayTypeLatticeElement().getArrayBaseTypeLattice() : type;
- if (baseType.isClassType()) {
- Value root = value().getAliasedValue();
- if (!root.isPhi() && root.definition.isNewInstance()) {
- DexClass clazz = appInfo.definitionFor(root.definition.asNewInstance().clazz);
- if (clazz == null) {
- return true;
- }
- if (clazz.superType == null) {
- return false;
- }
- DexItemFactory dexItemFactory = appInfo.dexItemFactory();
- DexEncodedMethod resolutionResult =
- appInfo
- .resolveMethod(clazz.type, dexItemFactory.objectMembers.finalize)
- .getSingleTarget();
- return resolutionResult != null && resolutionResult.isProgramMethod(appInfo);
- }
-
- return appInfo.mayHaveFinalizeMethodDirectlyOrIndirectly(
- baseType.asClassTypeLatticeElement());
+ if (!baseType.isClassType()) {
+ return false;
}
- return false;
+ if (field.getOptimizationInfo().getAbstractValue().isZero()) {
+ return false;
+ }
+
+ Value root = value().getAliasedValue();
+ if (!root.isPhi() && root.definition.isNewInstance()) {
+ DexClass clazz = appInfo.definitionFor(root.definition.asNewInstance().clazz);
+ if (clazz == null) {
+ return true;
+ }
+ if (clazz.superType == null) {
+ return false;
+ }
+ DexItemFactory dexItemFactory = appInfo.dexItemFactory();
+ DexEncodedMethod resolutionResult =
+ appInfo
+ .resolveMethod(clazz.type, dexItemFactory.objectMembers.finalize)
+ .getSingleTarget();
+ return resolutionResult != null && resolutionResult.isProgramMethod(appInfo);
+ }
+
+ return appInfo.mayHaveFinalizeMethodDirectlyOrIndirectly(baseType.asClassTypeLatticeElement());
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
new file mode 100644
index 0000000..94bfdaf
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceFieldInstruction.java
@@ -0,0 +1,30 @@
+// 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.code;
+
+import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.ir.code.FieldInstruction.Assumption;
+
+public interface InstanceFieldInstruction {
+
+ boolean hasOutValue();
+
+ Value outValue();
+
+ Value object();
+
+ boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context, Assumption assumption);
+
+ FieldInstruction asFieldInstruction();
+
+ boolean isInstanceGet();
+
+ InstanceGet asInstanceGet();
+
+ boolean isInstancePut();
+
+ InstancePut asInstancePut();
+}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
index 2ca1b2a..2a7f621 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstanceGet.java
@@ -31,7 +31,7 @@
import com.android.tools.r8.ir.optimize.InliningConstraints;
import java.util.Set;
-public class InstanceGet extends FieldInstruction {
+public class InstanceGet extends FieldInstruction implements InstanceFieldInstruction {
public InstanceGet(Value dest, Value object, DexField field) {
super(field, dest, object);
@@ -56,6 +56,7 @@
return outValue;
}
+ @Override
public Value object() {
assert inValues.size() == 1;
return inValues.get(0);
@@ -119,6 +120,7 @@
return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
}
+ @Override
public boolean instructionMayHaveSideEffects(
AppView<?> appView, DexType context, Assumption assumption) {
return instructionInstanceCanThrow(appView, context, assumption).isThrowing();
diff --git a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
index cd71750..12f3d93 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InstancePut.java
@@ -31,7 +31,7 @@
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import java.util.Arrays;
-public class InstancePut extends FieldInstruction {
+public class InstancePut extends FieldInstruction implements InstanceFieldInstruction {
public InstancePut(DexField field, Value object, Value value) {
this(field, object, value, false);
@@ -63,6 +63,7 @@
return visitor.visit(this);
}
+ @Override
public Value object() {
return inValues.get(0);
}
@@ -115,17 +116,23 @@
@Override
public boolean instructionMayHaveSideEffects(AppView<?> appView, DexType context) {
+ return instructionMayHaveSideEffects(appView, context, Assumption.NONE);
+ }
+
+ @Override
+ public boolean instructionMayHaveSideEffects(
+ AppView<?> appView, DexType context, Assumption assumption) {
if (appView.appInfo().hasLiveness()) {
AppInfoWithLiveness appInfoWithLiveness = appView.appInfo().withLiveness();
- if (instructionInstanceCanThrow(appView, context).isThrowing()) {
+ if (instructionInstanceCanThrow(appView, context, assumption).isThrowing()) {
return true;
}
DexEncodedField encodedField = appInfoWithLiveness.resolveField(getField());
assert encodedField != null : "NoSuchFieldError (resolution failure) should be caught.";
return appInfoWithLiveness.isFieldRead(encodedField)
- || isStoringObjectWithFinalizer(appInfoWithLiveness);
+ || isStoringObjectWithFinalizer(appInfoWithLiveness, encodedField);
}
// In D8, we always have to assume that the field can be read, and thus have side effects.
diff --git a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
index c9bab8b..ff1dd42 100644
--- a/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
+++ b/src/main/java/com/android/tools/r8/ir/code/StaticPut.java
@@ -122,7 +122,7 @@
}
return appInfoWithLiveness.isFieldRead(encodedField)
- || isStoringObjectWithFinalizer(appInfoWithLiveness);
+ || isStoringObjectWithFinalizer(appInfoWithLiveness, encodedField);
}
// In D8, we always have to assume that the field can be read, and thus have side effects.
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 038bc33..5b3e1ed 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
@@ -27,6 +27,7 @@
import com.android.tools.r8.ir.analysis.TypeChecker;
import com.android.tools.r8.ir.analysis.constant.SparseConditionalConstantPropagation;
import com.android.tools.r8.ir.analysis.fieldaccess.FieldAccessAnalysis;
+import com.android.tools.r8.ir.analysis.fieldaccess.TrivialFieldAccessReprocessor;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.InstanceFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.fieldvalueanalysis.StaticFieldValueAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
@@ -702,6 +703,9 @@
enumUnboxer.finishAnalysis();
enumUnboxer.unboxEnums(postMethodProcessorBuilder, executorService, feedback);
}
+ new TrivialFieldAccessReprocessor(appView.withLiveness(), postMethodProcessorBuilder)
+ .run(executorService, feedback, timing);
+
timing.begin("IR conversion phase 2");
graphLenseForIR = appView.graphLense();
PostMethodProcessor postMethodProcessor =
@@ -825,13 +829,8 @@
// Check if what we've added to the application builder as synthesized classes are same as
// what we've added and used through AppInfo.
- assert appView
- .appInfo()
- .getSynthesizedClassesForSanityCheck()
- .containsAll(builder.getSynthesizedClasses())
- && builder
- .getSynthesizedClasses()
- .containsAll(appView.appInfo().getSynthesizedClassesForSanityCheck());
+ assert appView.appInfo().synthesizedClasses().containsAll(builder.getSynthesizedClasses())
+ && builder.getSynthesizedClasses().containsAll(appView.appInfo().synthesizedClasses());
return builder.build();
}
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
index 6052427..e742528 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/PostMethodProcessor.java
@@ -83,7 +83,7 @@
}
}
- void put(Set<DexEncodedMethod> methodsToRevisit) {
+ public void put(Set<DexEncodedMethod> methodsToRevisit) {
put(methodsToRevisit, defaultCodeOptimizations);
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
index 48b8637..16619d2 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/MemberValuePropagation.java
@@ -20,12 +20,14 @@
import com.android.tools.r8.ir.code.FieldInstruction;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRMetadata;
-import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstanceFieldInstruction;
+import com.android.tools.r8.ir.code.InstancePut;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
import com.android.tools.r8.ir.code.InvokeMethod;
import com.android.tools.r8.ir.code.InvokeStatic;
import com.android.tools.r8.ir.code.InvokeVirtual;
+import com.android.tools.r8.ir.code.Position;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
@@ -342,22 +344,22 @@
affectedValues.addAll(current.outValue().affectedValues());
DexType context = code.method.method.holder;
if (current.instructionMayHaveSideEffects(appView, context)) {
+ BasicBlock block = current.getBlock();
+ Position position = current.getPosition();
+
// All usages are replaced by the replacement value.
current.outValue().replaceUsers(replacement.outValue());
// To preserve side effects, original field-get is replaced by an explicit null-check, if
// the field-get instruction may only fail with an NPE, or the field-get remains as-is.
- Instruction currentOrNullCheck;
if (current.isInstanceGet()) {
- currentOrNullCheck =
- replaceInstanceGetByNullCheckIfPossible(current.asInstanceGet(), iterator, context);
- } else {
- currentOrNullCheck = current;
+ replaceInstanceFieldInstructionByNullCheckIfPossible(
+ current.asInstanceGet(), iterator, context);
}
// Insert the definition of the replacement.
- replacement.setPosition(currentOrNullCheck.getPosition());
- if (currentOrNullCheck.getBlock().hasCatchHandlers()) {
+ replacement.setPosition(position);
+ if (block.hasCatchHandlers()) {
iterator.split(code, blocks).listIterator(code).add(replacement);
} else {
iterator.add(replacement);
@@ -369,14 +371,18 @@
}
}
- private Instruction replaceInstanceGetByNullCheckIfPossible(
- InstanceGet instruction, InstructionListIterator iterator, DexType context) {
- assert !instruction.outValue().hasAnyUsers();
+ private void replaceInstanceFieldInstructionByNullCheckIfPossible(
+ InstanceFieldInstruction instruction, InstructionListIterator iterator, DexType context) {
+ assert !instruction.hasOutValue() || !instruction.outValue().hasAnyUsers();
if (instruction.instructionMayHaveSideEffects(
appView, context, FieldInstruction.Assumption.RECEIVER_NOT_NULL)) {
- return instruction;
+ return;
}
Value receiver = instruction.object();
+ if (receiver.isNeverNull()) {
+ iterator.removeOrReplaceByDebugLocalRead();
+ return;
+ }
InvokeMethod replacement;
if (appView.options().canUseRequireNonNull()) {
DexMethod requireNonNullMethod = appView.dexItemFactory().objectsMethods.requireNonNull;
@@ -386,7 +392,20 @@
replacement = new InvokeVirtual(getClassMethod, null, ImmutableList.of(receiver));
}
iterator.replaceCurrentInstruction(replacement);
- return replacement;
+ }
+
+ private void replaceInstancePutByNullCheckIfNeverRead(
+ IRCode code, InstructionListIterator iterator, InstancePut current) {
+ DexEncodedField target = appView.appInfo().resolveField(current.getField());
+ if (target == null || appView.appInfo().isFieldRead(target)) {
+ return;
+ }
+
+ if (target.isStatic() != current.isStaticGet()) {
+ return;
+ }
+
+ replaceInstanceFieldInstructionByNullCheckIfPossible(current, iterator, code.method.holder());
}
/**
@@ -396,7 +415,7 @@
*/
public void rewriteWithConstantValues(IRCode code, DexType callingContext) {
IRMetadata metadata = code.metadata();
- if (!metadata.mayHaveFieldGet() && !metadata.mayHaveInvokeMethod()) {
+ if (!metadata.mayHaveFieldInstruction() && !metadata.mayHaveInvokeMethod()) {
return;
}
@@ -413,6 +432,8 @@
} else if (current.isFieldGet()) {
rewriteFieldGetWithConstantValues(
code, affectedValues, blocks, iterator, current.asFieldInstruction());
+ } else if (current.isInstancePut()) {
+ replaceInstancePutByNullCheckIfNeverRead(code, iterator, current.asInstancePut());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
index e33b617..81d573b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackDelayed.java
@@ -144,6 +144,8 @@
@Override
public void recordFieldHasAbstractValue(
DexEncodedField field, AppView<AppInfoWithLiveness> appView, AbstractValue abstractValue) {
+ assert appView.appInfo().getFieldAccessInfoCollection().contains(field.field);
+ assert !appView.appInfo().getFieldAccessInfoCollection().get(field.field).hasReflectiveAccess();
if (appView.appInfo().mayPropagateValueFor(field.field)) {
getFieldOptimizationInfoForUpdating(field).setAbstractValue(abstractValue);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
index 5cbfa16..61ed827 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -853,7 +853,9 @@
public boolean mayPropagateValueFor(DexReference reference) {
assert checkIfObsolete();
- return !isPinned(reference) && !neverPropagateValue.contains(reference);
+ return options().enableValuePropagation
+ && !isPinned(reference)
+ && !neverPropagateValue.contains(reference);
}
private boolean isLibraryOrClasspathField(DexEncodedField field) {
diff --git a/src/main/java/com/android/tools/r8/shaking/TreePruner.java b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
index 05d281b..da42de6 100644
--- a/src/main/java/com/android/tools/r8/shaking/TreePruner.java
+++ b/src/main/java/com/android/tools/r8/shaking/TreePruner.java
@@ -18,11 +18,13 @@
import com.android.tools.r8.graph.EnclosingMethodAttribute;
import com.android.tools.r8.graph.InnerClassAttribute;
import com.android.tools.r8.graph.NestMemberClassAttribute;
+import com.android.tools.r8.ir.optimize.info.FieldOptimizationInfo;
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.utils.ExceptionUtils;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Timing;
import com.google.common.collect.Sets;
+import com.google.common.collect.Streams;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -175,6 +177,9 @@
clazz.removeEnclosingMethod(this::isAttributeReferencingPrunedItem);
rewriteNestAttributes(clazz);
usagePrinter.visited();
+ assert Streams.stream(clazz.fields())
+ .map(DexEncodedField::getOptimizationInfo)
+ .noneMatch(FieldOptimizationInfo::isDead);
}
private void rewriteNestAttributes(DexProgramClass clazz) {
diff --git a/src/test/examples/shaking1/print-mapping.ref b/src/test/examples/shaking1/print-mapping-cf.ref
similarity index 64%
rename from src/test/examples/shaking1/print-mapping.ref
rename to src/test/examples/shaking1/print-mapping-cf.ref
index d346c04..87a7bc0 100644
--- a/src/test/examples/shaking1/print-mapping.ref
+++ b/src/test/examples/shaking1/print-mapping-cf.ref
@@ -1,6 +1,5 @@
shaking1.Shaking -> shaking1.Shaking:
shaking1.Used -> a.a:
- java.lang.String name -> a
1:1:java.lang.String method():17:17 -> a
1:1:void main(java.lang.String[]):8:8 -> main
- 1:2:void <init>(java.lang.String):12:13 -> <init>
+ 1:1:void <init>(java.lang.String):12:12 -> <init>
diff --git a/src/test/examples/shaking1/print-mapping-dex.ref b/src/test/examples/shaking1/print-mapping-dex.ref
new file mode 100644
index 0000000..602e5f8
--- /dev/null
+++ b/src/test/examples/shaking1/print-mapping-dex.ref
@@ -0,0 +1,5 @@
+shaking1.Shaking -> shaking1.Shaking:
+shaking1.Used -> a.a:
+ java.lang.String method() -> a
+ 1:1:void main(java.lang.String[]):8:8 -> main
+ 1:1:void <init>(java.lang.String):12:12 -> <init>
diff --git a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
index b9883ef..2cb3e95 100644
--- a/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
+++ b/src/test/java/com/android/tools/r8/accessrelaxation/NoRelaxationForSerializableTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestCompileResult;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.naming.MemberNaming.MethodSignature;
@@ -32,7 +33,8 @@
@NeverMerge
class MySerializable implements Serializable {
- transient int value;
+
+ @NeverPropagateValue transient int value;
MySerializable(int value) {
this.value = value;
@@ -169,6 +171,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(CLASSES)
.enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
.addKeepRuleFiles(configuration)
.setMinApi(parameters.getApiLevel())
.compile();
diff --git a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
index 7813a2d..9461c6b 100644
--- a/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/analysis/sideeffect/SingletonClassInitializerPatternCanBePostponedTest.java
@@ -8,6 +8,7 @@
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.MatcherAssert.assertThat;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -37,6 +38,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(SingletonClassInitializerPatternCanBePostponedTest.class)
.addKeepMainRule(TestClass.class)
+ .enableMemberValuePropagationAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -65,7 +67,7 @@
static A INSTANCE = new A(" world!");
- final String message;
+ @NeverPropagateValue final String message;
A(String message) {
this.message = message;
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 2c8f4b3..0745620 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,7 +6,6 @@
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.assertTrue;
import com.android.tools.r8.NeverClassInline;
@@ -88,9 +87,7 @@
ClassSubject aClassSubject = inspector.clazz(A.class);
assertThat(aClassSubject, isPresent());
- // TODO(b/125282093): Need to remove the instance-put instructions in A.<init>(). This can not
- // be done safely by the time we process A.<init>(), so some kind of post-processing is needed.
- assertEquals(3, aClassSubject.allInstanceFields().size());
+ assertTrue(aClassSubject.allInstanceFields().isEmpty());
}
static class TestClass {
@@ -125,7 +122,7 @@
String s = "Hello world!";
}
- enum MyEnum {
+ public enum MyEnum {
A,
B
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
index f68a131..433ee13 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/membervaluepropagation/NonFinalFieldWithDefaultValueAssignmentPropagationTest.java
@@ -7,7 +7,7 @@
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.assertFalse;
+import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
@@ -61,8 +61,7 @@
MethodSubject configConstructorSubject = configClassSubject.init();
assertThat(configConstructorSubject, isPresent());
- // TODO(b/147799637): Should be true.
- assertFalse(
+ assertTrue(
configConstructorSubject.streamInstructions().noneMatch(InstructionSubject::isInstancePut));
}
diff --git a/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java b/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java
index 69b4b03..180541a 100644
--- a/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/FieldMinificationCollisionTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.StringUtils;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -29,6 +30,7 @@
.addKeepMainRule(TestClass.class)
.addKeepRules(
"-keep class " + B.class.getTypeName() + " { public java.lang.String f2; }")
+ .enableMemberValuePropagationAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
.enableMergeAnnotations()
@@ -55,7 +57,7 @@
@NeverMerge
static class A {
- public String f1;
+ @NeverPropagateValue public String f1;
public A(String f1) {
this.f1 = f1;
@@ -65,7 +67,7 @@
@NeverMerge
static class B extends A {
- public String f2;
+ @NeverPropagateValue public String f2;
public B(String f1, String f2) {
super(f1);
@@ -76,7 +78,7 @@
@NeverClassInline
static class C extends B {
- public String f3;
+ @NeverPropagateValue public String f3;
public C(String f1, String f2, String f3) {
super(f1, f2);
diff --git a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java
index 77190ce..943e4bf 100644
--- a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubClassTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.BooleanUtils;
@@ -34,7 +35,8 @@
@Parameterized.Parameters(name = "{0}, reserve name: {1}")
public static List<Object[]> data() {
- return buildParameters(getTestParameters().withAllRuntimes().build(), BooleanUtils.values());
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build(), BooleanUtils.values());
}
public ReservedFieldNameInSubClassTest(TestParameters parameters, boolean reserveName) {
@@ -49,6 +51,7 @@
testForR8(parameters.getBackend())
.addProgramClasses(
TestClass.class, A.class, B.class, C.class, I.class, J.class, K.class)
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -57,7 +60,7 @@
+ C.class.getTypeName()
+ "{ java.lang.String a; }"
: "")
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), TestClass.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
@@ -140,13 +143,13 @@
@NeverMerge
static class A {
- String f1 = "He";
+ @NeverPropagateValue String f1 = "He";
}
@NeverMerge
static class B extends A {
- String f2 = "l";
+ @NeverPropagateValue String f2 = "l";
}
@NeverMerge
@@ -169,7 +172,7 @@
static class C extends B implements J, K {
- String a = "!";
+ @NeverPropagateValue String a = "!";
@Override
public String toString() {
diff --git a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java
index 057ba0c..309c156 100644
--- a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSubInterfaceTest.java
@@ -11,6 +11,7 @@
import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.BooleanUtils;
@@ -46,6 +47,7 @@
R8TestRunResult result =
testForR8(Backend.DEX)
.addProgramClasses(TestClass.class, A.class, B.class, I.class, J.class)
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addKeepMainRule(TestClass.class)
.addKeepRules(
@@ -102,6 +104,7 @@
.addProgramClasses(TestClass.class, A.class, B.class)
.addLibraryClasses(I.class, J.class)
.addLibraryFiles(runtimeJar(Backend.DEX))
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addKeepMainRule(TestClass.class)
.compile()
@@ -138,7 +141,7 @@
@NeverMerge
static class A {
- String f2 = " ";
+ @NeverPropagateValue String f2 = " ";
}
static class B extends A implements J {
diff --git a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java
index f7551b7..4af8655 100644
--- a/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/ReservedFieldNameInSuperInterfaceTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assume.assumeFalse;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.utils.BooleanUtils;
@@ -50,6 +51,7 @@
reserveName
? "-keepclassmembernames class " + I.class.getTypeName() + "{ <fields>; }"
: "")
+ .enableMemberValuePropagationAnnotations()
.run(TestClass.class)
.assertSuccessWithOutput(expectedOutput);
@@ -79,6 +81,7 @@
.addLibraryClasses(I.class)
.addLibraryFiles(runtimeJar(Backend.DEX))
.addKeepMainRule(TestClass.class)
+ .enableMemberValuePropagationAnnotations()
.compile()
.addRunClasspathFiles(testForD8().addProgramClasses(I.class).compile().writeToZip())
.run(TestClass.class)
@@ -116,7 +119,7 @@
static class A implements I {
- String f2 = "world!";
+ @NeverPropagateValue String f2 = "world!";
@Override
public String toString() {
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java
index da47d4c..3d4b859 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingKeepPrecedenceTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -38,7 +39,7 @@
@NeverClassInline
public static class B { // Should be kept with all members without renaming.
- int foo = 4;
+ @NeverPropagateValue int foo = 4;
@NeverInline
int bar() {
@@ -63,7 +64,7 @@
@Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingKeepPrecedenceTest(TestParameters parameters) {
@@ -90,7 +91,8 @@
.addKeepClassRules(A.class)
.addKeepRules("-keepclassmembernames class " + B.class.getTypeName() + " { *; }")
.addKeepMainRule(Main.class)
- .setMinApi(parameters.getRuntime())
+ .enableMemberValuePropagationAnnotations()
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines(
"What is the answer to life the universe and everything?", "42")
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java
index d6382aa..f527ead 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/ApplyMappingMinificationTest.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.CompilationFailedException;
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.R8TestRunResult;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -32,8 +33,10 @@
@NeverClassInline
public static class A {
- public int fieldA = 1;
- public int fieldB = 2;
+
+ @NeverPropagateValue public int fieldA = 1;
+
+ @NeverPropagateValue public int fieldB = 2;
@NeverInline
public void methodA() {
@@ -75,7 +78,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public ApplyMappingMinificationTest(TestParameters parameters) {
@@ -96,9 +99,10 @@
.addKeepRules(
"-keepclassmembers class " + A.class.getTypeName() + " { void methodC(); }")
.enableInliningAnnotations()
+ .enableMemberValuePropagationAnnotations()
.enableNeverClassInliningAnnotations()
.addApplyMapping(StringUtils.lines(pgMap))
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), C.class)
.assertSuccessWithOutputLines("1", "2", "A.methodA", "A.methodB", "A.methodC", "B.foo")
.inspect(
diff --git a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
index 30a8126..3b683fa 100644
--- a/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
+++ b/src/test/java/com/android/tools/r8/naming/applymapping/sourcelibrary/MemberResolutionTest.java
@@ -9,6 +9,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -29,7 +30,7 @@
@NeverMerge
abstract class AbstractChecker {
// String tag -> p
- private String tag = "PrivateInitialTag_AbstractChecker";
+ @NeverPropagateValue private String tag = "PrivateInitialTag_AbstractChecker";
// check() -> x
private void check() {
@@ -80,7 +81,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public MemberResolutionTest(TestParameters parameters) {
@@ -121,9 +122,10 @@
AbstractChecker.class, ConcreteChecker.class, MemberResolutionTestMain.class)
.addKeepMainRule(MemberResolutionTestMain.class)
.addKeepRules("-applymapping " + mapPath)
+ .enableMemberValuePropagationAnnotations()
.enableMergeAnnotations()
.addOptionsModification(options -> options.enableInlining = false)
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.run(parameters.getRuntime(), MemberResolutionTestMain.class)
.assertSuccessWithOutput(expectedOutput)
.inspector();
diff --git a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
index 327e1b5..9c88b7f 100644
--- a/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/PrintUsageTest.java
@@ -158,7 +158,9 @@
Optional<ClassSubject> thing = inspector.clazz("shaking8.Thing");
assertTrue(thing.isPresent());
assertTrue(thing.get().field("int", "aField"));
- assertFalse(inspector.clazz("shaking8.OtherThing").isPresent());
+ Optional<ClassSubject> otherThing = inspector.clazz("shaking8.OtherThing");
+ assertTrue(otherThing.isPresent());
+ assertTrue(otherThing.get().field("int", "otherField"));
assertTrue(inspector.clazz("shaking8.YetAnotherThing").isPresent());
}
diff --git a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
index fbe40d6..d246ba7 100644
--- a/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/TreeShakingSpecificTest.java
@@ -140,7 +140,11 @@
.collect(Collectors.joining("\n"));
String refMapping =
new String(
- Files.readAllBytes(Paths.get(EXAMPLES_DIR, "shaking1", "print-mapping.ref")),
+ Files.readAllBytes(
+ Paths.get(
+ EXAMPLES_DIR,
+ "shaking1",
+ "print-mapping-" + backend.name().toLowerCase() + ".ref")),
StandardCharsets.UTF_8);
Assert.assertEquals(sorted(refMapping), sorted(actualMapping));
}
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
index 707a415..366a97e 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptViaClassInitializerTestRunner.java
@@ -13,6 +13,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverMerge;
+import com.android.tools.r8.NeverPropagateValue;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -51,7 +52,7 @@
public enum T {
A(A::new);
- private final Supplier<Object> factory;
+ @NeverPropagateValue private final Supplier<Object> factory;
T(Supplier<Object> factory) {
this.factory = factory;
@@ -111,6 +112,7 @@
.enableGraphInspector(consumer)
.addProgramClassesAndInnerClasses(Main.class, A.class, T.class)
.addKeepMethodRules(mainMethod)
+ .enableMemberValuePropagationAnnotations()
.setMinApi(AndroidApiLevel.N)
.apply(
b -> {