Merge commit 'c471bd7f6f7b3d20d030df56df1fe84a237d82f4' into dev-release
diff --git a/src/main/java/com/android/tools/r8/graph/DexApplication.java b/src/main/java/com/android/tools/r8/graph/DexApplication.java
index d0a336a..67f8be4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexApplication.java
+++ b/src/main/java/com/android/tools/r8/graph/DexApplication.java
@@ -131,6 +131,7 @@
return flags;
}
+ @Override
public abstract DexClass definitionFor(DexType type);
public abstract DexProgramClass programDefinitionFor(DexType type);
diff --git a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
index d18db79..57212d4 100644
--- a/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
+++ b/src/main/java/com/android/tools/r8/graph/DexDefinitionSupplier.java
@@ -76,6 +76,11 @@
@Deprecated
DexClass definitionFor(DexType type);
+ default DexClassAndMethod definitionFor(DexMethod method) {
+ DexClass holder = definitionFor(method.getHolderType());
+ return holder != null ? holder.lookupClassMethod(method) : null;
+ }
+
// Use programDefinitionFor with a context.
@Deprecated
default DexProgramClass definitionForProgramType(DexType type) {
diff --git a/src/main/java/com/android/tools/r8/graph/DexMethod.java b/src/main/java/com/android/tools/r8/graph/DexMethod.java
index 4f23975..0cd5a14 100644
--- a/src/main/java/com/android/tools/r8/graph/DexMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexMethod.java
@@ -145,7 +145,7 @@
@Override
public String toString() {
- return "Method " + holder + "." + name + " " + proto.toString();
+ return toSourceString();
}
public MethodReference asMethodReference() {
diff --git a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
index 6d3e71c..e69e74b 100644
--- a/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
+++ b/src/main/java/com/android/tools/r8/graph/JarClassFileReader.java
@@ -330,7 +330,11 @@
@Override
public void visitPermittedSubclass(String permittedSubclass) {
- throw new CompilationError("Sealed classes are not supported", origin);
+ if (classKind == ClassKind.PROGRAM) {
+ throw new CompilationError("Sealed classes are not supported as program classes", origin);
+ }
+ // For library and classpath just ignore the permitted subclasses, as the compiler is not
+ // validating the code with respect to sealed classes.
}
@Override
diff --git a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
index 0a60b01..dda7746 100644
--- a/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
+++ b/src/main/java/com/android/tools/r8/graph/RewrittenPrototypeDescription.java
@@ -204,21 +204,28 @@
public static class RewrittenTypeInfo extends ArgumentInfo {
+ private final DexType castType;
private final DexType oldType;
private final DexType newType;
private final SingleValue singleValue;
+ public static Builder builder() {
+ return new Builder();
+ }
+
public static RewrittenTypeInfo toVoid(
DexType oldReturnType, DexItemFactory dexItemFactory, SingleValue singleValue) {
assert singleValue != null;
- return new RewrittenTypeInfo(oldReturnType, dexItemFactory.voidType, singleValue);
+ return new RewrittenTypeInfo(oldReturnType, dexItemFactory.voidType, null, singleValue);
}
public RewrittenTypeInfo(DexType oldType, DexType newType) {
- this(oldType, newType, null);
+ this(oldType, newType, null, null);
}
- public RewrittenTypeInfo(DexType oldType, DexType newType, SingleValue singleValue) {
+ public RewrittenTypeInfo(
+ DexType oldType, DexType newType, DexType castType, SingleValue singleValue) {
+ this.castType = castType;
this.oldType = oldType;
this.newType = newType;
this.singleValue = singleValue;
@@ -229,6 +236,10 @@
return other.hasRewrittenReturnInfo() ? combine(other.getRewrittenReturnInfo()) : this;
}
+ public DexType getCastType() {
+ return castType;
+ }
+
public DexType getNewType() {
return newType;
}
@@ -245,6 +256,10 @@
return newType.isVoidType();
}
+ public boolean hasCastType() {
+ return castType != null;
+ }
+
public boolean hasSingleValue() {
return singleValue != null;
}
@@ -271,18 +286,23 @@
public RewrittenTypeInfo combine(RewrittenTypeInfo other) {
assert !getNewType().isVoidType();
assert getNewType() == other.getOldType();
- return new RewrittenTypeInfo(getOldType(), other.getNewType(), other.getSingleValue());
+ return new RewrittenTypeInfo(
+ getOldType(), other.getNewType(), getCastType(), other.getSingleValue());
}
@Override
public RewrittenTypeInfo rewrittenWithLens(
AppView<AppInfoWithLiveness> appView, GraphLens graphLens) {
+ DexType rewrittenCastType = castType != null ? graphLens.lookupType(castType) : null;
DexType rewrittenNewType = graphLens.lookupType(newType);
SingleValue rewrittenSingleValue =
hasSingleValue() ? getSingleValue().rewrittenWithLens(appView, graphLens) : null;
- if (rewrittenNewType != newType || rewrittenSingleValue != singleValue) {
+ if (rewrittenCastType != castType
+ || rewrittenNewType != newType
+ || rewrittenSingleValue != singleValue) {
// The old type is intentionally not rewritten.
- return new RewrittenTypeInfo(oldType, rewrittenNewType, rewrittenSingleValue);
+ return new RewrittenTypeInfo(
+ oldType, rewrittenNewType, rewrittenCastType, rewrittenSingleValue);
}
return this;
}
@@ -308,6 +328,45 @@
assert newType.toBaseType(dexItemFactory).isIntType();
return true;
}
+
+ public static class Builder {
+
+ private DexType castType;
+ private DexType oldType;
+ private DexType newType;
+ private SingleValue singleValue;
+
+ public Builder applyIf(boolean condition, Consumer<Builder> consumer) {
+ if (condition) {
+ consumer.accept(this);
+ }
+ return this;
+ }
+
+ public Builder setCastType(DexType castType) {
+ this.castType = castType;
+ return this;
+ }
+
+ public Builder setOldType(DexType oldType) {
+ this.oldType = oldType;
+ return this;
+ }
+
+ public Builder setNewType(DexType newType) {
+ this.newType = newType;
+ return this;
+ }
+
+ public Builder setSingleValue(SingleValue singleValue) {
+ this.singleValue = singleValue;
+ return this;
+ }
+
+ public RewrittenTypeInfo build() {
+ return new RewrittenTypeInfo(oldType, newType, castType, singleValue);
+ }
+ }
}
public static class ArgumentInfoCollection {
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
index a8b8d30..682da64 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/ClassInstanceFieldsMerger.java
@@ -8,6 +8,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.DexTypeUtils;
import com.android.tools.r8.horizontalclassmerging.HorizontalClassMergerGraphLens.Builder;
import com.android.tools.r8.horizontalclassmerging.policies.SameInstanceFields.InstanceFieldInfo;
import com.android.tools.r8.utils.IterableUtils;
@@ -149,12 +151,15 @@
DexEncodedField newField;
if (needsRelaxedType(targetField, sourceFields)) {
+ DexType newFieldType =
+ DexTypeUtils.computeLeastUpperBound(
+ appView,
+ Iterables.transform(
+ Iterables.concat(IterableUtils.singleton(targetField), sourceFields),
+ DexEncodedField::getType));
newField =
targetField.toTypeSubstitutedField(
- appView,
- targetField
- .getReference()
- .withType(appView.dexItemFactory().objectType, appView.dexItemFactory()));
+ appView, targetField.getReference().withType(newFieldType, appView.dexItemFactory()));
} else {
newField = targetField;
}
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
index 202f78c..b14f07c 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/HorizontalClassMerger.java
@@ -126,7 +126,7 @@
KeepInfoCollection keepInfo = appView.getKeepInfo();
keepInfo.mutate(
mutator ->
- mutator.removeKeepInfoForPrunedItems(
+ mutator.removeKeepInfoForMergedClasses(
PrunedItems.builder().setRemovedClasses(mergedClasses.getSources()).build()));
// Must rewrite AppInfoWithLiveness before pruning the merged classes, to ensure that allocation
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
index 6e88f4d..0c19d53 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/policies/NoIllegalInlining.java
@@ -29,7 +29,7 @@
private boolean disallowInlining(ProgramMethod method) {
Code code = method.getDefinition().getCode();
- if (appView.appInfo().isNeverInlineMethod(method.getReference())) {
+ if (!appView.getKeepInfo(method).isInliningAllowed(appView.options())) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
index bb4f505..10af841 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/NullSimpleInliningConstraint.java
@@ -52,12 +52,13 @@
: NeverSimpleInliningConstraint.getInstance();
} else if (argumentInfo.isRewrittenTypeInfo()) {
RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
- // We should only get here as a result of enum unboxing.
- assert rewrittenTypeInfo.verifyIsDueToUnboxing(appView.dexItemFactory());
- // Rewrite definitely-null constraints to definitely-zero constraints.
- return nullability.isDefinitelyNull()
- ? factory.createEqualToNumberConstraint(getArgumentIndex(), 0)
- : factory.createNotEqualToNumberConstraint(getArgumentIndex(), 0);
+ if (rewrittenTypeInfo.getNewType().isIntType()) {
+ // Rewrite definitely-null constraints to definitely-zero constraints.
+ return nullability.isDefinitelyNull()
+ ? factory.createEqualToNumberConstraint(getArgumentIndex(), 0)
+ : factory.createNotEqualToNumberConstraint(getArgumentIndex(), 0);
+ }
+ return this;
}
return withArgumentIndex(changes.getNewArgumentIndex(getArgumentIndex()), factory);
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
index 7596684..4d9b9a0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/GeneratedMessageLiteShrinker.java
@@ -4,38 +4,49 @@
package com.android.tools.r8.ir.analysis.proto;
+import static com.android.tools.r8.graph.DexClassAndMethod.asProgramMethodOrNull;
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getInfoValueFromMessageInfoConstructionInvoke;
import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.getObjectsValueFromMessageInfoConstructionInvoke;
import static com.android.tools.r8.ir.analysis.proto.ProtoUtils.setObjectsValueForMessageInfoConstructionInvoke;
+import static com.android.tools.r8.ir.analysis.type.Nullability.definitelyNotNull;
+import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexItemFactory;
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.ProgramMethod;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoMessageInfo;
import com.android.tools.r8.ir.analysis.proto.schema.ProtoObject;
-import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.code.ArrayPut;
import com.android.tools.r8.ir.code.BasicBlock;
+import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.ConstString;
import com.android.tools.r8.ir.code.IRCode;
import com.android.tools.r8.ir.code.IRCodeUtils;
import com.android.tools.r8.ir.code.Instruction;
import com.android.tools.r8.ir.code.InstructionListIterator;
+import com.android.tools.r8.ir.code.InvokeDirect;
import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.InvokeMethodWithReceiver;
import com.android.tools.r8.ir.code.MemberType;
import com.android.tools.r8.ir.code.NewArrayEmpty;
+import com.android.tools.r8.ir.code.NewInstance;
import com.android.tools.r8.ir.code.Value;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.OneTimeMethodProcessor;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackIgnore;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.shaking.DependentMinimumKeepInfoCollection;
import com.android.tools.r8.utils.Timing;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
+import com.google.common.collect.Sets;
import java.util.List;
+import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
@@ -60,8 +71,8 @@
// Types.
this.objectArrayType =
TypeElement.fromDexType(
- appView.dexItemFactory().objectArrayType, Nullability.definitelyNotNull(), appView);
- this.stringType = TypeElement.stringClassType(appView, Nullability.definitelyNotNull());
+ appView.dexItemFactory().objectArrayType, definitelyNotNull(), appView);
+ this.stringType = TypeElement.stringClassType(appView, definitelyNotNull());
}
public void extendRootSet(DependentMinimumKeepInfoCollection dependentMinimumKeepInfo) {
@@ -81,25 +92,17 @@
.disallowOptimization();
}
- ProgramMethod newRepeatedGeneratedExtensionMethod =
- generatedMessageLiteClass.lookupProgramMethod(
- references.generatedMessageLiteMethods.newRepeatedGeneratedExtension);
- if (newRepeatedGeneratedExtensionMethod != null) {
- dependentMinimumKeepInfo
- .getOrCreateUnconditionalMinimumKeepInfoFor(
- newRepeatedGeneratedExtensionMethod.getReference())
- .disallowOptimization();
- }
-
- ProgramMethod newSingularGeneratedExtensionMethod =
- generatedMessageLiteClass.lookupProgramMethod(
- references.generatedMessageLiteMethods.newSingularGeneratedExtension);
- if (newSingularGeneratedExtensionMethod != null) {
- dependentMinimumKeepInfo
- .getOrCreateUnconditionalMinimumKeepInfoFor(
- newSingularGeneratedExtensionMethod.getReference())
- .disallowOptimization();
- }
+ references.forEachMethodReference(
+ reference -> {
+ DexProgramClass holder =
+ asProgramClassOrNull(appView.definitionFor(reference.getHolderType()));
+ ProgramMethod method = reference.lookupOnProgramClass(holder);
+ if (method != null) {
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(method.getReference())
+ .disallowOptimization();
+ }
+ });
}
}
@@ -107,9 +110,98 @@
ProgramMethod method = code.context();
if (references.isDynamicMethod(method.getReference())) {
rewriteDynamicMethod(method, code);
+ } else if (appView.hasLiveness()) {
+ optimizeNewMutableInstance(appView.withLiveness(), code);
}
}
+ private void optimizeNewMutableInstance(AppView<AppInfoWithLiveness> appView, IRCode code) {
+ Set<Value> affectedValues = Sets.newIdentityHashSet();
+ BasicBlockIterator blockIterator = code.listIterator();
+ while (blockIterator.hasNext()) {
+ BasicBlock block = blockIterator.next();
+ InstructionListIterator instructionIterator = block.listIterator(code);
+ while (instructionIterator.hasNext()) {
+ Instruction instruction = instructionIterator.next();
+ DexType newMutableInstanceType = getNewMutableInstanceType(appView, instruction);
+ if (newMutableInstanceType == null) {
+ continue;
+ }
+
+ DexMethod instanceInitializerReference =
+ appView.dexItemFactory().createInstanceInitializer(newMutableInstanceType);
+ ProgramMethod instanceInitializer =
+ asProgramMethodOrNull(appView.definitionFor(instanceInitializerReference));
+ if (instanceInitializer == null
+ || AccessControl.isMemberAccessible(
+ instanceInitializer, instanceInitializer.getHolder(), code.context(), appView)
+ .isPossiblyFalse()) {
+ continue;
+ }
+
+ NewInstance newInstance =
+ NewInstance.builder()
+ .setType(newMutableInstanceType)
+ .setFreshOutValue(
+ code, newMutableInstanceType.toTypeElement(appView, definitelyNotNull()))
+ .setPosition(instruction)
+ .build();
+ instructionIterator.replaceCurrentInstruction(newInstance, affectedValues);
+
+ InvokeDirect constructorInvoke =
+ InvokeDirect.builder()
+ .setMethod(instanceInitializerReference)
+ .setSingleArgument(newInstance.outValue())
+ .setPosition(instruction)
+ .build();
+
+ if (block.hasCatchHandlers()) {
+ // Split the block after the new-instance instruction and insert the constructor call in
+ // the split block.
+ BasicBlock splitBlock =
+ instructionIterator.splitCopyCatchHandlers(code, blockIterator, appView.options());
+ instructionIterator = splitBlock.listIterator(code);
+ instructionIterator.add(constructorInvoke);
+ BasicBlock previousBlock =
+ blockIterator.previousUntil(previous -> previous == splitBlock);
+ assert previousBlock != null;
+ blockIterator.next();
+ } else {
+ instructionIterator.add(constructorInvoke);
+ }
+ }
+ }
+ if (!affectedValues.isEmpty()) {
+ new TypeAnalysis(appView).narrowing(affectedValues);
+ }
+ }
+
+ private DexType getNewMutableInstanceType(
+ AppView<AppInfoWithLiveness> appView, Instruction instruction) {
+ if (!instruction.isInvokeMethodWithReceiver()) {
+ return null;
+ }
+ InvokeMethodWithReceiver invoke = instruction.asInvokeMethodWithReceiver();
+ DexMethod invokedMethod = invoke.getInvokedMethod();
+ if (!references.isDynamicMethod(invokedMethod)
+ && !references.isDynamicMethodBridge(invokedMethod)) {
+ return null;
+ }
+ assert invokedMethod.getParameter(0) == references.methodToInvokeType;
+ if (!references.methodToInvokeMembers.isNewMutableInstanceEnum(
+ invoke.getFirstNonReceiverArgument())) {
+ return null;
+ }
+ TypeElement receiverType = invoke.getReceiver().getDynamicUpperBoundType(appView);
+ if (!receiverType.isClassType()) {
+ return null;
+ }
+ DexType rawReceiverType = receiverType.asClassType().getClassType();
+ return appView.appInfo().isStrictSubtypeOf(rawReceiverType, references.generatedMessageLiteType)
+ ? rawReceiverType
+ : null;
+ }
+
public void postOptimizeDynamicMethods(
IRConverter converter, ExecutorService executorService, Timing timing)
throws ExecutionException {
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
index 030fe3d..31633de 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/proto/ProtoReferences.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.code.Value;
+import java.util.function.Consumer;
public class ProtoReferences {
@@ -129,6 +130,17 @@
methodToInvokeMembers = new MethodToInvokeMembers(factory);
}
+ public void forEachMethodReference(Consumer<DexMethod> consumer) {
+ generatedExtensionMethods.forEachMethodReference(consumer);
+ generatedMessageLiteMethods.forEachMethodReference(consumer);
+ generatedMessageLiteBuilderMethods.forEachMethodReference(consumer);
+ generatedMessageLiteExtendableBuilderMethods.forEachMethodReference(consumer);
+ methodToInvokeMembers.forEachMethodReference(consumer);
+ consumer.accept(dynamicMethod);
+ consumer.accept(newMessageInfoMethod);
+ consumer.accept(rawMessageInfoConstructor);
+ }
+
public DexField getDefaultInstanceField(DexProgramClass holder) {
return dexItemFactory.createField(holder.type, holder.type, defaultInstanceFieldName);
}
@@ -220,6 +232,11 @@
dexItemFactory.constructorMethodName);
}
+ public void forEachMethodReference(Consumer<DexMethod> consumer) {
+ consumer.accept(constructor);
+ consumer.accept(constructorWithClass);
+ }
+
public boolean isConstructor(DexMethod method) {
return method == constructor || method == constructorWithClass;
}
@@ -230,7 +247,6 @@
public final DexMethod createBuilderMethod;
public final DexMethod dynamicMethodBridgeMethod;
public final DexMethod dynamicMethodBridgeMethodWithObject;
- public final DexMethod isInitializedMethod;
public final DexMethod newRepeatedGeneratedExtension;
public final DexMethod newSingularGeneratedExtension;
@@ -251,11 +267,6 @@
dexItemFactory.createProto(
dexItemFactory.objectType, methodToInvokeType, dexItemFactory.objectType),
"dynamicMethod");
- isInitializedMethod =
- dexItemFactory.createMethod(
- generatedMessageLiteType,
- dexItemFactory.createProto(dexItemFactory.booleanType),
- "isInitialized");
newRepeatedGeneratedExtension =
dexItemFactory.createMethod(
generatedMessageLiteType,
@@ -283,25 +294,31 @@
dexItemFactory.classType),
"newSingularGeneratedExtension");
}
+
+ public void forEachMethodReference(Consumer<DexMethod> consumer) {
+ consumer.accept(createBuilderMethod);
+ consumer.accept(dynamicMethodBridgeMethod);
+ consumer.accept(dynamicMethodBridgeMethodWithObject);
+ consumer.accept(newRepeatedGeneratedExtension);
+ consumer.accept(newSingularGeneratedExtension);
+ }
}
public class GeneratedMessageLiteBuilderMethods {
- public final DexMethod buildPartialMethod;
public final DexMethod constructorMethod;
private GeneratedMessageLiteBuilderMethods(DexItemFactory dexItemFactory) {
- buildPartialMethod =
- dexItemFactory.createMethod(
- generatedMessageLiteBuilderType,
- dexItemFactory.createProto(generatedMessageLiteType),
- "buildPartial");
constructorMethod =
dexItemFactory.createMethod(
generatedMessageLiteBuilderType,
dexItemFactory.createProto(dexItemFactory.voidType, generatedMessageLiteType),
dexItemFactory.constructorMethodName);
}
+
+ public void forEachMethodReference(Consumer<DexMethod> consumer) {
+ consumer.accept(constructorMethod);
+ }
}
public class GeneratedMessageLiteExtendableBuilderMethods {
@@ -322,6 +339,11 @@
dexItemFactory.voidType, generatedMessageLiteExtendableMessageType),
dexItemFactory.constructorMethodName);
}
+
+ public void forEachMethodReference(Consumer<DexMethod> consumer) {
+ consumer.accept(buildPartialMethod);
+ consumer.accept(constructorMethod);
+ }
}
public class MethodToInvokeMembers {
@@ -355,6 +377,10 @@
methodToInvokeType, methodToInvokeType, "SET_MEMOIZED_IS_INITIALIZED");
}
+ public void forEachMethodReference(Consumer<DexMethod> consumer) {
+ // Intentionally empty.
+ }
+
public boolean isNewMutableInstanceEnum(DexField field) {
return field == newMutableInstanceField;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
index 65e9670..7e54571 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ClassTypeElement.java
@@ -7,6 +7,7 @@
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexClass;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.ir.analysis.type.InterfaceCollection.Builder;
import com.android.tools.r8.utils.BooleanBox;
@@ -154,6 +155,16 @@
return getOrCreateVariant(nullability().meet(nullability));
}
+ public DexType toDexType(DexItemFactory dexItemFactory) {
+ if (type == dexItemFactory.objectType) {
+ DexType singleKnownInterface = getInterfaces().getSingleKnownInterface();
+ if (singleKnownInterface != null) {
+ return singleKnownInterface;
+ }
+ }
+ return type;
+ }
+
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
index 25cb05d..e97006c 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeElement.java
@@ -236,6 +236,9 @@
if (this == other) {
return true;
}
+ if (isBottom() != other.isBottom()) {
+ return false;
+ }
if (isPrimitiveType() || other.isPrimitiveType()) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
index 02459a7..6ded2e5 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/TypeUtils.java
@@ -4,10 +4,188 @@
package com.android.tools.r8.ir.analysis.type;
+import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
+import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
+import static com.android.tools.r8.ir.code.Opcodes.IF;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_GET;
+import static com.android.tools.r8.ir.code.Opcodes.INSTANCE_PUT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_DIRECT;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_INTERFACE;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_STATIC;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_SUPER;
+import static com.android.tools.r8.ir.code.Opcodes.INVOKE_VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.RETURN;
+import static com.android.tools.r8.ir.code.Opcodes.STATIC_PUT;
+
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.DexField;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.InstanceGet;
+import com.android.tools.r8.ir.code.InstancePut;
+import com.android.tools.r8.ir.code.Instruction;
+import com.android.tools.r8.ir.code.InstructionOrPhi;
+import com.android.tools.r8.ir.code.InvokeMethod;
+import com.android.tools.r8.ir.code.Phi;
+import com.android.tools.r8.ir.code.StaticPut;
+import com.android.tools.r8.ir.code.Value;
+import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.WorkList;
+import java.util.Objects;
public class TypeUtils {
+ private static class UserAndValuePair {
+
+ final InstructionOrPhi user;
+ final Value value;
+
+ UserAndValuePair(InstructionOrPhi user, Value value) {
+ this.user = user;
+ this.value = value;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null || getClass() != obj.getClass()) {
+ return false;
+ }
+ UserAndValuePair pair = (UserAndValuePair) obj;
+ return user == pair.user && value == pair.value;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(user, value);
+ }
+ }
+
+ /**
+ * Returns the "use type" of a given value {@link Value}, i.e., the weakest static type that this
+ * value must have in order for the program to type check.
+ */
+ public static TypeElement computeUseType(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method, Value value) {
+ TypeElement staticType = value.getType();
+ TypeElement useType = TypeElement.getBottom();
+ WorkList<UserAndValuePair> users = WorkList.newEqualityWorkList();
+ enqueueUsers(value, users);
+ while (users.hasNext()) {
+ UserAndValuePair item = users.next();
+ InstructionOrPhi user = item.user;
+ if (user.isPhi()) {
+ enqueueUsers(user.asPhi(), users);
+ } else {
+ Instruction instruction = user.asInstruction();
+ TypeElement instructionUseType =
+ computeUseTypeForInstruction(appView, method, instruction, item.value, users);
+ useType = useType.join(instructionUseType, appView);
+ if (useType.isTop() || useType.equalUpToNullability(staticType)) {
+ // Bail-out.
+ return staticType;
+ }
+ }
+ }
+ return useType;
+ }
+
+ private static void enqueueUsers(Value value, WorkList<UserAndValuePair> users) {
+ for (Instruction user : value.uniqueUsers()) {
+ users.addIfNotSeen(new UserAndValuePair(user, value));
+ }
+ for (Phi user : value.uniquePhiUsers()) {
+ users.addIfNotSeen(new UserAndValuePair(user, value));
+ }
+ }
+
+ private static TypeElement computeUseTypeForInstruction(
+ AppView<AppInfoWithLiveness> appView,
+ ProgramMethod method,
+ Instruction instruction,
+ Value value,
+ WorkList<UserAndValuePair> users) {
+ switch (instruction.opcode()) {
+ case ASSUME:
+ return computeUseTypeForAssume(instruction.asAssume(), users);
+ case CHECK_CAST:
+ case IF:
+ return TypeElement.getBottom();
+ case INSTANCE_GET:
+ return computeUseTypeForInstanceGet(appView, instruction.asInstanceGet());
+ case INSTANCE_PUT:
+ return computeUseTypeForInstancePut(appView, instruction.asInstancePut(), value);
+ case INVOKE_DIRECT:
+ case INVOKE_INTERFACE:
+ case INVOKE_STATIC:
+ case INVOKE_SUPER:
+ case INVOKE_VIRTUAL:
+ return computeUseTypeForInvoke(appView, instruction.asInvokeMethod(), value);
+ case RETURN:
+ return computeUseTypeForReturn(appView, method);
+ case STATIC_PUT:
+ return computeUseTypeForStaticPut(appView, instruction.asStaticPut());
+ default:
+ // Bail out for unhandled instructions.
+ return TypeElement.getTop();
+ }
+ }
+
+ private static TypeElement computeUseTypeForAssume(
+ Assume assume, WorkList<UserAndValuePair> users) {
+ enqueueUsers(assume.outValue(), users);
+ return TypeElement.getBottom();
+ }
+
+ private static TypeElement computeUseTypeForInstanceGet(
+ AppView<AppInfoWithLiveness> appView, InstanceGet instanceGet) {
+ return instanceGet.getField().getHolderType().toTypeElement(appView);
+ }
+
+ private static TypeElement computeUseTypeForInstancePut(
+ AppView<AppInfoWithLiveness> appView, InstancePut instancePut, Value value) {
+ DexField field = instancePut.getField();
+ TypeElement useType = TypeElement.getBottom();
+ if (instancePut.object() == value) {
+ useType = useType.join(field.getHolderType().toTypeElement(appView), appView);
+ }
+ if (instancePut.value() == value) {
+ useType = useType.join(field.getType().toTypeElement(appView), appView);
+ }
+ return useType;
+ }
+
+ private static TypeElement computeUseTypeForInvoke(
+ AppView<AppInfoWithLiveness> appView, InvokeMethod invoke, Value value) {
+ TypeElement useType = TypeElement.getBottom();
+ for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
+ Value argument = invoke.getArgument(argumentIndex);
+ if (argument != value) {
+ continue;
+ }
+ TypeElement useTypeForArgument =
+ invoke
+ .getInvokedMethod()
+ .getArgumentType(argumentIndex, invoke.isInvokeStatic())
+ .toTypeElement(appView);
+ useType = useType.join(useTypeForArgument, appView);
+ }
+ assert !useType.isBottom();
+ return useType;
+ }
+
+ private static TypeElement computeUseTypeForReturn(
+ AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
+ return method.getReturnType().toTypeElement(appView);
+ }
+
+ private static TypeElement computeUseTypeForStaticPut(
+ AppView<AppInfoWithLiveness> appView, StaticPut staticPut) {
+ return staticPut.getField().getType().toTypeElement(appView);
+ }
+
public static boolean isNullPointerException(TypeElement type, AppView<?> appView) {
return type.isClassType()
&& type.asClassType().getClassType() == appView.dexItemFactory().npeType;
diff --git a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
index 523ca73..4de1c80 100644
--- a/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
+++ b/src/main/java/com/android/tools/r8/ir/code/BasicBlockIterator.java
@@ -6,6 +6,7 @@
import com.android.tools.r8.utils.IteratorUtils;
import java.util.ListIterator;
+import java.util.function.Predicate;
public class BasicBlockIterator implements ListIterator<BasicBlock> {
@@ -63,6 +64,10 @@
return listIterator.previousIndex();
}
+ public BasicBlock previousUntil(Predicate<BasicBlock> predicate) {
+ return IteratorUtils.previousUntil(this, predicate);
+ }
+
@Override
public void add(BasicBlock block) {
listIterator.add(block);
diff --git a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
index 764167c..e34ab51 100644
--- a/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/CheckCast.java
@@ -232,7 +232,7 @@
assert outType.equalUpToNullability(castType);
// Check soundness of null information.
- assert inType.nullability().lessThanOrEqual(outType.nullability());
+ assert inType.nullability() == outType.nullability();
// Since we cannot remove the cast the in-value must be different from null.
assert !inType.isNullType();
diff --git a/src/main/java/com/android/tools/r8/ir/code/Instruction.java b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
index 3409c01..30f9c5f 100644
--- a/src/main/java/com/android/tools/r8/ir/code/Instruction.java
+++ b/src/main/java/com/android/tools/r8/ir/code/Instruction.java
@@ -796,6 +796,14 @@
return null;
}
+ public boolean isSafeCheckCast() {
+ return false;
+ }
+
+ public SafeCheckCast asSafeCheckCast() {
+ return null;
+ }
+
public boolean isConstNumber() {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
index efbcd4c..06173d4 100644
--- a/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
+++ b/src/main/java/com/android/tools/r8/ir/code/InvokeMethod.java
@@ -71,6 +71,10 @@
}
}
+ public Value getFirstNonReceiverArgument() {
+ return getArgument(getFirstNonReceiverArgumentIndex());
+ }
+
public int getFirstNonReceiverArgumentIndex() {
return BooleanUtils.intValue(isInvokeMethodWithReceiver());
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
index 05fba04..024f6f2 100644
--- a/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
+++ b/src/main/java/com/android/tools/r8/ir/code/NewInstance.java
@@ -38,6 +38,10 @@
this.clazz = clazz;
}
+ public static Builder builder() {
+ return new Builder();
+ }
+
public DexType getType() {
return clazz;
}
@@ -228,4 +232,24 @@
assert type.isDefinitelyNotNull();
return true;
}
+
+ public static class Builder extends BuilderBase<Builder, NewInstance> {
+
+ private DexType type;
+
+ public Builder setType(DexType type) {
+ this.type = type;
+ return this;
+ }
+
+ @Override
+ public NewInstance build() {
+ return amend(new NewInstance(type, outValue));
+ }
+
+ @Override
+ public Builder self() {
+ return this;
+ }
+ }
}
diff --git a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
index 8b36c90..72a62cd 100644
--- a/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
+++ b/src/main/java/com/android/tools/r8/ir/code/SafeCheckCast.java
@@ -35,6 +35,16 @@
return false;
}
+ @Override
+ public boolean isSafeCheckCast() {
+ return true;
+ }
+
+ @Override
+ public SafeCheckCast asSafeCheckCast() {
+ return this;
+ }
+
public static class Builder extends CheckCast.Builder {
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
index c3fd49f..5f318cd 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRBuilder.java
@@ -120,9 +120,7 @@
import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.code.ValueTypeConstraint;
import com.android.tools.r8.ir.code.Xor;
-import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.naming.dexitembasedstring.NameComputationInfo;
-import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
import com.android.tools.r8.origin.Origin;
import com.android.tools.r8.utils.AndroidApiLevel;
import com.android.tools.r8.utils.Pair;
@@ -748,26 +746,6 @@
new TypeAnalysis(appView).narrowing(ir);
}
- // Update the IR code if collected call site optimization info has something useful.
- // While aggregation of parameter information at call sites would be more precise than static
- // types, those could be still less precise at one single call site, where specific arguments
- // will be passed during (double) inlining. Instead of adding assumptions and removing invalid
- // ones, it's better not to insert assumptions for inlinee in the beginning.
- CallSiteOptimizationInfo callSiteOptimizationInfo =
- getMethod().getOptimizationInfo().getArgumentInfos();
- if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && method == context) {
- // TODO(b/190154391): Consider pruning all argument information from the optimization info
- // after the second optimization pass. That way we save memory and can assert here that
- // !appView.hasLiveness() (which currently may happen due to the reflective behavior
- // handling in the final round of tree shaking).
- if (appView.hasLiveness()) {
- ArgumentPropagatorIROptimizer.optimize(
- appView.withLiveness(),
- ir,
- callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
- }
- }
-
if (appView.options().isStringSwitchConversionEnabled()) {
StringSwitchConverter.convertToStringSwitchInstructions(ir, appView.dexItemFactory());
}
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 327326c..c4dba5e 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
@@ -81,6 +81,7 @@
import com.android.tools.r8.ir.optimize.classinliner.ClassInliner;
import com.android.tools.r8.ir.optimize.enums.EnumUnboxer;
import com.android.tools.r8.ir.optimize.enums.EnumValueOptimizer;
+import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfoCollector;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackDelayed;
@@ -96,8 +97,10 @@
import com.android.tools.r8.logging.Log;
import com.android.tools.r8.naming.IdentifierNameStringMarker;
import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagator;
+import com.android.tools.r8.optimize.argumentpropagation.ArgumentPropagatorIROptimizer;
import com.android.tools.r8.position.MethodPosition;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.shaking.KeepMethodInfo;
import com.android.tools.r8.shaking.LibraryMethodOverrideAnalysis;
import com.android.tools.r8.utils.Action;
import com.android.tools.r8.utils.CfgPrinter;
@@ -1212,6 +1215,20 @@
previous = printMethod(code, "IR after disable assertions (SSA)", previous);
+ // Update the IR code if collected call site optimization info has something useful.
+ // While aggregation of parameter information at call sites would be more precise than static
+ // types, those could be still less precise at one single call site, where specific arguments
+ // will be passed during (double) inlining. Instead of adding assumptions and removing invalid
+ // ones, it's better not to insert assumptions for inlinee in the beginning.
+ CallSiteOptimizationInfo callSiteOptimizationInfo =
+ context.getOptimizationInfo().getArgumentInfos();
+ if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo() && appView.hasLiveness()) {
+ ArgumentPropagatorIROptimizer.optimize(
+ appView.withLiveness(),
+ code,
+ callSiteOptimizationInfo.asConcreteCallSiteOptimizationInfo());
+ }
+
if (assumeInserter != null) {
assumeInserter.insertAssumeInstructions(code, timing);
}
@@ -1678,7 +1695,8 @@
|| definition.getOptimizationInfo().isReachabilitySensitive()) {
return false;
}
- if (!appView.getKeepInfo(method).isInliningAllowed(options)) {
+ KeepMethodInfo keepInfo = appView.getKeepInfo(method);
+ if (!keepInfo.isInliningAllowed(options) && !keepInfo.isClassInliningAllowed(options)) {
return false;
}
return true;
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
index 5794f8d..f6c957c 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/LensCodeRewriter.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.graph.UseRegistry.MethodHandleUse.NOT_ARGUMENT_TO_LAMBDA_METAFACTORY;
import static com.android.tools.r8.ir.code.Invoke.Type.STATIC;
import static com.android.tools.r8.ir.code.Invoke.Type.VIRTUAL;
+import static com.android.tools.r8.ir.code.Opcodes.ARGUMENT;
import static com.android.tools.r8.ir.code.Opcodes.ASSUME;
import static com.android.tools.r8.ir.code.Opcodes.CHECK_CAST;
import static com.android.tools.r8.ir.code.Opcodes.CONST_CLASS;
@@ -33,7 +34,6 @@
import static com.android.tools.r8.utils.ObjectUtils.getBooleanOrElse;
import com.android.tools.r8.errors.CompilationError;
-import com.android.tools.r8.errors.Unreachable;
import com.android.tools.r8.graph.AccessControl;
import com.android.tools.r8.graph.AppInfoWithClassHierarchy;
import com.android.tools.r8.graph.AppView;
@@ -60,7 +60,7 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeElement;
import com.android.tools.r8.ir.analysis.value.SingleNumberValue;
-import com.android.tools.r8.ir.code.Assume;
+import com.android.tools.r8.ir.code.Argument;
import com.android.tools.r8.ir.code.BasicBlock;
import com.android.tools.r8.ir.code.BasicBlockIterator;
import com.android.tools.r8.ir.code.CatchHandlers;
@@ -105,7 +105,6 @@
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.List;
-import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.function.BiFunction;
@@ -256,6 +255,9 @@
DexMethod actualTarget = lensLookup.getReference();
Invoke.Type actualInvokeType = lensLookup.getType();
+ iterator =
+ insertCastsForInvokeArgumentsIfNeeded(code, blocks, iterator, invoke, lensLookup);
+
RewrittenPrototypeDescription prototypeChanges = lensLookup.getPrototypeChanges();
if (prototypeChanges.requiresRewritingAtCallSite()
|| invoke.getType() != actualInvokeType
@@ -335,10 +337,22 @@
}
}
- Value newOutValue =
- prototypeChanges.hasBeenChangedToReturnVoid()
- ? null
- : makeOutValue(invoke, code);
+ Value newOutValue;
+ if (prototypeChanges.hasRewrittenReturnInfo()) {
+ if (invoke.hasOutValue() && !prototypeChanges.hasBeenChangedToReturnVoid()) {
+ TypeElement newReturnType =
+ prototypeChanges
+ .getRewrittenReturnInfo()
+ .getNewType()
+ .toTypeElement(appView);
+ newOutValue = code.createValue(newReturnType, invoke.getLocalInfo());
+ affectedPhis.addAll(invoke.outValue().uniquePhiUsers());
+ } else {
+ newOutValue = null;
+ }
+ } else {
+ newOutValue = makeOutValue(invoke, code);
+ }
Map<SingleNumberValue, Map<DexType, Value>> parameterMap = new IdentityHashMap<>();
@@ -415,19 +429,6 @@
iterator.add(constantReturnMaterializingInstruction);
}
}
-
- DexType actualReturnType = actualTarget.proto.returnType;
- DexType expectedReturnType = graphLens.lookupType(invokedMethod.proto.returnType);
- if (newInvoke.hasOutValue() && actualReturnType != expectedReturnType) {
- throw new Unreachable(
- "Unexpected need to insert a cast. Possibly related to resolving"
- + " b/79143143.\n"
- + invokedMethod
- + " type changed from "
- + expectedReturnType
- + " to "
- + actualReturnType);
- }
}
}
break;
@@ -660,6 +661,9 @@
if (ret.isReturnVoid()) {
break;
}
+
+ insertCastForReturnIfNeeded(code, blocks, iterator, ret);
+
DexType returnType = code.context().getReturnType();
Value retValue = ret.returnValue();
DexType initialType =
@@ -683,49 +687,30 @@
}
break;
- case ASSUME:
+ case ARGUMENT:
{
- // TODO(b/174543992): It's not clear we should rewrite the assumes here. The code
- // present fixes the problem for enum unboxing, but not for lambda merging.
- // The LensCodeRewriter is run before most assume instructions are inserted, however,
- // the call site optimization may propagate assumptions at IR building time, and such
- // assumes are already present.
- // R8 clears the assumes if the type is rewritten to a primitive type.
- Assume assume = current.asAssume();
- if (assume.hasOutValue()) {
- TypeElement type = assume.getOutType();
- TypeElement substituted = type.rewrittenWithLens(appView, graphLens);
- if (substituted != type) {
- assert type.isArrayType() || type.isClassType();
- affectedPhis.addAll(assume.outValue().uniquePhiUsers());
- if (substituted.isPrimitiveType()) {
- assert type.isClassType();
- assert appView.unboxedEnums().isUnboxedEnum(type.asClassType().getClassType());
- // Any assumption of a class type being converted to a primitive type is
- // invalid. Dynamic type is irrelevant and non null is incorrect.
- assume.outValue().replaceUsers(assume.src());
- iterator.removeOrReplaceByDebugLocalRead();
- } else if (substituted.isPrimitiveArrayType()) {
- assert type.isArrayType();
- // Non-null assumptions on a class array type being converted to a primitive
- // array type remains, but dynamic type becomes irrelevant.
- assume.unsetDynamicTypeAssumption();
- if (assume.hasNonNullAssumption()) {
- assume.outValue().setType(substituted);
- } else {
- assume.outValue().replaceUsers(assume.src());
- iterator.removeOrReplaceByDebugLocalRead();
- }
- } else {
- assert !substituted.isPrimitiveType();
- assert !substituted.isPrimitiveArrayType();
- current.outValue().setType(substituted);
- }
- }
+ Argument argument = current.asArgument();
+ TypeElement currentArgumentType = argument.getOutType();
+ TypeElement newArgumentType =
+ method
+ .getArgumentType(argument.getIndex())
+ .toTypeElement(appView, currentArgumentType.nullability());
+ if (!newArgumentType.equals(currentArgumentType)) {
+ affectedPhis.addAll(argument.outValue().uniquePhiUsers());
+ iterator.replaceCurrentInstruction(
+ Argument.builder()
+ .setIndex(argument.getIndex())
+ .setFreshOutValue(code, newArgumentType)
+ .setPosition(argument)
+ .build());
}
}
break;
+ case ASSUME:
+ assert false;
+ break;
+
default:
if (current.hasOutValue()) {
// For all other instructions, substitute any changed type.
@@ -756,7 +741,7 @@
private InstructionListIterator insertCastForFieldAssignmentIfNeeded(
IRCode code,
- ListIterator<BasicBlock> blocks,
+ BasicBlockIterator blocks,
InstructionListIterator iterator,
FieldPut fieldPut,
FieldLookupResult lookup) {
@@ -770,15 +755,112 @@
.setPosition(fieldPut.getPosition())
.build();
iterator.add(checkCast);
- iterator =
- iterator.splitCopyCatchHandlers(code, blocks, appView.options()).listIterator(code);
fieldPut.setValue(checkCast.outValue());
+
+ if (checkCast.getBlock().hasCatchHandlers()) {
+ // Split the block and reset the block iterator.
+ BasicBlock splitBlock = iterator.splitCopyCatchHandlers(code, blocks, appView.options());
+ BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+ assert previousBlock == splitBlock;
+ blocks.next();
+ iterator = splitBlock.listIterator(code);
+ }
+
Instruction next = iterator.next();
assert next == fieldPut;
}
return iterator;
}
+ private InstructionListIterator insertCastsForInvokeArgumentsIfNeeded(
+ IRCode code,
+ BasicBlockIterator blocks,
+ InstructionListIterator iterator,
+ InvokeMethod invoke,
+ MethodLookupResult lookup) {
+ RewrittenPrototypeDescription prototypeChanges = lookup.getPrototypeChanges();
+ if (prototypeChanges.isEmpty()) {
+ return iterator;
+ }
+ for (int argumentIndex = 0; argumentIndex < invoke.arguments().size(); argumentIndex++) {
+ RewrittenTypeInfo rewrittenTypeInfo =
+ prototypeChanges
+ .getArgumentInfoCollection()
+ .getArgumentInfo(argumentIndex)
+ .asRewrittenTypeInfo();
+ if (rewrittenTypeInfo != null && rewrittenTypeInfo.hasCastType()) {
+ iterator.previous();
+ Value object = invoke.getArgument(argumentIndex);
+ CheckCast checkCast =
+ SafeCheckCast.builder()
+ .setObject(object)
+ .setFreshOutValue(
+ code,
+ rewrittenTypeInfo
+ .getCastType()
+ .toTypeElement(appView, object.getType().nullability()))
+ .setCastType(rewrittenTypeInfo.getCastType())
+ .setPosition(invoke.getPosition())
+ .build();
+ iterator.add(checkCast);
+ invoke.replaceValue(argumentIndex, checkCast.outValue());
+
+ if (checkCast.getBlock().hasCatchHandlers()) {
+ // Split the block and reset the block iterator.
+ BasicBlock splitBlock = iterator.splitCopyCatchHandlers(code, blocks, appView.options());
+ BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+ assert previousBlock == splitBlock;
+ blocks.next();
+ iterator = splitBlock.listIterator(code);
+ }
+
+ Instruction next = iterator.next();
+ assert next == invoke;
+ }
+ }
+ return iterator;
+ }
+
+ private InstructionListIterator insertCastForReturnIfNeeded(
+ IRCode code, BasicBlockIterator blocks, InstructionListIterator iterator, Return ret) {
+ RewrittenPrototypeDescription prototypeChanges =
+ appView
+ .graphLens()
+ .lookupPrototypeChangesForMethodDefinition(code.context().getReference());
+ if (!prototypeChanges.hasRewrittenReturnInfo()
+ || !prototypeChanges.getRewrittenReturnInfo().hasCastType()) {
+ return iterator;
+ }
+
+ iterator.previous();
+
+ // Split the block and reset the block iterator.
+ if (ret.getBlock().hasCatchHandlers()) {
+ BasicBlock splitBlock = iterator.splitCopyCatchHandlers(code, blocks, options);
+ BasicBlock previousBlock = blocks.previousUntil(block -> block == splitBlock);
+ assert previousBlock != null;
+ blocks.next();
+ iterator = splitBlock.listIterator(code);
+ }
+
+ DexType castType = prototypeChanges.getRewrittenReturnInfo().getCastType();
+ Value returnValue = ret.returnValue();
+ CheckCast checkCast =
+ SafeCheckCast.builder()
+ .setObject(returnValue)
+ .setFreshOutValue(
+ code, castType.toTypeElement(appView, returnValue.getType().nullability()))
+ .setCastType(castType)
+ .setPosition(ret.getPosition())
+ .build();
+ iterator.add(checkCast);
+ ret.replaceValue(0, checkCast.outValue());
+
+ Instruction next = iterator.next();
+ assert next == ret;
+ return iterator;
+ }
+
private DexField rewriteFieldReference(FieldLookupResult lookup, ProgramMethod context) {
if (lookup.hasReboundReference()) {
DexClass holder = appView.definitionFor(lookup.getReboundReference().getHolderType());
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
index ecd06de..a817cd6 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/callgraph/InvokeExtractor.java
@@ -57,7 +57,7 @@
// We don't care about calls to native methods.
return;
}
- if (!appView.getKeepInfo(callee).isInliningAllowed(appView.options())) {
+ if (!appView.getKeepInfo(callee).isOptimizationAllowed(appView.options())) {
// Since the callee is kept and optimizations are disallowed, we cannot inline it into the
// caller, and we also cannot collect any optimization info for the method. Therefore, we
// drop the call edge to reduce the total number of call graph edges, which should lead to
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
index 71db541..282f4f1 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/CfInstructionDesugaringEventConsumer.java
@@ -28,6 +28,7 @@
import com.android.tools.r8.ir.desugar.records.RecordDesugaringEventConsumer.RecordInstructionDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.twr.TwrCloseResourceDesugaringEventConsumer;
import com.android.tools.r8.shaking.Enqueuer.SyntheticAdditions;
+import com.android.tools.r8.shaking.KeepMethodInfo.Joiner;
import com.google.common.collect.Sets;
import java.util.ArrayList;
import java.util.Collections;
@@ -161,7 +162,7 @@
@Override
public void acceptTwrCloseResourceMethod(ProgramMethod closeMethod, ProgramMethod context) {
- methodProcessor.scheduleDesugaredMethodForProcessing(closeMethod);
+ methodProcessor.scheduleMethodForProcessing(closeMethod, this);
}
@Override
@@ -333,7 +334,7 @@
public void acceptInvokeStaticInterfaceOutliningMethod(
ProgramMethod method, ProgramMethod context) {
// Intentionally empty. The method will be hit by tracing if required.
- additions.addNeverInlineMethod(method);
+ additions.addMinimumKeepInfo(method, Joiner::disallowInlining);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
index d7f7109..b934c40 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/backports/BackportedMethods.java
@@ -14,6 +14,7 @@
import com.android.tools.r8.cf.code.CfArrayStore;
import com.android.tools.r8.cf.code.CfCheckCast;
import com.android.tools.r8.cf.code.CfCmp;
+import com.android.tools.r8.cf.code.CfConstClass;
import com.android.tools.r8.cf.code.CfConstNull;
import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfConstString;
@@ -109,6 +110,7 @@
factory.createSynthesizedType("[Ljava/lang/CharSequence;");
factory.createSynthesizedType("[Ljava/lang/Class;");
factory.createSynthesizedType("[Ljava/lang/Object;");
+ factory.createSynthesizedType("[Ljava/lang/Throwable;");
factory.createSynthesizedType("[Ljava/util/Map$Entry;");
}
@@ -511,10 +513,14 @@
CfLabel label15 = new CfLabel();
CfLabel label16 = new CfLabel();
CfLabel label17 = new CfLabel();
+ CfLabel label18 = new CfLabel();
+ CfLabel label19 = new CfLabel();
+ CfLabel label20 = new CfLabel();
+ CfLabel label21 = new CfLabel();
return new CfCode(
method.holder,
+ 6,
4,
- 3,
ImmutableList.of(
label0,
new CfLoad(ValueType.OBJECT, 1),
@@ -749,7 +755,7 @@
FrameType.initialized(options.itemFactory.objectType)
}),
new ArrayDeque<>(Arrays.asList())),
- new CfGoto(label16),
+ new CfGoto(label20),
label12,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
@@ -763,10 +769,76 @@
new CfStore(ValueType.OBJECT, 2),
label13,
new CfLoad(ValueType.OBJECT, 0),
- new CfIf(If.Type.EQ, ValueType.OBJECT, label14),
- new CfLoad(ValueType.OBJECT, 0),
- new CfGoto(label15),
+ new CfIf(If.Type.EQ, ValueType.OBJECT, label19),
label14,
+ new CfConstClass(options.itemFactory.throwableType),
+ new CfConstString(options.itemFactory.createString("addSuppressed")),
+ new CfConstNumber(1, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(0, ValueType.INT),
+ new CfConstClass(options.itemFactory.throwableType),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.classType,
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.stringType,
+ options.itemFactory.createType("[Ljava/lang/Class;")),
+ options.itemFactory.createString("getDeclaredMethod")),
+ false),
+ new CfStore(ValueType.OBJECT, 3),
+ label15,
+ new CfLoad(ValueType.OBJECT, 3),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfConstNumber(1, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(0, ValueType.INT),
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.createProto(
+ options.itemFactory.objectType,
+ options.itemFactory.objectType,
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ options.itemFactory.createString("invoke")),
+ false),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label16,
+ new CfGoto(label18),
+ label17,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.throwableType),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.throwableType)
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(
+ options.itemFactory.createType("Ljava/lang/Exception;"))))),
+ new CfStore(ValueType.OBJECT, 3),
+ label18,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1, 2},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.throwableType),
+ FrameType.initialized(options.itemFactory.objectType),
+ FrameType.initialized(options.itemFactory.throwableType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfThrow(),
+ label19,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0, 1, 2},
@@ -777,19 +849,8 @@
}),
new ArrayDeque<>(Arrays.asList())),
new CfLoad(ValueType.OBJECT, 2),
- label15,
- new CfFrame(
- new Int2ReferenceAVLTreeMap<>(
- new int[] {0, 1, 2},
- new FrameType[] {
- FrameType.initialized(options.itemFactory.throwableType),
- FrameType.initialized(options.itemFactory.objectType),
- FrameType.initialized(options.itemFactory.throwableType)
- }),
- new ArrayDeque<>(
- Arrays.asList(FrameType.initialized(options.itemFactory.throwableType)))),
new CfThrow(),
- label16,
+ label20,
new CfFrame(
new Int2ReferenceAVLTreeMap<>(
new int[] {0, 1},
@@ -799,7 +860,7 @@
}),
new ArrayDeque<>(Arrays.asList())),
new CfReturnVoid(),
- label17),
+ label21),
ImmutableList.of(
new CfTryCatch(
label2,
@@ -841,7 +902,12 @@
label0,
label11,
ImmutableList.of(options.itemFactory.throwableType),
- ImmutableList.of(label12))),
+ ImmutableList.of(label12)),
+ new CfTryCatch(
+ label14,
+ label16,
+ ImmutableList.of(options.itemFactory.createType("Ljava/lang/Exception;")),
+ ImmutableList.of(label17))),
ImmutableList.of());
}
@@ -9079,6 +9145,163 @@
ImmutableList.of());
}
+ public static CfCode ThrowableMethods_addSuppressed(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 6,
+ 3,
+ ImmutableList.of(
+ label0,
+ new CfConstClass(options.itemFactory.throwableType),
+ new CfConstString(options.itemFactory.createString("addSuppressed")),
+ new CfConstNumber(1, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(0, ValueType.INT),
+ new CfConstClass(options.itemFactory.throwableType),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.classType,
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.stringType,
+ options.itemFactory.createType("[Ljava/lang/Class;")),
+ options.itemFactory.createString("getDeclaredMethod")),
+ false),
+ new CfStore(ValueType.OBJECT, 2),
+ label1,
+ new CfLoad(ValueType.OBJECT, 2),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfConstNumber(1, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
+ new CfStackInstruction(CfStackInstruction.Opcode.Dup),
+ new CfConstNumber(0, ValueType.INT),
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfArrayStore(MemberType.OBJECT),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.createProto(
+ options.itemFactory.objectType,
+ options.itemFactory.objectType,
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ options.itemFactory.createString("invoke")),
+ false),
+ new CfStackInstruction(CfStackInstruction.Opcode.Pop),
+ label2,
+ new CfGoto(label4),
+ label3,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.throwableType),
+ FrameType.initialized(options.itemFactory.throwableType)
+ }),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(
+ options.itemFactory.createType("Ljava/lang/Exception;"))))),
+ new CfStore(ValueType.OBJECT, 2),
+ label4,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0, 1},
+ new FrameType[] {
+ FrameType.initialized(options.itemFactory.throwableType),
+ FrameType.initialized(options.itemFactory.throwableType)
+ }),
+ new ArrayDeque<>(Arrays.asList())),
+ new CfReturnVoid(),
+ label5),
+ ImmutableList.of(
+ new CfTryCatch(
+ label0,
+ label2,
+ ImmutableList.of(options.itemFactory.createType("Ljava/lang/Exception;")),
+ ImmutableList.of(label3))),
+ ImmutableList.of());
+ }
+
+ public static CfCode ThrowableMethods_getSuppressed(InternalOptions options, DexMethod method) {
+ CfLabel label0 = new CfLabel();
+ CfLabel label1 = new CfLabel();
+ CfLabel label2 = new CfLabel();
+ CfLabel label3 = new CfLabel();
+ CfLabel label4 = new CfLabel();
+ CfLabel label5 = new CfLabel();
+ return new CfCode(
+ method.holder,
+ 3,
+ 2,
+ ImmutableList.of(
+ label0,
+ new CfConstClass(options.itemFactory.throwableType),
+ new CfConstString(options.itemFactory.createString("getSuppressed")),
+ new CfConstNumber(0, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Class;")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.classType,
+ options.itemFactory.createProto(
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.stringType,
+ options.itemFactory.createType("[Ljava/lang/Class;")),
+ options.itemFactory.createString("getDeclaredMethod")),
+ false),
+ new CfStore(ValueType.OBJECT, 1),
+ label1,
+ new CfLoad(ValueType.OBJECT, 1),
+ new CfLoad(ValueType.OBJECT, 0),
+ new CfConstNumber(0, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Object;")),
+ new CfInvoke(
+ 182,
+ options.itemFactory.createMethod(
+ options.itemFactory.createType("Ljava/lang/reflect/Method;"),
+ options.itemFactory.createProto(
+ options.itemFactory.objectType,
+ options.itemFactory.objectType,
+ options.itemFactory.createType("[Ljava/lang/Object;")),
+ options.itemFactory.createString("invoke")),
+ false),
+ new CfCheckCast(options.itemFactory.createType("[Ljava/lang/Throwable;")),
+ label2,
+ new CfReturn(ValueType.OBJECT),
+ label3,
+ new CfFrame(
+ new Int2ReferenceAVLTreeMap<>(
+ new int[] {0},
+ new FrameType[] {FrameType.initialized(options.itemFactory.throwableType)}),
+ new ArrayDeque<>(
+ Arrays.asList(
+ FrameType.initialized(
+ options.itemFactory.createType("Ljava/lang/Exception;"))))),
+ new CfStore(ValueType.OBJECT, 1),
+ label4,
+ new CfConstNumber(0, ValueType.INT),
+ new CfNewArray(options.itemFactory.createType("[Ljava/lang/Throwable;")),
+ new CfReturn(ValueType.OBJECT),
+ label5),
+ ImmutableList.of(
+ new CfTryCatch(
+ label0,
+ label2,
+ ImmutableList.of(options.itemFactory.createType("Ljava/lang/Exception;")),
+ ImmutableList.of(label3))),
+ ImmutableList.of());
+ }
+
public static CfCode UnsafeMethods_compareAndSwapObject(
InternalOptions options, DexMethod method) {
CfLabel label0 = new CfLabel();
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
index a332828..48dca2d 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/constantdynamic/ConstantDynamicClass.java
@@ -162,7 +162,11 @@
synthesizeConstantDynamicClass(builder);
} else {
// Unconditionally throw as the RI.
- behaviour = resolution.isFailedResolution() ? THROW_NSME : THROW_ICCE;
+ behaviour =
+ resolution.isNoSuchMethodErrorResult(
+ context.getContextClass(), appView.appInfoForDesugaring())
+ ? THROW_NSME
+ : THROW_ICCE;
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
index b1e77cd..1a46480 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/desugaredlibrary/retargeter/DesugaredLibraryRetargeterSyntheticHelper.java
@@ -13,7 +13,7 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterInstructionEventConsumer;
import com.android.tools.r8.ir.desugar.desugaredlibrary.retargeter.DesugaredLibraryRetargeterSynthesizerEventConsumer.DesugaredLibraryRetargeterL8SynthesizerEventConsumer;
-import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticClassBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
import com.android.tools.r8.utils.DescriptorUtils;
@@ -164,9 +164,8 @@
.setCode(
methodSig ->
appView.options().isDesugaredLibraryCompilation()
- ? new EmulateInterfaceSyntheticCfCodeProvider(
+ ? new EmulateDispatchSyntheticCfCodeProvider(
methodSig.getHolderType(),
- emulatedDispatchMethod.getHolderType(),
desugarMethod,
itfMethod,
Collections.emptyList(),
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
index 5c2ec42..3d91f5c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/itf/ProgramEmulatedInterfaceSynthesizer.java
@@ -16,7 +16,7 @@
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaring;
import com.android.tools.r8.ir.desugar.CfClassSynthesizerDesugaringEventConsumer;
import com.android.tools.r8.ir.desugar.itf.EmulatedInterfaceSynthesizerEventConsumer.L8ProgramEmulatedInterfaceSynthesizerEventConsumer;
-import com.android.tools.r8.ir.synthetic.EmulateInterfaceSyntheticCfCodeProvider;
+import com.android.tools.r8.ir.synthetic.EmulateDispatchSyntheticCfCodeProvider;
import com.android.tools.r8.synthesis.SyntheticMethodBuilder;
import com.android.tools.r8.synthesis.SyntheticNaming;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
@@ -145,9 +145,8 @@
helper.ensureDefaultAsMethodOfProgramCompanionClassStub(method).getReference();
List<Pair<DexType, DexMethod>> extraDispatchCases =
getDispatchCases(method, theInterface, companionMethod);
- return new EmulateInterfaceSyntheticCfCodeProvider(
+ return new EmulateDispatchSyntheticCfCodeProvider(
emulatedInterfaceMethod.getHolderType(),
- method.getHolderType(),
companionMethod,
libraryMethod,
extraDispatchCases,
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
index 716b6e2..1b5b27c 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/twr/TwrInstructionDesugaring.java
@@ -3,15 +3,12 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.ir.desugar.twr;
-import com.android.tools.r8.cf.code.CfConstNumber;
import com.android.tools.r8.cf.code.CfInstruction;
import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfNewArray;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.errors.CompilationError;
import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.CfCode;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
@@ -19,7 +16,6 @@
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.ir.code.ValueType;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaring;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringCollection;
import com.android.tools.r8.ir.desugar.CfInstructionDesugaringEventConsumer;
@@ -27,9 +23,11 @@
import com.android.tools.r8.ir.desugar.LocalStackAllocator;
import com.android.tools.r8.ir.desugar.backports.BackportedMethods;
import com.android.tools.r8.synthesis.SyntheticNaming.SyntheticKind;
+import com.android.tools.r8.utils.InternalOptions;
import com.google.common.collect.ImmutableList;
import java.util.Collection;
-import org.jetbrains.annotations.NotNull;
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
import org.objectweb.asm.Opcodes;
public class TwrInstructionDesugaring implements CfInstructionDesugaring {
@@ -66,58 +64,85 @@
if (isTwrCloseResourceInvoke(instruction)) {
return rewriteTwrCloseResourceInvoke(eventConsumer, context, methodProcessingContext);
}
- if (isTwrSuppressedInvoke(instruction, addSuppressed)) {
- return rewriteTwrAddSuppressedInvoke();
- }
- if (isTwrSuppressedInvoke(instruction, getSuppressed)) {
- return rewriteTwrGetSuppressedInvoke();
+ if (!appView.options().canUseSuppressedExceptions()) {
+ if (isTwrSuppressedInvoke(instruction, addSuppressed)) {
+ return rewriteTwrAddSuppressedInvoke(eventConsumer, methodProcessingContext);
+ }
+ if (isTwrSuppressedInvoke(instruction, getSuppressed)) {
+ return rewriteTwrGetSuppressedInvoke(eventConsumer, methodProcessingContext);
+ }
}
return null;
}
- private Collection<CfInstruction> rewriteTwrAddSuppressedInvoke() {
- // Remove Throwable::addSuppressed(Throwable) call.
- return ImmutableList.of(new CfStackInstruction(Opcode.Pop), new CfStackInstruction(Opcode.Pop));
+ private Collection<CfInstruction> rewriteTwrAddSuppressedInvoke(
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto proto =
+ factory.createProto(factory.voidType, factory.throwableType, factory.throwableType);
+ return createAndCallSyntheticMethod(
+ SyntheticKind.BACKPORT,
+ proto,
+ BackportedMethods::ThrowableMethods_addSuppressed,
+ methodProcessingContext,
+ eventConsumer::acceptBackportedMethod,
+ methodProcessingContext.getMethodContext());
}
- private Collection<CfInstruction> rewriteTwrGetSuppressedInvoke() {
- // Replace call to Throwable::getSuppressed() with new Throwable[0].
- return ImmutableList.of(
- new CfStackInstruction(Opcode.Pop),
- new CfConstNumber(0, ValueType.INT),
- new CfNewArray(dexItemFactory.createArrayType(1, dexItemFactory.throwableType)));
+ private Collection<CfInstruction> rewriteTwrGetSuppressedInvoke(
+ CfInstructionDesugaringEventConsumer eventConsumer,
+ MethodProcessingContext methodProcessingContext) {
+ DexItemFactory factory = appView.dexItemFactory();
+ DexProto proto =
+ factory.createProto(
+ factory.createArrayType(1, factory.throwableType), factory.throwableType);
+ return createAndCallSyntheticMethod(
+ SyntheticKind.BACKPORT,
+ proto,
+ BackportedMethods::ThrowableMethods_getSuppressed,
+ methodProcessingContext,
+ eventConsumer::acceptBackportedMethod,
+ methodProcessingContext.getMethodContext());
}
- @NotNull
private ImmutableList<CfInstruction> rewriteTwrCloseResourceInvoke(
CfInstructionDesugaringEventConsumer eventConsumer,
ProgramMethod context,
MethodProcessingContext methodProcessingContext) {
// Synthesize a new method.
- ProgramMethod closeMethod = createSyntheticCloseResourceMethod(methodProcessingContext);
- eventConsumer.acceptTwrCloseResourceMethod(closeMethod, context);
- // Rewrite the invoke to the new synthetic.
- return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, closeMethod.getReference(), false));
+ return createAndCallSyntheticMethod(
+ SyntheticKind.TWR_CLOSE_RESOURCE,
+ twrCloseResourceProto,
+ BackportedMethods::CloseResourceMethod_closeResourceImpl,
+ methodProcessingContext,
+ eventConsumer::acceptTwrCloseResourceMethod,
+ context);
}
- private ProgramMethod createSyntheticCloseResourceMethod(
- MethodProcessingContext methodProcessingContext) {
- return appView
- .getSyntheticItems()
- .createMethod(
- SyntheticKind.TWR_CLOSE_RESOURCE,
- methodProcessingContext.createUniqueContext(),
- appView,
- methodBuilder ->
- methodBuilder
- .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
- .setProto(twrCloseResourceProto)
- // Will be traced by the enqueuer.
- .disableAndroidApiLevelCheck()
- .setCode(
- m ->
- BackportedMethods.CloseResourceMethod_closeResourceImpl(
- appView.options(), m)));
+ private ImmutableList<CfInstruction> createAndCallSyntheticMethod(
+ SyntheticKind kind,
+ DexProto proto,
+ BiFunction<InternalOptions, DexMethod, CfCode> generator,
+ MethodProcessingContext methodProcessingContext,
+ BiConsumer<ProgramMethod, ProgramMethod> eventConsumerCallback,
+ ProgramMethod context) {
+ ProgramMethod method =
+ appView
+ .getSyntheticItems()
+ .createMethod(
+ kind,
+ methodProcessingContext.createUniqueContext(),
+ appView,
+ builder ->
+ builder
+ // Will be traced by the enqueuer.
+ .disableAndroidApiLevelCheck()
+ .setProto(proto)
+ .setAccessFlags(MethodAccessFlags.createPublicStaticSynthetic())
+ .setCode(methodSig -> generator.apply(appView.options(), methodSig)));
+ eventConsumerCallback.accept(method, context);
+ return ImmutableList.of(new CfInvoke(Opcodes.INVOKESTATIC, method.getReference(), false));
}
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index a10c4d7..7d12496 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -34,6 +34,7 @@
import com.android.tools.r8.ir.analysis.type.Nullability;
import com.android.tools.r8.ir.analysis.type.TypeAnalysis;
import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.type.TypeUtils;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.ConstantOrNonConstantNumberValue;
import com.android.tools.r8.ir.analysis.value.SingleConstClassValue;
@@ -1530,8 +1531,7 @@
TypeElement inTypeLattice = inValue.getType();
TypeElement outTypeLattice = outValue.getType();
- TypeElement castTypeLattice =
- TypeElement.fromDexType(castType, inTypeLattice.nullability(), appView);
+ TypeElement castTypeLattice = castType.toTypeElement(appView, inTypeLattice.nullability());
assert inTypeLattice.nullability().lessThanOrEqual(outTypeLattice.nullability());
@@ -1586,6 +1586,21 @@
return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
}
+ // If the cast is guaranteed to succeed and only there to ensure the program type checks, then
+ // check if the program would still type check after removing the cast.
+ if (checkCast.isSafeCheckCast()
+ || checkCast
+ .getFirstOperand()
+ .getDynamicType(appViewWithLiveness)
+ .getDynamicUpperBoundType()
+ .lessThanOrEqualUpToNullability(castTypeLattice, appView)) {
+ TypeElement useType =
+ TypeUtils.computeUseType(appViewWithLiveness, context, checkCast.outValue());
+ if (inTypeLattice.lessThanOrEqualUpToNullability(useType, appView)) {
+ return RemoveCheckCastInstructionIfTrivialResult.REMOVED_CAST_DO_NARROW;
+ }
+ }
+
// Otherwise, keep the checkcast to preserve verification errors. E.g., down-cast:
// A < B < C
// c = ... // Even though we know c is of type A,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
index 35e919e..4e6bea5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/DefaultInliningOracle.java
@@ -341,11 +341,6 @@
return true;
}
- if (appInfo.isNeverInlineMethod(singleTargetReference)) {
- whyAreYouNotInliningReporter.reportMarkedAsNeverInline();
- return true;
- }
-
if (appInfo.noSideEffects.containsKey(invoke.getInvokedMethod())
|| appInfo.noSideEffects.containsKey(resolutionResult.getResolvedMethod().getReference())
|| appInfo.noSideEffects.containsKey(singleTargetReference)) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
index 7ef7216..4093846 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/OutlinerImpl.java
@@ -795,6 +795,9 @@
while (index < currentCandidateInstructions.size()) {
processInstruction(currentCandidateInstructions.get(index));
}
+ if (actualInstructions > 0) {
+ candidate(start, index);
+ }
}
// Get int in-values for an instruction. For commutative binary operations using the current
@@ -1448,12 +1451,17 @@
boolean sawLinearFlowWithCatchHandlers = false;
while (instructionIterator.hasNext()) {
Instruction instruction = instructionIterator.next();
- // Disregard linear flow when there are catch handlers
- if (instruction.getBlock() != block
- && (block.hasCatchHandlers() || instruction.getBlock().hasCatchHandlers())) {
- lastSeenBlock = instruction.getBlock();
- sawLinearFlowWithCatchHandlers = true;
- break;
+ if (instruction.getBlock() != block) {
+ // Disregard linear flow when there are catch handlers
+ if (block.hasCatchHandlers() || instruction.getBlock().hasCatchHandlers()) {
+ lastSeenBlock = instruction.getBlock();
+ sawLinearFlowWithCatchHandlers = true;
+ break;
+ }
+ // Disregard revisiting already processed blocks.
+ if (seenBlocks.contains(instruction.getBlock())) {
+ break;
+ }
}
builder.add(instruction);
counter++;
@@ -1464,6 +1472,7 @@
}
lastSeenBlock = instruction.getBlock();
}
+ // Add all seen blocks including trivial goto blocks skipped by the linear iterator.
seenBlocks.addAll(instructionIterator.getSeenBlocks());
if (sawLinearFlowWithCatchHandlers) {
assert lastSeenBlock != block;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
index d317655..056d9c6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/InlineCandidateProcessor.java
@@ -1224,6 +1224,9 @@
.isPossiblyFalse()) {
return false;
}
+ if (!appView.getKeepInfo(singleTarget).isClassInliningAllowed(appView.options())) {
+ return false;
+ }
if (!singleTarget
.getDefinition()
.isInliningCandidate(
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
index 5403b86..4aceb17 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/constraint/ConditionalClassInlinerMethodConstraint.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfo;
import com.android.tools.r8.graph.RewrittenPrototypeDescription.ArgumentInfoCollection;
-import com.android.tools.r8.graph.RewrittenPrototypeDescription.RewrittenTypeInfo;
import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.analysis.value.SingleConstValue;
import com.android.tools.r8.ir.analysis.value.objectstate.ObjectState;
@@ -51,11 +50,10 @@
// usages of that parameter for class inlining.
return;
}
- if (argumentInfo.isRewrittenTypeInfo()) {
+ if (argumentInfo.isRewrittenTypeInfo()
+ && argumentInfo.asRewrittenTypeInfo().getNewType().isIntType()) {
// This is due to enum unboxing. After enum unboxing, we no longer need information
// about the usages of this parameter for class inlining.
- RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
- assert rewrittenTypeInfo.verifyIsDueToUnboxing(appView.dexItemFactory());
return;
}
backing.put(changes.getNewArgumentIndex(argumentIndex), usagePerContext);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
index 1fe7a27..e055f70 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/enums/EnumUnboxingLens.java
@@ -63,11 +63,14 @@
if (unboxedEnums.hasUnboxedValueFor(singleFieldValue.getField())) {
prototypeChanges =
prototypeChanges.withRewrittenReturnInfo(
- new RewrittenTypeInfo(
- rewrittenTypeInfo.getOldType(),
- rewrittenTypeInfo.getNewType(),
- abstractValueFactory.createSingleNumberValue(
- unboxedEnums.getUnboxedValue(singleFieldValue.getField()))));
+ RewrittenTypeInfo.builder()
+ .setCastType(rewrittenTypeInfo.getCastType())
+ .setOldType(rewrittenTypeInfo.getOldType())
+ .setNewType(rewrittenTypeInfo.getNewType())
+ .setSingleValue(
+ abstractValueFactory.createSingleNumberValue(
+ unboxedEnums.getUnboxedValue(singleFieldValue.getField())))
+ .build());
}
}
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index 2ee5a6e..e855aa1 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -119,6 +119,10 @@
return constants.getOrDefault(argIndex, UnknownValue.getInstance());
}
+ public Nullability getNullability(int argIndex) {
+ return getDynamicType(argIndex).getNullability();
+ }
+
public static CallSiteOptimizationInfo fromMethodState(
AppView<AppInfoWithLiveness> appView,
ProgramMethod method,
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
index 62db1f4..2cb900b 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/MutableMethodOptimizationInfo.java
@@ -445,7 +445,13 @@
}
public MutableMethodOptimizationInfo fixupUnusedArguments(MethodOptimizationInfoFixer fixer) {
- unusedArguments = fixer.fixupUnusedArguments(unusedArguments);
+ fixupUnusedArguments(fixer.fixupUnusedArguments(unusedArguments));
+ return this;
+ }
+
+ public MutableMethodOptimizationInfo fixupUnusedArguments(BitSet unusedArguments) {
+ this.unusedArguments =
+ unusedArguments != null && !unusedArguments.isEmpty() ? unusedArguments : null;
return this;
}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
index c6cd18d..a763db6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/OptimizationFeedbackSimple.java
@@ -210,6 +210,16 @@
method.getDefinition().getMutableOptimizationInfo().setUnusedArguments(unusedArguments);
}
+ public void fixupUnusedArguments(ProgramMethod method, Consumer<BitSet> fixer) {
+ if (method.getOptimizationInfo().hasUnusedArguments()) {
+ MutableMethodOptimizationInfo optimizationInfo =
+ method.getDefinition().getMutableOptimizationInfo();
+ BitSet newUnusedArguments = (BitSet) optimizationInfo.getUnusedArguments().clone();
+ fixer.accept(newUnusedArguments);
+ optimizationInfo.fixupUnusedArguments(newUnusedArguments);
+ }
+ }
+
// Unset methods.
@Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
index 3a9c668..537150e 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/inliner/DefaultInliningReasonStrategy.java
@@ -39,7 +39,7 @@
DexEncodedMethod targetMethod = target.getDefinition();
DexMethod targetReference = target.getReference();
if (targetMethod.getOptimizationInfo().forceInline()) {
- assert !appView.appInfo().isNeverInlineMethod(targetReference);
+ assert appView.getKeepInfo(target).isInliningAllowed(appView.options());
return Reason.FORCE;
}
if (appView.appInfo().hasLiveness()
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
index 13f384e..8c6a531 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/typechecks/CheckCastAndInstanceOfMethodSpecialization.java
@@ -142,6 +142,7 @@
parentMethodDefinition.getCode().buildIR(parentMethod, appView, parentMethod.getOrigin());
converter.markProcessed(code, feedback);
// Fixup method optimization info (the method no longer returns a constant).
+ feedback.fixupUnusedArguments(parentMethod, unusedArguments -> unusedArguments.clear(0));
feedback.unsetAbstractReturnValue(parentMethod);
feedback.unsetClassInlinerMethodConstraint(parentMethod);
} else {
diff --git a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
similarity index 69%
rename from src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
rename to src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
index e1ba3b2..4847e78 100644
--- a/src/main/java/com/android/tools/r8/ir/synthetic/EmulateInterfaceSyntheticCfCodeProvider.java
+++ b/src/main/java/com/android/tools/r8/ir/synthetic/EmulateDispatchSyntheticCfCodeProvider.java
@@ -1,4 +1,4 @@
-// Copyright (c) 2019, the R8 project authors. Please see the AUTHORS file
+// Copyright (c) 2022, 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.
@@ -28,24 +28,23 @@
import java.util.List;
import org.objectweb.asm.Opcodes;
-public class EmulateInterfaceSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
+public class EmulateDispatchSyntheticCfCodeProvider extends SyntheticCfCodeProvider {
- private final DexType interfaceType;
- private final DexMethod companionMethod;
- private final DexMethod libraryMethod;
+ private final DexType receiverType;
+ private final DexMethod forwardingMethod;
+ private final DexMethod interfaceMethod;
private final List<Pair<DexType, DexMethod>> extraDispatchCases;
- public EmulateInterfaceSyntheticCfCodeProvider(
+ public EmulateDispatchSyntheticCfCodeProvider(
DexType holder,
- DexType interfaceType,
- DexMethod companionMethod,
- DexMethod libraryMethod,
+ DexMethod forwardingMethod,
+ DexMethod interfaceMethod,
List<Pair<DexType, DexMethod>> extraDispatchCases,
AppView<?> appView) {
super(appView, holder);
- this.interfaceType = interfaceType;
- this.companionMethod = companionMethod;
- this.libraryMethod = libraryMethod;
+ this.receiverType = forwardingMethod.getParameter(0);
+ this.forwardingMethod = forwardingMethod;
+ this.interfaceMethod = interfaceMethod;
this.extraDispatchCases = extraDispatchCases;
}
@@ -60,22 +59,22 @@
ImmutableInt2ReferenceSortedMap.Builder<FrameType> localsBuilder =
ImmutableInt2ReferenceSortedMap.builder();
- localsBuilder.put(0, FrameType.initialized(interfaceType));
+ localsBuilder.put(0, FrameType.initialized(receiverType));
int index = 1;
- for (DexType param : libraryMethod.proto.parameters.values) {
+ for (DexType param : interfaceMethod.proto.parameters.values) {
localsBuilder.put(index++, FrameType.initialized(param));
}
ImmutableInt2ReferenceSortedMap<FrameType> locals = localsBuilder.build();
- instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
- instructions.add(new CfInstanceOf(libraryMethod.holder));
+ instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
+ instructions.add(new CfInstanceOf(interfaceMethod.holder));
instructions.add(new CfIf(If.Type.EQ, ValueType.INT, labels[nextLabel]));
// Branch with library call.
- instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
- instructions.add(new CfCheckCast(libraryMethod.holder));
+ instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
+ instructions.add(new CfCheckCast(interfaceMethod.holder));
loadExtraParameters(instructions);
- instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, libraryMethod, true));
+ instructions.add(new CfInvoke(Opcodes.INVOKEINTERFACE, interfaceMethod, true));
addReturn(instructions);
// SubInterface dispatch (subInterfaces are ordered).
@@ -83,12 +82,12 @@
// Type check basic block.
instructions.add(labels[nextLabel++]);
instructions.add(new CfFrame(locals, ImmutableDeque.of()));
- instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
+ instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
instructions.add(new CfInstanceOf(dispatch.getFirst()));
instructions.add(new CfIf(If.Type.EQ, ValueType.INT, labels[nextLabel]));
// Call basic block.
- instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
+ instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
instructions.add(new CfCheckCast(dispatch.getFirst()));
loadExtraParameters(instructions);
instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, dispatch.getSecond(), false));
@@ -98,25 +97,25 @@
// Branch with companion call.
instructions.add(labels[nextLabel]);
instructions.add(new CfFrame(locals, ImmutableDeque.of()));
- instructions.add(new CfLoad(ValueType.fromDexType(interfaceType), 0));
+ instructions.add(new CfLoad(ValueType.fromDexType(receiverType), 0));
loadExtraParameters(instructions);
- instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, companionMethod, false));
+ instructions.add(new CfInvoke(Opcodes.INVOKESTATIC, forwardingMethod, false));
addReturn(instructions);
return standardCfCodeFromInstructions(instructions);
}
private void loadExtraParameters(List<CfInstruction> instructions) {
int index = 1;
- for (DexType type : libraryMethod.proto.parameters.values) {
+ for (DexType type : interfaceMethod.proto.parameters.values) {
instructions.add(new CfLoad(ValueType.fromDexType(type), index++));
}
}
private void addReturn(List<CfInstruction> instructions) {
- if (libraryMethod.proto.returnType == appView.dexItemFactory().voidType) {
+ if (interfaceMethod.proto.returnType == appView.dexItemFactory().voidType) {
instructions.add(new CfReturnVoid());
} else {
- instructions.add(new CfReturn(ValueType.fromDexType(libraryMethod.proto.returnType)));
+ instructions.add(new CfReturn(ValueType.fromDexType(interfaceMethod.proto.returnType)));
}
}
}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
index f948457..70e6d04 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNameMinifier.java
@@ -6,7 +6,6 @@
import static com.android.tools.r8.graph.DexProgramClass.asProgramClassOrNull;
import com.android.tools.r8.graph.AppView;
-import com.android.tools.r8.graph.BottomUpClassHierarchyTraversal;
import com.android.tools.r8.graph.DexClass;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexField;
@@ -19,12 +18,15 @@
import com.android.tools.r8.graph.SubtypingInfo;
import com.android.tools.r8.graph.TopDownClassHierarchyTraversal;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
+import com.android.tools.r8.utils.SetUtils;
import com.android.tools.r8.utils.Timing;
+import com.android.tools.r8.utils.TraversalContinuation;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Comparator;
import java.util.Deque;
import java.util.IdentityHashMap;
import java.util.List;
@@ -37,8 +39,12 @@
private final AppView<AppInfoWithLiveness> appView;
private final SubtypingInfo subtypingInfo;
private final Map<DexField, DexString> renaming = new IdentityHashMap<>();
- private Map<DexType, ReservedFieldNamingState> reservedNamingStates = new IdentityHashMap<>();
+ private final Map<DexType, ReservedFieldNamingState> reservedNamingStates =
+ new IdentityHashMap<>();
private final MemberNamingStrategy strategy;
+ private final Map<DexType, DexType> frontiers = new IdentityHashMap<>();
+ private final Map<DexType, Set<ReservedFieldNamingState>> frontierStatesForInterfaces =
+ new IdentityHashMap<>();
FieldNameMinifier(
AppView<AppInfoWithLiveness> appView,
@@ -60,8 +66,8 @@
// Rename the definitions.
timing.begin("rename-definitions");
renameFieldsInInterfaces(interfaces);
- propagateReservedFieldNamesUpwards();
renameFieldsInClasses();
+ renameFieldsInUnrelatedClasspathClasses();
timing.end();
// Rename the references that are not rebound to definitions for some reasons.
timing.begin("rename-references");
@@ -93,19 +99,36 @@
}
private void reserveFieldNames() {
- // Reserve all field names that need to be reserved.
- appView
- .appInfo()
- .forEachTypeInHierarchyOfLiveProgramClasses(
+ // Build up all reservations in the class hierarchy such that all reserved names are placed
+ // at the boundary between a library class and a program class - referred to as the frontier.
+ // Special handling is done for interfaces by always considering them to be roots. When
+ // traversing down the hierarchy we built up a map from interface to reservation states:
+ // - when we reach a frontier find all directly and indirectly implemented interfaces and
+ // add the current reservation state
+ // - when we see a program class that implements a direct super type that is an interface also
+ // add the current reservation state. Note that even though we do not visit super interfaces
+ // here, this still works because a super interface will be in the same partition.
+ // For an in depth description see MethodNameMinifier.
+ TopDownClassHierarchyTraversal.forAllClasses(appView)
+ .visit(
+ appView.appInfo().classes(),
clazz -> {
- ReservedFieldNamingState reservedNames = null;
+ DexType frontier =
+ clazz.superType == null
+ ? appView.dexItemFactory().objectType
+ : frontiers.getOrDefault(clazz.superType, clazz.type);
+ // If frontier != clazz.type we have seen a program class that is on the boundary.
+ // Otherwise, if we are visiting a program class then that is the frontier.
+ if (frontier != clazz.type || clazz.isProgramClass()) {
+ DexType existingValue = frontiers.put(clazz.type, frontier);
+ assert existingValue == null;
+ }
+ ReservedFieldNamingState reservationState =
+ getOrCreateReservedFieldNamingState(frontier);
for (DexEncodedField field : clazz.fields()) {
DexString reservedName = strategy.getReservedName(field, clazz);
if (reservedName != null) {
- if (reservedNames == null) {
- reservedNames = getOrCreateReservedFieldNamingState(clazz.type);
- }
- reservedNames.markReservedDirectly(
+ reservationState.markReserved(
reservedName, field.getReference().name, field.getReference().type);
// TODO(b/148846065): Consider lazily computing the renaming on actual lookups.
if (reservedName != field.getReference().name) {
@@ -113,51 +136,47 @@
}
}
}
-
- // For interfaces, propagate reserved names to all implementing classes.
- if (clazz.isInterface() && reservedNames != null) {
- for (DexType implementationType :
- subtypingInfo.allImmediateImplementsSubtypes(clazz.type)) {
- DexClass implementation = appView.definitionFor(implementationType);
- if (implementation != null) {
- assert !implementation.isInterface();
- getOrCreateReservedFieldNamingState(implementationType)
- .includeReservations(reservedNames);
+ if (clazz.isInterface()) {
+ frontierStatesForInterfaces.put(
+ clazz.type, SetUtils.newIdentityHashSet(reservationState));
+ }
+ // Include all reservations from super frontier states. This will propagate reserved
+ // names for interfaces down to implementing subtypes.
+ for (DexType superType : clazz.allImmediateSupertypes()) {
+ // No need to visit object since there are no fields there.
+ if (superType != appView.dexItemFactory().objectType) {
+ ReservedFieldNamingState superReservationState =
+ getOrCreateReservedFieldNamingState(
+ frontiers.getOrDefault(superType, superType));
+ if (superReservationState != reservationState) {
+ reservationState.includeReservations(superReservationState);
+ }
+ if (clazz.isProgramClass()) {
+ DexClass superClass = appView.definitionFor(superType, clazz.asProgramClass());
+ if (superClass != null && superClass.isInterface()) {
+ frontierStatesForInterfaces.get(superType).add(reservationState);
+ }
}
}
}
- });
-
- // TODO(b/148846065): Consider lazily computing the renaming on actual lookups.
- appView
- .appInfo()
- .forEachReferencedClasspathClass(
- clazz -> {
- for (DexEncodedField field : clazz.fields()) {
- DexString reservedName = strategy.getReservedName(field, clazz);
- if (reservedName != null && reservedName != field.getReference().name) {
- renaming.put(field.getReference(), reservedName);
- }
+ if (frontier == clazz.type && clazz.isProgramClass()) {
+ patchUpAllIndirectlyImplementingInterfacesFromLibraryAndClassPath(
+ clazz.asProgramClass(), reservationState);
}
});
-
- propagateReservedFieldNamesUpwards();
}
- private void propagateReservedFieldNamesUpwards() {
- BottomUpClassHierarchyTraversal.forProgramClasses(appView, subtypingInfo)
- .visit(
- appView.appInfo().classes(),
- clazz -> {
- ReservedFieldNamingState reservedNames = getReservedFieldNamingState(clazz.type);
- if (reservedNames != null) {
- for (DexType supertype : clazz.allImmediateSupertypes()) {
- if (supertype.isProgramType(appView)) {
- getOrCreateReservedFieldNamingState(supertype)
- .includeReservationsFromBelow(reservedNames);
- }
- }
+ private void patchUpAllIndirectlyImplementingInterfacesFromLibraryAndClassPath(
+ DexProgramClass clazz, ReservedFieldNamingState reservationState) {
+ appView
+ .appInfo()
+ .traverseSuperTypes(
+ clazz,
+ (superType, superClass, isInterface) -> {
+ if (isInterface && superClass.isNotProgramClass()) {
+ frontierStatesForInterfaces.get(superType).add(reservationState);
}
+ return TraversalContinuation.CONTINUE;
});
}
@@ -179,8 +198,7 @@
.clone();
ReservedFieldNamingState reservedNames =
- getOrCreateReservedFieldNamingState(clazz.type);
- // TODO(b/213041051): This could avoid duplication of strings downwards.
+ getReservedFieldNamingState(frontiers.getOrDefault(clazz.type, clazz.type));
FieldNamingState state = parentState.createChildState(reservedNames);
if (clazz.isProgramClass()) {
clazz.asProgramClass().forEachProgramField(field -> renameField(field, state));
@@ -191,7 +209,24 @@
});
}
+ private void renameFieldsInUnrelatedClasspathClasses() {
+ if (appView.options().getProguardConfiguration().hasApplyMappingFile()) {
+ appView
+ .appInfo()
+ .forEachReferencedClasspathClass(
+ clazz -> {
+ for (DexEncodedField field : clazz.fields()) {
+ DexString reservedName = strategy.getReservedName(field, clazz);
+ if (reservedName != null && reservedName != field.getReference().name) {
+ renaming.put(field.getReference(), reservedName);
+ }
+ }
+ });
+ }
+ }
+
private void renameFieldsInInterfaces(Collection<DexClass> interfaces) {
+ // TODO(b/213415674): Only consider interfaces in the hierarchy of classes.
InterfacePartitioning partitioning = new InterfacePartitioning(this);
for (Set<DexClass> partition : partitioning.sortedPartitions(interfaces)) {
renameFieldsInInterfacePartition(partition);
@@ -199,18 +234,24 @@
}
private void renameFieldsInInterfacePartition(Set<DexClass> partition) {
- ReservedFieldNamingState reservedNamesInPartition = new ReservedFieldNamingState(appView);
- for (DexClass clazz : partition) {
- ReservedFieldNamingState reservedNamesInInterface = getReservedFieldNamingState(clazz.type);
- if (reservedNamesInInterface != null) {
- reservedNamesInPartition.includeReservations(reservedNamesInInterface);
- reservedNamesInPartition.includeReservationsFromBelow(reservedNamesInInterface);
- }
- }
-
ReservedFieldNamingState namesToBeReservedInImplementsSubclasses =
new ReservedFieldNamingState(appView);
-
+ ReservedFieldNamingState reservedNamesInPartition = new ReservedFieldNamingState(appView);
+ for (DexClass clazz : partition) {
+ ReservedFieldNamingState reservedNamesInInterface =
+ getReservedFieldNamingState(frontiers.getOrDefault(clazz.type, clazz.type));
+ if (reservedNamesInInterface != null) {
+ reservedNamesInPartition.includeReservations(reservedNamesInInterface);
+ Set<ReservedFieldNamingState> reservedFieldNamingStates =
+ frontierStatesForInterfaces.get(clazz.type);
+ assert reservedFieldNamingStates != null;
+ reservedFieldNamingStates.forEach(
+ reservedStates -> {
+ reservedNamesInPartition.includeReservations(reservedStates);
+ reservedStates.setInterfaceMinificationState(namesToBeReservedInImplementsSubclasses);
+ });
+ }
+ }
FieldNamingState state = new FieldNamingState(appView, strategy, reservedNamesInPartition);
for (DexClass clazz : partition) {
if (clazz.isProgramClass()) {
@@ -220,25 +261,11 @@
.forEachProgramField(
field -> {
DexString newName = renameField(field, state);
- namesToBeReservedInImplementsSubclasses.markReservedDirectly(
+ namesToBeReservedInImplementsSubclasses.markReserved(
newName, field.getReference().name, field.getReference().type);
});
}
}
-
- Set<DexType> visited = Sets.newIdentityHashSet();
- for (DexClass clazz : partition) {
- for (DexType implementationType : subtypingInfo.allImmediateImplementsSubtypes(clazz.type)) {
- if (!visited.add(implementationType)) {
- continue;
- }
- DexClass implementation = appView.definitionFor(implementationType);
- if (implementation != null) {
- getOrCreateReservedFieldNamingState(implementationType)
- .setInterfaceMinificationState(namesToBeReservedInImplementsSubclasses);
- }
- }
- }
}
private DexString renameField(ProgramField field, FieldNamingState state) {
@@ -279,12 +306,12 @@
static class InterfacePartitioning {
- private final FieldNameMinifier minfier;
+ private final FieldNameMinifier minifier;
private final AppView<AppInfoWithLiveness> appView;
private final Set<DexType> visited = Sets.newIdentityHashSet();
InterfacePartitioning(FieldNameMinifier minifier) {
- this.minfier = minifier;
+ this.minifier = minifier;
appView = minifier.appView;
}
@@ -303,7 +330,7 @@
}
private Set<DexClass> buildSortedPartition(DexClass src) {
- Set<DexClass> partition = new TreeSet<>((x, y) -> x.type.compareTo(y.type));
+ Set<DexClass> partition = new TreeSet<>(Comparator.comparing(DexClass::getType));
Deque<DexType> worklist = new ArrayDeque<>();
worklist.add(src.type);
@@ -325,7 +352,7 @@
if (clazz.isInterface()) {
partition.add(clazz);
- for (DexType subtype : minfier.subtypingInfo.allImmediateSubtypes(type)) {
+ for (DexType subtype : minifier.subtypingInfo.allImmediateSubtypes(type)) {
if (visited.add(subtype)) {
worklist.add(subtype);
}
@@ -334,7 +361,7 @@
if (visited.add(clazz.superType)) {
worklist.add(clazz.superType);
}
- for (DexType subclass : minfier.subtypingInfo.allImmediateExtendsSubtypes(type)) {
+ for (DexType subclass : minifier.subtypingInfo.allImmediateExtendsSubtypes(type)) {
if (visited.add(subclass)) {
worklist.add(subclass);
}
diff --git a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
index 9e99065..e507112 100644
--- a/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/FieldNamingState.java
@@ -45,10 +45,7 @@
}
public FieldNamingState createChildState(ReservedFieldNamingState reservedNames) {
- FieldNamingState childState =
- new FieldNamingState(appView, strategy, reservedNames, internalStates);
- childState.includeReservations(this.reservedNames);
- return childState;
+ return new FieldNamingState(appView, strategy, reservedNames, internalStates);
}
public DexString getOrCreateNameFor(ProgramField field) {
diff --git a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
index 7e1c7df..cd6c271 100644
--- a/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
+++ b/src/main/java/com/android/tools/r8/naming/MethodNameMinifier.java
@@ -20,7 +20,6 @@
import com.google.common.collect.HashBiMap;
import com.google.common.collect.ImmutableMap;
import java.util.ArrayList;
-import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
@@ -134,7 +133,7 @@
// The use of a bidirectional map allows us to map a naming state to the type it represents,
// which is useful for debugging.
private final BiMap<DexType, MethodReservationState<?>> reservationStates = HashBiMap.create();
- private final Map<DexType, MethodNamingState<?>> namingStates = new HashMap<>();
+ private final Map<DexType, MethodNamingState<?>> namingStates = new IdentityHashMap<>();
private final Map<DexType, DexType> frontiers = new IdentityHashMap<>();
private final MethodNamingState<?> rootNamingState;
diff --git a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
index 258646e..2b66d18 100644
--- a/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
+++ b/src/main/java/com/android/tools/r8/naming/ReservedFieldNamingState.java
@@ -43,8 +43,8 @@
return internalState == null ? null : internalState.getReservedByName(name);
}
- void markReservedDirectly(DexString name, DexString originalName, DexType type) {
- getOrCreateInternalState(type).markReservedDirectly(name, originalName);
+ void markReserved(DexString name, DexString originalName, DexType type) {
+ getOrCreateInternalState(type).markReserved(name, originalName);
}
void includeReservations(ReservedFieldNamingState reservedNames) {
@@ -54,13 +54,6 @@
includeInterfaceReservationState(reservedNames);
}
- void includeReservationsFromBelow(ReservedFieldNamingState reservedNames) {
- for (Map.Entry<DexType, InternalState> entry : reservedNames.internalStates.entrySet()) {
- getOrCreateInternalState(entry.getKey()).includeReservationsFromBelow(entry.getValue());
- }
- includeInterfaceReservationState(reservedNames);
- }
-
private void includeInterfaceReservationState(ReservedFieldNamingState reservedNames) {
if (reservedNames.interfaceMinificationState != null) {
assert interfaceMinificationState == null
@@ -71,7 +64,7 @@
void setInterfaceMinificationState(ReservedFieldNamingState namingState) {
assert namingState != null;
- assert interfaceMinificationState == null;
+ assert interfaceMinificationState == null || interfaceMinificationState == namingState;
this.interfaceMinificationState = namingState;
}
@@ -82,25 +75,19 @@
static class InternalState {
- private Map<DexString, DexString> reservedNamesDirect = new IdentityHashMap<>();
- private Map<DexString, DexString> reservedNamesBelow = new IdentityHashMap<>();
+ private final Map<DexString, DexString> reservedNames = new IdentityHashMap<>();
DexString getReservedByName(DexString name) {
- DexString reservedBy = reservedNamesDirect.get(name);
- return reservedBy != null ? reservedBy : reservedNamesBelow.get(name);
+ DexString reservedBy = reservedNames.get(name);
+ return reservedBy != null ? reservedBy : reservedNames.get(name);
}
- void markReservedDirectly(DexString name, DexString originalName) {
- reservedNamesDirect.put(name, originalName);
+ void markReserved(DexString name, DexString originalName) {
+ reservedNames.put(name, originalName);
}
void includeReservations(InternalState state) {
- reservedNamesDirect.putAll(state.reservedNamesDirect);
- }
-
- void includeReservationsFromBelow(InternalState state) {
- reservedNamesBelow.putAll(state.reservedNamesDirect);
- reservedNamesBelow.putAll(state.reservedNamesBelow);
+ reservedNames.putAll(state.reservedNames);
}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
index 4764635..5f43dd3 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagator.java
@@ -107,7 +107,7 @@
reprocessingCriteriaCollection.analyzeArgumentUses(method, code);
} else {
assert !methodProcessor.isPrimaryMethodProcessor();
- assert reprocessingCriteriaCollection == null;
+ assert !methodProcessor.isPostMethodProcessor() || reprocessingCriteriaCollection == null;
}
}
@@ -170,9 +170,10 @@
.run(stronglyConnectedProgramComponents, affectedClasses::add, executorService, timing);
// Find all the code objects that need reprocessing.
- new ArgumentPropagatorMethodReprocessingEnqueuer(appView)
+ new ArgumentPropagatorMethodReprocessingEnqueuer(appView, reprocessingCriteriaCollection)
.enqueueMethodForReprocessing(
graphLens, postMethodProcessorBuilder, executorService, timing);
+ reprocessingCriteriaCollection = null;
// Finally, apply the graph lens to the program (i.e., remove constant parameters from method
// definitions).
@@ -209,7 +210,6 @@
stronglyConnectedProgramComponents,
interfaceDispatchOutsideProgram)
.populateOptimizationInfo(converter, executorService, timing);
- reprocessingCriteriaCollection = null;
timing.end();
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
index 897aaa8..7c196b1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorApplicationFixer.java
@@ -93,9 +93,11 @@
return method.toTypeSubstitutedMethod(
methodReferenceAfterParameterRemoval,
builder -> {
- RewrittenPrototypeDescription prototypeChanges =
- graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
- builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+ if (graphLens.hasPrototypeChanges(methodReferenceAfterParameterRemoval)) {
+ RewrittenPrototypeDescription prototypeChanges =
+ graphLens.getPrototypeChanges(methodReferenceAfterParameterRemoval);
+ builder.apply(prototypeChanges.createParameterAnnotationsRemover(method));
+ }
});
});
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
index eef092d..2839aeb 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorCodeScanner.java
@@ -502,12 +502,6 @@
// then use UnknownParameterState.
if (parameterTypeElement.isClassType()) {
DynamicType dynamicType = argument.getDynamicType(appView);
- if (!parameterReprocessingCriteria.shouldReprocessDueToDynamicType()) {
- dynamicType =
- parameterReprocessingCriteria.widenDynamicClassType(
- appView, dynamicType, parameterTypeElement.asClassType());
- }
-
DynamicType widenedDynamicType =
WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, parameterType);
return abstractValue.isUnknown() && widenedDynamicType.isUnknown()
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
index 3ed1334..17865e4 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorGraphLens.java
@@ -35,7 +35,7 @@
}
public boolean hasPrototypeChanges(DexMethod method) {
- return method != internalGetPreviousMethodSignature(method);
+ return prototypeChanges.containsKey(method);
}
public RewrittenPrototypeDescription getPrototypeChanges(DexMethod method) {
@@ -43,6 +43,10 @@
return prototypeChanges.getOrDefault(method, RewrittenPrototypeDescription.none());
}
+ public boolean isAffected(DexMethod method) {
+ return method != internalGetPreviousMethodSignature(method) || hasPrototypeChanges(method);
+ }
+
@Override
protected FieldLookupResult internalDescribeLookupField(FieldLookupResult previous) {
FieldLookupResult lookupResult = super.internalDescribeLookupField(previous);
@@ -61,8 +65,7 @@
protected RewrittenPrototypeDescription internalDescribePrototypeChanges(
RewrittenPrototypeDescription prototypeChanges, DexMethod method) {
DexMethod previous = internalGetPreviousMethodSignature(method);
- if (previous == method) {
- assert !this.prototypeChanges.containsKey(method);
+ if (!hasPrototypeChanges(method)) {
return prototypeChanges;
}
RewrittenPrototypeDescription newPrototypeChanges =
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
index f3e9000..d79d23e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorMethodReprocessingEnqueuer.java
@@ -19,6 +19,7 @@
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.conversion.PostMethodProcessor;
import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
+import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ThreadUtils;
import com.android.tools.r8.utils.Timing;
@@ -34,9 +35,13 @@
public class ArgumentPropagatorMethodReprocessingEnqueuer {
private final AppView<AppInfoWithLiveness> appView;
+ private final ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection;
- public ArgumentPropagatorMethodReprocessingEnqueuer(AppView<AppInfoWithLiveness> appView) {
+ public ArgumentPropagatorMethodReprocessingEnqueuer(
+ AppView<AppInfoWithLiveness> appView,
+ ArgumentPropagatorReprocessingCriteriaCollection reprocessingCriteriaCollection) {
this.appView = appView;
+ this.reprocessingCriteriaCollection = reprocessingCriteriaCollection;
}
/**
@@ -61,7 +66,7 @@
timing.end();
timing.begin("Enqueue methods with non-trivial info");
- enqueueMethodsWithNonTrivialOptimizationInfo(methodsToReprocessBuilder);
+ enqueueAffectedCallees(graphLens, methodsToReprocessBuilder);
timing.end();
timing.begin("Enqueue affected methods");
@@ -73,16 +78,34 @@
timing.end();
}
- private void enqueueMethodsWithNonTrivialOptimizationInfo(
+ private void enqueueAffectedCallees(
+ ArgumentPropagatorGraphLens graphLens,
LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder) {
GraphLens currentGraphLens = appView.graphLens();
for (DexProgramClass clazz : appView.appInfo().classes()) {
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
+ if (method.getDefinition().getCode().isSharedCodeObject()) {
+ return;
+ }
+
+ if (graphLens != null) {
+ DexMethod rewrittenMethodSignature =
+ graphLens.internalGetNextMethodSignature(method.getReference());
+ if (graphLens.hasPrototypeChanges(rewrittenMethodSignature)) {
+ assert !appView.appInfo().isNeverReprocessMethod(method);
+ methodsToReprocessBuilder.add(method, currentGraphLens);
+ appView.testing().callSiteOptimizationInfoInspector.accept(method);
+ return;
+ }
+ }
+
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- if (callSiteOptimizationInfo.isConcreteCallSiteOptimizationInfo()
+ if (reprocessingCriteriaCollection
+ .getReprocessingCriteria(method)
+ .shouldReprocess(appView, method, callSiteOptimizationInfo)
&& !appView.appInfo().isNeverReprocessMethod(method)) {
methodsToReprocessBuilder.add(method, currentGraphLens);
appView.testing().callSiteOptimizationInfoInspector.accept(method);
@@ -99,6 +122,7 @@
LongLivedProgramMethodSetBuilder<ProgramMethodSet> methodsToReprocessBuilder,
ExecutorService executorService)
throws ExecutionException {
+ GraphLens currentGraphLens = appView.graphLens();
Collection<List<ProgramMethod>> methodsToReprocess =
ThreadUtils.processItemsWithResults(
appView.appInfo().classes(),
@@ -107,12 +131,7 @@
clazz.forEachProgramMethodMatching(
DexEncodedMethod::hasCode,
method -> {
- if (graphLens.internalGetNextMethodSignature(method.getReference())
- != method.getReference()) {
- if (!method.getOptimizationInfo().hasBeenInlinedIntoSingleCallSite()) {
- methodsToReprocessInClass.add(method);
- }
- } else {
+ if (!methodsToReprocessBuilder.contains(method, currentGraphLens)) {
AffectedMethodUseRegistry registry =
new AffectedMethodUseRegistry(appView, method, graphLens);
if (method.registerCodeReferencesWithResult(registry)) {
@@ -124,10 +143,9 @@
return methodsToReprocessInClass;
},
executorService);
- GraphLens currentGraphLens = appView.graphLens();
methodsToReprocess.forEach(
- methodsToReprocessForClass ->
- methodsToReprocessBuilder.addAll(methodsToReprocessForClass, currentGraphLens));
+ methodsToReprocessInClass ->
+ methodsToReprocessBuilder.addAll(methodsToReprocessInClass, currentGraphLens));
}
static class AffectedMethodUseRegistry extends UseRegistryWithResult<Boolean, ProgramMethod> {
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
index 94da691..f9bfde1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorOptimizationInfoPopulator.java
@@ -14,6 +14,8 @@
import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
+import com.android.tools.r8.ir.analysis.value.AbstractValue;
import com.android.tools.r8.ir.conversion.IRConverter;
import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.ir.optimize.info.MethodOptimizationInfo;
@@ -22,17 +24,16 @@
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteClassTypeParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodStateCollectionByReference;
import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
+import com.android.tools.r8.optimize.argumentpropagation.codescanner.StateCloner;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InParameterFlowPropagator;
import com.android.tools.r8.optimize.argumentpropagation.propagation.InterfaceMethodArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.propagation.VirtualDispatchMethodArgumentPropagator;
import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.ArgumentPropagatorReprocessingCriteriaCollection;
-import com.android.tools.r8.optimize.argumentpropagation.reprocessingcriteria.MethodReprocessingCriteria;
import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
import com.android.tools.r8.utils.ListUtils;
@@ -45,7 +46,6 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.BiConsumer;
-import java.util.stream.IntStream;
/**
* Propagates the argument flow information collected by the {@link ArgumentPropagatorCodeScanner}.
@@ -187,6 +187,14 @@
ConcreteMonomorphicMethodState monomorphicMethodState = concreteMethodState.asMonomorphic();
+ // Widen the dynamic type information so that we don't store any trivial dynamic types.
+ // Note that all dynamic types are already being widened when the method states are created, but
+ // this does not guarantee that they are non-trivial at this point, since we may refine the
+ // object allocation info collection during the primary optimization pass.
+ if (!widenDynamicTypes(method, monomorphicMethodState)) {
+ return;
+ }
+
// Verify that there is no parameter with bottom info.
assert monomorphicMethodState.getParameterStates().stream().noneMatch(ParameterState::isBottom);
@@ -196,50 +204,18 @@
.map(ParameterState::asConcrete)
.noneMatch(ConcreteParameterState::hasInParameters);
- // Verify that the dynamic type information is correct.
- assert IntStream.range(0, monomorphicMethodState.getParameterStates().size())
- .filter(
- index -> {
- ParameterState parameterState = monomorphicMethodState.getParameterState(index);
- return parameterState.isConcrete() && parameterState.asConcrete().isClassParameter();
- })
- .allMatch(
- index -> {
- DynamicType dynamicType =
- monomorphicMethodState
- .getParameterState(index)
- .asConcrete()
- .asClassParameter()
- .getDynamicType();
- DexType staticType = method.getArgumentType(index);
- assert dynamicType.isUnknown()
- || !WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType)
- .isUnknown();
- return true;
- });
-
- // If we have any reprocessing criteria for the given method, check that they are satisfied
- // before reenqueing.
- MethodReprocessingCriteria reprocessingCriteria =
- reprocessingCriteriaCollection.getReprocessingCriteria(method);
- ConcreteMonomorphicMethodStateOrUnknown widenedMethodState =
- reprocessingCriteria.widenMethodState(appView, method, monomorphicMethodState);
- if (widenedMethodState.isUnknown()) {
- return;
- }
-
- ConcreteMonomorphicMethodState finalMethodState = widenedMethodState.asMonomorphic();
getSimpleFeedback()
.setArgumentInfos(
method,
- ConcreteCallSiteOptimizationInfo.fromMethodState(appView, method, finalMethodState));
+ ConcreteCallSiteOptimizationInfo.fromMethodState(
+ appView, method, monomorphicMethodState));
// Strengthen the return value of the method if the method is known to return one of the
// arguments.
MethodOptimizationInfo optimizationInfo = method.getOptimizationInfo();
if (optimizationInfo.returnsArgument()) {
ParameterState returnedArgumentState =
- finalMethodState.getParameterState(optimizationInfo.getReturnedArgument());
+ monomorphicMethodState.getParameterState(optimizationInfo.getReturnedArgument());
OptimizationFeedback.getSimple()
.methodReturnsAbstractValue(
method.getDefinition(), appView, returnedArgumentState.getAbstractValue(appView));
@@ -328,4 +304,44 @@
appView.abstractValueFactory().createZeroValue());
}
}
+
+ private boolean widenDynamicTypes(
+ ProgramMethod method, ConcreteMonomorphicMethodState methodState) {
+ for (int argumentIndex = 0;
+ argumentIndex < methodState.getParameterStates().size();
+ argumentIndex++) {
+ ConcreteParameterState parameterState =
+ methodState.getParameterState(argumentIndex).asConcrete();
+ if (parameterState == null || !parameterState.isClassParameter()) {
+ continue;
+ }
+ DynamicType dynamicType = parameterState.asClassParameter().getDynamicType();
+ DexType staticType = method.getArgumentType(argumentIndex);
+ if (shouldWidenDynamicTypeToUnknown(dynamicType, staticType)) {
+ methodState.setParameterState(
+ argumentIndex,
+ parameterState.mutableJoin(
+ appView,
+ new ConcreteClassTypeParameterState(AbstractValue.bottom(), DynamicType.unknown()),
+ staticType,
+ StateCloner.getIdentity()));
+ }
+ }
+ return !methodState.isEffectivelyUnknown();
+ }
+
+ private boolean shouldWidenDynamicTypeToUnknown(DynamicType dynamicType, DexType staticType) {
+ if (dynamicType.isUnknown()) {
+ return false;
+ }
+ if (WideningUtils.widenDynamicNonReceiverType(appView, dynamicType, staticType).isUnknown()) {
+ return true;
+ }
+ TypeElement staticTypeElement = staticType.toTypeElement(appView);
+ TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticTypeElement);
+ if (!dynamicUpperBoundType.lessThanOrEqual(staticTypeElement, appView)) {
+ return true;
+ }
+ return false;
+ }
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
index 6b94973..4890dd9 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/ArgumentPropagatorProgramOptimizer.java
@@ -36,7 +36,6 @@
import com.android.tools.r8.shaking.KeepFieldInfo;
import com.android.tools.r8.utils.AccessUtils;
import com.android.tools.r8.utils.BooleanBox;
-import com.android.tools.r8.utils.BooleanUtils;
import com.android.tools.r8.utils.IntBox;
import com.android.tools.r8.utils.InternalOptions;
import com.android.tools.r8.utils.Pair;
@@ -47,7 +46,11 @@
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceMaps;
+import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntArraySet;
+import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
import it.unimi.dsi.fastutil.ints.IntSet;
import it.unimi.dsi.fastutil.ints.IntSets;
import java.util.ArrayList;
@@ -57,11 +60,13 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;
+import java.util.function.IntFunction;
import java.util.function.IntPredicate;
public class ArgumentPropagatorProgramOptimizer {
@@ -69,22 +74,45 @@
static class AllowedPrototypeChanges {
private static final AllowedPrototypeChanges EMPTY =
- new AllowedPrototypeChanges(false, IntSets.EMPTY_SET);
+ new AllowedPrototypeChanges(null, Int2ReferenceMaps.emptyMap(), IntSets.EMPTY_SET);
- boolean canRewriteToVoid;
+ DexType newReturnType;
+ Int2ReferenceMap<DexType> newParameterTypes;
IntSet removableParameterIndices;
- AllowedPrototypeChanges(boolean canRewriteToVoid, IntSet removableParameterIndices) {
- this.canRewriteToVoid = canRewriteToVoid;
+ AllowedPrototypeChanges(
+ DexType newReturnType,
+ Int2ReferenceMap<DexType> newParameterTypes,
+ IntSet removableParameterIndices) {
+ this.newReturnType = newReturnType;
+ this.newParameterTypes = newParameterTypes;
this.removableParameterIndices = removableParameterIndices;
}
public static AllowedPrototypeChanges create(RewrittenPrototypeDescription prototypeChanges) {
- return prototypeChanges.isEmpty()
- ? empty()
- : new AllowedPrototypeChanges(
- prototypeChanges.hasBeenChangedToReturnVoid(),
- prototypeChanges.getArgumentInfoCollection().getKeys());
+ if (prototypeChanges.isEmpty()) {
+ return empty();
+ }
+ DexType newReturnType =
+ prototypeChanges.hasRewrittenReturnInfo()
+ ? prototypeChanges.getRewrittenReturnInfo().getNewType()
+ : null;
+ Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
+ IntSet removableParameterIndices = new IntOpenHashSet();
+ prototypeChanges
+ .getArgumentInfoCollection()
+ .forEach(
+ (argumentIndex, argumentInfo) -> {
+ if (argumentInfo.isRemovedArgumentInfo()) {
+ removableParameterIndices.add(argumentIndex);
+ } else {
+ assert argumentInfo.isRewrittenTypeInfo();
+ RewrittenTypeInfo rewrittenTypeInfo = argumentInfo.asRewrittenTypeInfo();
+ newParameterTypes.put(argumentIndex, rewrittenTypeInfo.getNewType());
+ }
+ });
+ return new AllowedPrototypeChanges(
+ newReturnType, newParameterTypes, removableParameterIndices);
}
public static AllowedPrototypeChanges empty() {
@@ -93,7 +121,7 @@
@Override
public int hashCode() {
- return BooleanUtils.intValue(canRewriteToVoid) | (removableParameterIndices.hashCode() << 1);
+ return Objects.hash(newReturnType, newParameterTypes, removableParameterIndices);
}
@Override
@@ -102,7 +130,8 @@
return false;
}
AllowedPrototypeChanges other = (AllowedPrototypeChanges) obj;
- return canRewriteToVoid == other.canRewriteToVoid
+ return newReturnType == other.newReturnType
+ && newParameterTypes.equals(other.newParameterTypes)
&& removableParameterIndices.equals(other.removableParameterIndices);
}
}
@@ -308,31 +337,46 @@
return;
}
- // Find the parameters that are constant or unused in all methods.
+ if (containsImmediateInterfaceOfInstantiatedLambda(methods)) {
+ return;
+ }
+
+ // Find the parameters that are either (i) the same constant, (ii) all unused, or (iii)
+ // all possible to strengthen to the same stronger type, in all methods.
+ Int2ReferenceMap<DexType> newParameterTypes = new Int2ReferenceOpenHashMap<>();
IntSet removableVirtualMethodParametersInAllMethods = new IntArraySet();
for (int parameterIndex = 1;
parameterIndex < signature.getProto().getArity() + 1;
parameterIndex++) {
- if (canRemoveParameterFromVirtualMethods(parameterIndex, methods)) {
+ if (canRemoveParameterFromVirtualMethods(methods, parameterIndex)) {
removableVirtualMethodParametersInAllMethods.add(parameterIndex);
+ } else {
+ DexType newParameterType =
+ getNewParameterTypeForVirtualMethods(methods, parameterIndex);
+ if (newParameterType != null) {
+ newParameterTypes.put(parameterIndex, newParameterType);
+ }
}
}
// If any prototype changes can be made, record it.
SingleValue returnValueForVirtualMethods =
- getReturnValueForVirtualMethods(signature, methods);
- boolean canRewriteVirtualMethodsToVoid = returnValueForVirtualMethods != null;
- if (canRewriteVirtualMethodsToVoid
+ getReturnValueForVirtualMethods(methods, signature);
+ DexType newReturnType =
+ getNewReturnTypeForVirtualMethods(methods, returnValueForVirtualMethods);
+ if (newReturnType != null
+ || !newParameterTypes.isEmpty()
|| !removableVirtualMethodParametersInAllMethods.isEmpty()) {
allowedPrototypeChangesForVirtualMethods.put(
signature,
new AllowedPrototypeChanges(
- canRewriteVirtualMethodsToVoid,
+ newReturnType,
+ newParameterTypes,
removableVirtualMethodParametersInAllMethods));
}
// Also record the found return value for abstract virtual methods.
- if (canRewriteVirtualMethodsToVoid) {
+ if (newReturnType == dexItemFactory.voidType) {
for (ProgramMethod method : methods) {
if (method.getAccessFlags().isAbstract()) {
returnValuesForVirtualMethods.put(method, returnValueForVirtualMethods);
@@ -370,7 +414,7 @@
}
private SingleValue getReturnValueForVirtualMethods(
- DexMethodSignature signature, ProgramMethodSet methods) {
+ ProgramMethodSet methods, DexMethodSignature signature) {
if (signature.getReturnType().isVoidType()) {
return null;
}
@@ -378,14 +422,6 @@
SingleValue returnValue = null;
for (ProgramMethod method : methods) {
if (method.getDefinition().isAbstract()) {
- DexProgramClass holder = method.getHolder();
- if (holder.isInterface()) {
- ObjectAllocationInfoCollection objectAllocationInfoCollection =
- appView.appInfo().getObjectAllocationInfoCollection();
- if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
- return null;
- }
- }
// OK, this can be rewritten to have void return type.
continue;
}
@@ -403,18 +439,24 @@
return returnValue;
}
+ private boolean containsImmediateInterfaceOfInstantiatedLambda(ProgramMethodSet methods) {
+ for (ProgramMethod method : methods) {
+ DexProgramClass holder = method.getHolder();
+ if (holder.isInterface()) {
+ ObjectAllocationInfoCollection objectAllocationInfoCollection =
+ appView.appInfo().getObjectAllocationInfoCollection();
+ if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
private boolean canRemoveParameterFromVirtualMethods(
- int parameterIndex, ProgramMethodSet methods) {
+ ProgramMethodSet methods, int parameterIndex) {
for (ProgramMethod method : methods) {
if (method.getDefinition().isAbstract()) {
- DexProgramClass holder = method.getHolder();
- if (holder.isInterface()) {
- ObjectAllocationInfoCollection objectAllocationInfoCollection =
- appView.appInfo().getObjectAllocationInfoCollection();
- if (objectAllocationInfoCollection.isImmediateInterfaceOfInstantiatedLambda(holder)) {
- return false;
- }
- }
// OK, this parameter can be removed.
continue;
}
@@ -435,6 +477,48 @@
return true;
}
+ private DexType getNewReturnTypeForVirtualMethods(
+ ProgramMethodSet methods, SingleValue returnValue) {
+ if (returnValue != null) {
+ return dexItemFactory.voidType;
+ }
+ DexType newReturnType = null;
+ for (ProgramMethod method : methods) {
+ if (method.getDefinition().isAbstract()) {
+ // OK, this method can have any return type.
+ continue;
+ }
+ DexType newReturnTypeForMethod = getNewReturnType(method, null);
+ if (newReturnTypeForMethod == null
+ || (newReturnType != null && newReturnType != newReturnTypeForMethod)) {
+ return null;
+ }
+ newReturnType = newReturnTypeForMethod;
+ }
+ assert newReturnType == null || newReturnType != methods.getFirst().getReturnType();
+ return newReturnType;
+ }
+
+ private DexType getNewParameterTypeForVirtualMethods(
+ ProgramMethodSet methods, int parameterIndex) {
+ DexType newParameterType = null;
+ for (ProgramMethod method : methods) {
+ if (method.getAccessFlags().isAbstract()) {
+ // OK, this parameter can have any type.
+ continue;
+ }
+ DexType newParameterTypeForMethod = getNewParameterType(method, parameterIndex);
+ if (newParameterTypeForMethod == null
+ || (newParameterType != null && newParameterType != newParameterTypeForMethod)) {
+ return null;
+ }
+ newParameterType = newParameterTypeForMethod;
+ }
+ assert newParameterType == null
+ || newParameterType != methods.getFirst().getArgumentType(parameterIndex);
+ return newParameterType;
+ }
+
// Returns true if the class was changed as a result of argument propagation.
private boolean visitClass(
DexProgramClass clazz,
@@ -654,84 +738,107 @@
}
IntSet removableParameterIndices = allowedPrototypeChanges.removableParameterIndices;
-
+ Int2ReferenceMap<DexType> newParameterTypes = allowedPrototypeChanges.newParameterTypes;
if (method.getAccessFlags().isAbstract()) {
- // Treat the parameters as unused.
- ArgumentInfoCollection.Builder removableParametersBuilder =
- ArgumentInfoCollection.builder();
- for (int removableParameterIndex : removableParameterIndices) {
- removableParametersBuilder.addArgumentInfo(
- removableParameterIndex,
- RemovedArgumentInfo.builder()
- .setType(method.getArgumentType(removableParameterIndex))
- .build());
- }
- return RewrittenPrototypeDescription.create(
- Collections.emptyList(),
- computeReturnChangesForMethod(method, allowedPrototypeChanges.canRewriteToVoid),
- removableParametersBuilder.build());
+ return computePrototypeChangesForAbstractVirtualMethod(
+ method,
+ allowedPrototypeChanges.newReturnType,
+ newParameterTypes,
+ removableParameterIndices);
}
RewrittenPrototypeDescription prototypeChanges =
computePrototypeChangesForMethod(
method,
- allowedPrototypeChanges.canRewriteToVoid,
+ allowedPrototypeChanges.newReturnType,
+ newParameterTypes::get,
removableParameterIndices::contains);
- assert prototypeChanges.getArgumentInfoCollection().size()
+ assert prototypeChanges.getArgumentInfoCollection().numberOfRemovedArguments()
== removableParameterIndices.size();
return prototypeChanges;
}
- private RewrittenPrototypeDescription computePrototypeChangesForMethod(ProgramMethod method) {
- return computePrototypeChangesForMethod(method, true, parameterIndex -> true);
- }
-
- private RewrittenPrototypeDescription computePrototypeChangesForMethod(
+ private RewrittenPrototypeDescription computePrototypeChangesForAbstractVirtualMethod(
ProgramMethod method,
- boolean allowToVoidRewriting,
- IntPredicate removableParameterIndices) {
- return RewrittenPrototypeDescription.create(
- Collections.emptyList(),
- computeReturnChangesForMethod(method, allowToVoidRewriting),
- computeParameterChangesForMethod(method, removableParameterIndices));
- }
-
- private ArgumentInfoCollection computeParameterChangesForMethod(
- ProgramMethod method, IntPredicate removableParameterIndices) {
- ConcreteCallSiteOptimizationInfo optimizationInfo =
- method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
- if (optimizationInfo == null) {
- return ArgumentInfoCollection.empty();
- }
-
- ArgumentInfoCollection.Builder removableParametersBuilder = ArgumentInfoCollection.builder();
- for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+ DexType newReturnType,
+ Int2ReferenceMap<DexType> newParameterTypes,
+ IntSet removableParameterIndices) {
+ // Treat the parameters as unused.
+ ArgumentInfoCollection.Builder argumentInfoCollectionBuilder =
+ ArgumentInfoCollection.builder();
+ for (int argumentIndex = 0;
argumentIndex < method.getDefinition().getNumberOfArguments();
argumentIndex++) {
- if (!removableParameterIndices.test(argumentIndex)) {
- continue;
- }
- AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
- if (abstractValue.isSingleValue()
- && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
- removableParametersBuilder.addArgumentInfo(
+ if (removableParameterIndices.contains(argumentIndex)) {
+ argumentInfoCollectionBuilder.addArgumentInfo(
argumentIndex,
- RemovedArgumentInfo.builder()
- .setSingleValue(abstractValue.asSingleValue())
- .setType(method.getArgumentType(argumentIndex))
+ RemovedArgumentInfo.builder().setType(method.getArgumentType(argumentIndex)).build());
+ } else if (newParameterTypes.containsKey(argumentIndex)) {
+ DexType newParameterType = newParameterTypes.get(argumentIndex);
+ argumentInfoCollectionBuilder.addArgumentInfo(
+ argumentIndex,
+ RewrittenTypeInfo.builder()
+ .setCastType(newParameterType)
+ .setOldType(method.getArgumentType(argumentIndex))
+ .setNewType(newParameterType)
.build());
}
}
- return removableParametersBuilder.build();
+ return RewrittenPrototypeDescription.create(
+ Collections.emptyList(),
+ computeReturnChangesForMethod(method, newReturnType),
+ argumentInfoCollectionBuilder.build());
}
- private RewrittenTypeInfo computeReturnChangesForMethod(
- ProgramMethod method, boolean allowToVoidRewriting) {
- if (!allowToVoidRewriting) {
- assert !returnValuesForVirtualMethods.containsKey(method);
+ private RewrittenPrototypeDescription computePrototypeChangesForMethod(ProgramMethod method) {
+ IntFunction<DexType> parameterIndexToParameterType =
+ appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)
+ ? parameterIndex -> getNewParameterType(method, parameterIndex)
+ : parameterIndex -> null;
+ return computePrototypeChangesForMethod(
+ method, getNewReturnType(method), parameterIndexToParameterType, parameterIndex -> true);
+ }
+
+ private DexType getNewReturnType(ProgramMethod method) {
+ return getNewReturnType(method, getReturnValue(method));
+ }
+
+ private DexType getNewReturnType(ProgramMethod method, SingleValue returnValue) {
+ DexType staticType = method.getReturnType();
+ if (staticType.isVoidType()
+ || !appView.getKeepInfo(method).isReturnTypeStrengtheningAllowed(options)) {
return null;
}
+ if (returnValue != null) {
+ return dexItemFactory.voidType;
+ }
+ TypeElement newReturnTypeElement =
+ method
+ .getOptimizationInfo()
+ .getDynamicType()
+ .getDynamicUpperBoundType(staticType.toTypeElement(appView));
+ assert newReturnTypeElement.isTop()
+ || newReturnTypeElement.lessThanOrEqual(staticType.toTypeElement(appView), appView);
+ if (!newReturnTypeElement.isClassType()) {
+ assert newReturnTypeElement.isArrayType()
+ || newReturnTypeElement.isNullType()
+ || newReturnTypeElement.isTop();
+ return null;
+ }
+ DexType newReturnType = newReturnTypeElement.asClassType().toDexType(dexItemFactory);
+ if (newReturnType == staticType) {
+ return null;
+ }
+ if (!appView.appInfo().isSubtype(newReturnType, staticType)) {
+ return null;
+ }
+ return AccessUtils.isAccessibleInSameContextsAs(
+ newReturnType, method.getReturnType(), appView)
+ ? newReturnType
+ : null;
+ }
+ private SingleValue getReturnValue(ProgramMethod method) {
AbstractValue returnValue;
if (method.getReturnType().isAlwaysNull(appView)) {
returnValue = appView.abstractValueFactory().createNullValue();
@@ -743,13 +850,118 @@
returnValue = method.getOptimizationInfo().getAbstractReturnValue();
}
- if (!returnValue.isSingleValue()
- || !returnValue.asSingleValue().isMaterializableInAllContexts(appView)) {
+ return returnValue.isSingleValue()
+ && returnValue.asSingleValue().isMaterializableInAllContexts(appView)
+ ? returnValue.asSingleValue()
+ : null;
+ }
+
+ private DexType getNewParameterType(ProgramMethod method, int parameterIndex) {
+ if (!appView.getKeepInfo(method).isParameterTypeStrengtheningAllowed(options)) {
return null;
}
+ DexType staticType = method.getArgumentType(parameterIndex);
+ if (!staticType.isClassType()) {
+ return null;
+ }
+ DynamicType dynamicType =
+ method.getOptimizationInfo().getArgumentInfos().getDynamicType(parameterIndex);
+ if (dynamicType == null || dynamicType.isUnknown()) {
+ return null;
+ }
+ TypeElement staticTypeElement = staticType.toTypeElement(appView);
+ TypeElement dynamicUpperBoundType = dynamicType.getDynamicUpperBoundType(staticTypeElement);
+ assert dynamicUpperBoundType.lessThanOrEqual(staticTypeElement, appView);
+ assert dynamicUpperBoundType.isReferenceType();
+ if (dynamicUpperBoundType.isNullType()) {
+ return null;
+ }
+ if (dynamicUpperBoundType.isArrayType()) {
+ return null;
+ }
+ assert dynamicUpperBoundType.isClassType();
+ DexType newParameterType = dynamicUpperBoundType.asClassType().toDexType(dexItemFactory);
+ if (newParameterType == staticType) {
+ return null;
+ }
+ if (!appView.appInfo().isSubtype(newParameterType, staticType)) {
+ return null;
+ }
+ return AccessUtils.isAccessibleInSameContextsAs(newParameterType, staticType, appView)
+ ? newParameterType
+ : null;
+ }
- SingleValue singleValue = returnValue.asSingleValue();
- return RewrittenTypeInfo.toVoid(method.getReturnType(), dexItemFactory, singleValue);
+ private RewrittenPrototypeDescription computePrototypeChangesForMethod(
+ ProgramMethod method,
+ DexType newReturnType,
+ IntFunction<DexType> newParameterTypes,
+ IntPredicate removableParameterIndices) {
+ return RewrittenPrototypeDescription.create(
+ Collections.emptyList(),
+ computeReturnChangesForMethod(method, newReturnType),
+ computeParameterChangesForMethod(method, newParameterTypes, removableParameterIndices));
+ }
+
+ private ArgumentInfoCollection computeParameterChangesForMethod(
+ ProgramMethod method,
+ IntFunction<DexType> newParameterTypes,
+ IntPredicate removableParameterIndices) {
+ ConcreteCallSiteOptimizationInfo optimizationInfo =
+ method.getOptimizationInfo().getArgumentInfos().asConcreteCallSiteOptimizationInfo();
+ if (optimizationInfo == null) {
+ return ArgumentInfoCollection.empty();
+ }
+
+ ArgumentInfoCollection.Builder parameterChangesBuilder = ArgumentInfoCollection.builder();
+ for (int argumentIndex = method.getDefinition().getFirstNonReceiverArgumentIndex();
+ argumentIndex < method.getDefinition().getNumberOfArguments();
+ argumentIndex++) {
+ if (removableParameterIndices.test(argumentIndex)) {
+ AbstractValue abstractValue = optimizationInfo.getAbstractArgumentValue(argumentIndex);
+ if (abstractValue.isSingleValue()
+ && abstractValue.asSingleValue().isMaterializableInContext(appView, method)) {
+ parameterChangesBuilder.addArgumentInfo(
+ argumentIndex,
+ RemovedArgumentInfo.builder()
+ .setSingleValue(abstractValue.asSingleValue())
+ .setType(method.getArgumentType(argumentIndex))
+ .build());
+ continue;
+ }
+ }
+
+ DexType dynamicType = newParameterTypes.apply(argumentIndex);
+ if (dynamicType != null) {
+ DexType staticType = method.getArgumentType(argumentIndex);
+ assert dynamicType != staticType;
+ parameterChangesBuilder.addArgumentInfo(
+ argumentIndex,
+ RewrittenTypeInfo.builder()
+ .setCastType(dynamicType)
+ .setOldType(staticType)
+ .setNewType(dynamicType)
+ .build());
+ }
+ }
+ return parameterChangesBuilder.build();
+ }
+
+ private RewrittenTypeInfo computeReturnChangesForMethod(
+ ProgramMethod method, DexType newReturnType) {
+ if (newReturnType == null) {
+ assert !returnValuesForVirtualMethods.containsKey(method);
+ return null;
+ }
+ assert newReturnType != method.getReturnType();
+ return RewrittenTypeInfo.builder()
+ .applyIf(
+ newReturnType == dexItemFactory.voidType,
+ builder -> builder.setSingleValue(getReturnValue(method)))
+ .setCastType(newReturnType)
+ .setOldType(method.getReturnType())
+ .setNewType(newReturnType)
+ .build();
}
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
index c98c383..d32df00 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/codescanner/ConcreteMonomorphicMethodState.java
@@ -21,9 +21,8 @@
public ConcreteMonomorphicMethodState(List<ParameterState> parameterStates) {
assert Streams.stream(Iterables.skip(parameterStates, 1))
.noneMatch(x -> x.isConcrete() && x.asConcrete().isReceiverParameter());
- assert Iterables.any(parameterStates, parameterState -> !parameterState.isUnknown())
- : "Must use UnknownMethodState instead";
this.parameterStates = parameterStates;
+ assert !isEffectivelyUnknown() : "Must use UnknownMethodState instead";
}
public ParameterState getParameterState(int index) {
@@ -34,6 +33,10 @@
return parameterStates;
}
+ public boolean isEffectivelyUnknown() {
+ return Iterables.all(parameterStates, ParameterState::isUnknown);
+ }
+
@Override
public ConcreteMonomorphicMethodState mutableCopy() {
List<ParameterState> copiedParametersStates = new ArrayList<>(size());
@@ -75,10 +78,7 @@
|| !parameterStates.get(argumentIndex).asConcrete().isReceiverParameter();
}
- if (Iterables.all(parameterStates, ParameterState::isUnknown)) {
- return unknown();
- }
- return this;
+ return isEffectivelyUnknown() ? unknown() : this;
}
@Override
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
index 90d7e4a..cfec454 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysFalseParameterReprocessingCriteria.java
@@ -6,7 +6,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
/**
@@ -34,7 +35,9 @@
@Override
public boolean shouldReprocess(
AppView<AppInfoWithLiveness> appView,
- ConcreteParameterState parameterState,
+ ProgramMethod method,
+ ConcreteCallSiteOptimizationInfo methodState,
+ int parameterIndex,
DexType parameterType) {
return false;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
index 72d382f..ff85df7 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/AlwaysTrueParameterReprocessingCriteria.java
@@ -6,7 +6,8 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.graph.ProgramMethod;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
/**
@@ -32,7 +33,9 @@
@Override
public boolean shouldReprocess(
AppView<AppInfoWithLiveness> appView,
- ConcreteParameterState parameterState,
+ ProgramMethod method,
+ ConcreteCallSiteOptimizationInfo methodState,
+ int parameterIndex,
DexType parameterType) {
return true;
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
index 658383f..69e0354 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/MethodReprocessingCriteria.java
@@ -7,13 +7,9 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.ProgramMethod;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteMonomorphicMethodStateOrUnknown;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.MethodState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.UnknownParameterState;
+import com.android.tools.r8.ir.optimize.info.CallSiteOptimizationInfo;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
-import com.google.common.collect.Iterables;
import it.unimi.dsi.fastutil.ints.Int2ReferenceMap;
import it.unimi.dsi.fastutil.ints.Int2ReferenceOpenHashMap;
@@ -43,35 +39,29 @@
parameterIndex, ParameterReprocessingCriteria.alwaysReprocess());
}
- public ConcreteMonomorphicMethodStateOrUnknown widenMethodState(
+ public boolean shouldReprocess(
AppView<AppInfoWithLiveness> appView,
ProgramMethod method,
- ConcreteMonomorphicMethodState methodState) {
- for (int parameterIndex = 0; parameterIndex < methodState.size(); parameterIndex++) {
- ParameterState parameterState = methodState.getParameterState(parameterIndex);
- assert !parameterState.isBottom();
- if (parameterState.isUnknown()) {
- continue;
+ CallSiteOptimizationInfo methodState) {
+ if (!methodState.isConcreteCallSiteOptimizationInfo()) {
+ return false;
+ }
+ ConcreteCallSiteOptimizationInfo concreteMethodState =
+ methodState.asConcreteCallSiteOptimizationInfo();
+ for (int parameterIndex = 0;
+ parameterIndex < method.getDefinition().getNumberOfArguments();
+ parameterIndex++) {
+ if (methodState.getAbstractArgumentValue(parameterIndex).isSingleValue()) {
+ return true;
}
-
- if (parameterState.getAbstractValue(appView).isSingleValue()) {
- // Don't widen when we have information that can be used for parameter removal.
- continue;
- }
-
ParameterReprocessingCriteria parameterReprocessingCriteria =
getParameterReprocessingCriteria(parameterIndex);
DexType parameterType = method.getArgumentType(parameterIndex);
if (parameterReprocessingCriteria.shouldReprocess(
- appView, parameterState.asConcrete(), parameterType)) {
- continue;
+ appView, method, concreteMethodState, parameterIndex, parameterType)) {
+ return true;
}
-
- methodState.setParameterState(parameterIndex, UnknownParameterState.get());
}
-
- return Iterables.all(methodState.getParameterStates(), ParameterState::isUnknown)
- ? MethodState.unknown()
- : methodState;
+ return false;
}
}
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
index b55bf13..47f01c1 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/NonTrivialParameterReprocessingCriteria.java
@@ -6,11 +6,10 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+import com.android.tools.r8.graph.ProgramMethod;
import com.android.tools.r8.ir.analysis.type.DynamicType;
import com.android.tools.r8.ir.analysis.type.Nullability;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcretePrimitiveTypeParameterState;
-import com.android.tools.r8.optimize.argumentpropagation.codescanner.ConcreteReferenceTypeParameterState;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.optimize.argumentpropagation.utils.WideningUtils;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
@@ -31,13 +30,16 @@
@Override
public boolean shouldReprocess(
AppView<AppInfoWithLiveness> appView,
- ConcreteParameterState parameterState,
+ ProgramMethod method,
+ ConcreteCallSiteOptimizationInfo methodState,
+ int parameterIndex,
DexType parameterType) {
- if (parameterState.isReferenceParameter()) {
- return shouldReprocess(appView, parameterState.asReferenceParameter(), parameterType);
+ if (parameterType.isReferenceType()) {
+ return shouldReprocessReferenceParameter(
+ appView, method, methodState, parameterIndex, parameterType);
} else {
- assert parameterState.isPrimitiveParameter();
- return shouldReprocess(appView, parameterState.asPrimitiveParameter(), parameterType);
+ assert parameterType.isPrimitiveType();
+ return shouldReprocessPrimitiveParameter(methodState, parameterIndex);
}
}
@@ -56,34 +58,35 @@
return true;
}
- private boolean shouldReprocess(
- AppView<AppInfoWithLiveness> appView,
- ConcretePrimitiveTypeParameterState parameterState,
- DexType parameterType) {
- return true;
+ private boolean shouldReprocessPrimitiveParameter(
+ ConcreteCallSiteOptimizationInfo methodState, int parameterIndex) {
+ return methodState.getAbstractArgumentValue(parameterIndex).isNonTrivial();
}
- private boolean shouldReprocess(
+ private boolean shouldReprocessReferenceParameter(
AppView<AppInfoWithLiveness> appView,
- ConcreteReferenceTypeParameterState parameterState,
+ ProgramMethod method,
+ ConcreteCallSiteOptimizationInfo methodState,
+ int parameterIndex,
DexType parameterType) {
if (shouldReprocessDueToAbstractValue()
- && !parameterState.getAbstractValue(appView).isUnknown()) {
+ && !methodState.getAbstractArgumentValue(parameterIndex).isUnknown()) {
return true;
}
if (shouldReprocessDueToDynamicType()) {
DynamicType widenedDynamicType =
WideningUtils.widenDynamicNonReceiverType(
appView,
- parameterState.getDynamicType().withNullability(Nullability.maybeNull()),
+ methodState.getDynamicType(parameterIndex).withNullability(Nullability.maybeNull()),
parameterType);
if (!widenedDynamicType.isUnknown()) {
return true;
}
}
+ boolean isReceiverParameter = parameterIndex == 0 && method.getDefinition().isInstance();
if (shouldReprocessDueToNullability()
- && !parameterState.isReceiverParameter()
- && !parameterState.getNullability().isUnknown()) {
+ && !isReceiverParameter
+ && !methodState.getNullability(parameterIndex).isUnknown()) {
return true;
}
return false;
diff --git a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
index d7c8890..9fb773e 100644
--- a/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
+++ b/src/main/java/com/android/tools/r8/optimize/argumentpropagation/reprocessingcriteria/ParameterReprocessingCriteria.java
@@ -6,9 +6,10 @@
import com.android.tools.r8.graph.AppView;
import com.android.tools.r8.graph.DexType;
+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.optimize.argumentpropagation.codescanner.ConcreteParameterState;
+import com.android.tools.r8.ir.optimize.info.ConcreteCallSiteOptimizationInfo;
import com.android.tools.r8.shaking.AppInfoWithLiveness;
public abstract class ParameterReprocessingCriteria {
@@ -35,7 +36,9 @@
public abstract boolean shouldReprocess(
AppView<AppInfoWithLiveness> appView,
- ConcreteParameterState parameterState,
+ ProgramMethod method,
+ ConcreteCallSiteOptimizationInfo methodState,
+ int parameterIndex,
DexType parameterType);
public abstract boolean shouldReprocessDueToAbstractValue();
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 cde8896..d38df4c 100644
--- a/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
+++ b/src/main/java/com/android/tools/r8/shaking/AppInfoWithLiveness.java
@@ -146,8 +146,6 @@
public final Map<DexMember<?, ?>, ProguardMemberRule> assumedValues;
/** All methods that should be inlined if possible due to a configuration directive. */
private final Set<DexMethod> alwaysInline;
- /** All methods that *must* never be inlined due to a configuration directive (testing only). */
- private final Set<DexMethod> neverInline;
/**
* All methods that *must* never be inlined as a result of having a single caller due to a
* configuration directive (testing only).
@@ -228,7 +226,6 @@
Map<DexMember<?, ?>, ProguardMemberRule> noSideEffects,
Map<DexMember<?, ?>, ProguardMemberRule> assumedValues,
Set<DexMethod> alwaysInline,
- Set<DexMethod> neverInline,
Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexMethod> whyAreYouNotInlining,
Set<DexMethod> keepConstantArguments,
@@ -266,7 +263,6 @@
this.assumedValues = assumedValues;
this.callSites = callSites;
this.alwaysInline = alwaysInline;
- this.neverInline = neverInline;
this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.whyAreYouNotInlining = whyAreYouNotInlining;
this.keepConstantArguments = keepConstantArguments;
@@ -312,7 +308,6 @@
previous.noSideEffects,
previous.assumedValues,
previous.alwaysInline,
- previous.neverInline,
previous.neverInlineDueToSingleCaller,
previous.whyAreYouNotInlining,
previous.keepConstantArguments,
@@ -363,7 +358,6 @@
pruneMapFromMembers(previous.noSideEffects, prunedItems, executorService, futures),
pruneMapFromMembers(previous.assumedValues, prunedItems, executorService, futures),
pruneMethods(previous.alwaysInline, prunedItems, executorService, futures),
- pruneMethods(previous.neverInline, prunedItems, executorService, futures),
pruneMethods(previous.neverInlineDueToSingleCaller, prunedItems, executorService, futures),
pruneMethods(previous.whyAreYouNotInlining, prunedItems, executorService, futures),
pruneMethods(previous.keepConstantArguments, prunedItems, executorService, futures),
@@ -571,7 +565,6 @@
noSideEffects,
assumedValues,
alwaysInline,
- neverInline,
neverInlineDueToSingleCaller,
whyAreYouNotInlining,
keepConstantArguments,
@@ -654,7 +647,6 @@
this.assumedValues = previous.assumedValues;
this.callSites = previous.callSites;
this.alwaysInline = previous.alwaysInline;
- this.neverInline = previous.neverInline;
this.neverInlineDueToSingleCaller = previous.neverInlineDueToSingleCaller;
this.whyAreYouNotInlining = previous.whyAreYouNotInlining;
this.keepConstantArguments = previous.keepConstantArguments;
@@ -790,10 +782,6 @@
return alwaysInline.isEmpty();
}
- public boolean isNeverInlineMethod(DexMethod method) {
- return neverInline.contains(method);
- }
-
public boolean isNeverInlineDueToSingleCallerMethod(ProgramMethod method) {
return neverInlineDueToSingleCaller.contains(method.getReference());
}
@@ -1119,7 +1107,7 @@
return false;
}
if (!method.getReturnType().isAlwaysNull(this)
- && !getKeepInfo().getMethodInfo(method, this).isInliningAllowed(options())) {
+ && !getKeepInfo().getMethodInfo(method, this).isOptimizationAllowed(options())) {
return false;
}
return true;
@@ -1295,7 +1283,6 @@
assumedValues,
(reference, rules) -> assumedValues.get(lens.getOriginalMemberSignature(reference))),
lens.rewriteReferences(alwaysInline),
- lens.rewriteReferences(neverInline),
lens.rewriteReferences(neverInlineDueToSingleCaller),
lens.rewriteReferences(whyAreYouNotInlining),
lens.rewriteReferences(keepConstantArguments),
diff --git a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
index c7a2bab..2a5405b 100644
--- a/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
+++ b/src/main/java/com/android/tools/r8/shaking/Enqueuer.java
@@ -3413,7 +3413,8 @@
private final Map<DexMethod, ProgramMethod> liveMethods = new ConcurrentHashMap<>();
- private final ProgramMethodSet neverInlineMethods = ProgramMethodSet.createConcurrent();
+ private final ProgramMethodMap<KeepMethodInfo.Joiner> minimumKeepInfo =
+ ProgramMethodMap.createConcurrent();
private final Map<DexType, DexClasspathClass> syntheticClasspathClasses =
new ConcurrentHashMap<>();
@@ -3481,8 +3482,9 @@
return set;
}
- public void addNeverInlineMethod(ProgramMethod method) {
- neverInlineMethods.add(method);
+ public void addMinimumKeepInfo(ProgramMethod method, Consumer<KeepMethodInfo.Joiner> consumer) {
+ consumer.accept(
+ minimumKeepInfo.computeIfAbsent(method, ignoreKey(KeepMethodInfo::newEmptyJoiner)));
}
void enqueueWorkItems(Enqueuer enqueuer) {
@@ -3509,7 +3511,10 @@
enqueuer.appInfo(), clazz, itfs);
});
- neverInlineMethods.forEach(m -> enqueuer.rootSet.neverInline.add(m.getReference()));
+ minimumKeepInfo.forEach(
+ (method, minimumKeepInfoForMethod) ->
+ enqueuer.applyMinimumKeepInfoWhenLiveOrTargeted(
+ method, UnconditionalKeepInfoEvent.get(), minimumKeepInfoForMethod));
}
}
@@ -3740,6 +3745,7 @@
? ImmutableSet.of(lambdaSynthesisContext.getReference())
: ImmutableSet.of(syntheticClass.getType());
};
+ amendKeepInfoWithCompanionMethods();
AppInfoWithLiveness appInfoWithLiveness =
new AppInfoWithLiveness(
appInfo.getSyntheticItems().commit(app),
@@ -3768,7 +3774,6 @@
rootSet.noSideEffects,
rootSet.assumedValues,
amendWithCompanionMethods(rootSet.alwaysInline),
- amendWithCompanionMethods(rootSet.neverInline),
amendWithCompanionMethods(rootSet.neverInlineDueToSingleCaller),
amendWithCompanionMethods(rootSet.whyAreYouNotInlining),
amendWithCompanionMethods(rootSet.keepConstantArguments),
@@ -3794,6 +3799,28 @@
return new EnqueuerResult(appInfoWithLiveness);
}
+ private void forEachCompanionMethod(BiConsumer<DexMethod, DexMethod> consumer) {
+ if (interfaceProcessor != null) {
+ interfaceProcessor.forEachMethodToMove(consumer);
+ }
+ }
+
+ private void amendKeepInfoWithCompanionMethods() {
+ forEachCompanionMethod(
+ (methodReference, companionReference) -> {
+ ProgramMethod companion = appView.definitionFor(companionReference).asProgramMethod();
+ KeepMethodInfo.Joiner minimumKeepInfoForCompanion =
+ keepInfo.getMethodInfo(methodReference, appInfo).joiner();
+ KeepMethodInfo.Joiner extraMinimumKeepInfoForCompanion =
+ dependentMinimumKeepInfo
+ .getUnconditionalMinimumKeepInfoOrDefault(MinimumKeepInfoCollection.empty())
+ .getOrDefault(methodReference, KeepMethodInfo.newEmptyJoiner())
+ .asMethodJoiner();
+ keepInfo.evaluateMethodRule(
+ companion, minimumKeepInfoForCompanion.merge(extraMinimumKeepInfoForCompanion));
+ });
+ }
+
private Set<DexMethod> amendWithCompanionMethods(Set<DexMethod> methods) {
if (methods.isEmpty() || interfaceProcessor == null) {
return methods;
diff --git a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
index 1eeaa21..d10b35c 100644
--- a/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
+++ b/src/main/java/com/android/tools/r8/shaking/GlobalKeepInfoConfiguration.java
@@ -16,6 +16,8 @@
boolean isAccessModificationEnabled();
+ boolean isMethodStaticizingEnabled();
+
boolean isRepackagingEnabled();
boolean isForceProguardCompatibilityEnabled();
diff --git a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
index 7292248..98ffa2a 100644
--- a/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
+++ b/src/main/java/com/android/tools/r8/shaking/IfRuleEvaluator.java
@@ -18,6 +18,7 @@
import com.android.tools.r8.graph.DexReference;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.graph.SubtypingInfo;
+import com.android.tools.r8.shaking.InlineRule.Type;
import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSet;
import com.android.tools.r8.shaking.RootSetUtils.ConsequentRootSetBuilder;
import com.android.tools.r8.shaking.RootSetUtils.RootSetBuilder;
@@ -346,11 +347,18 @@
rootSetBuilder.runPerRule(executorService, futures, neverClassInlineRuleForCondition, null);
}
+ InlineRule neverInlineForClassInliningRuleForCondition =
+ materializedRule.neverInlineRuleForCondition(dexItemFactory, Type.NEVER_CLASS_INLINE);
+ if (neverInlineForClassInliningRuleForCondition != null) {
+ rootSetBuilder.runPerRule(
+ executorService, futures, neverInlineForClassInliningRuleForCondition, null);
+ }
+
// If the condition of the -if rule has any members, then we need to keep these members to
// ensure that the subsequent rule will be applied again in the second round of tree
// shaking.
InlineRule neverInlineRuleForCondition =
- materializedRule.neverInlineRuleForCondition(dexItemFactory);
+ materializedRule.neverInlineRuleForCondition(dexItemFactory, Type.NEVER);
if (neverInlineRuleForCondition != null) {
rootSetBuilder.runPerRule(executorService, futures, neverInlineRuleForCondition, null);
}
diff --git a/src/main/java/com/android/tools/r8/shaking/InlineRule.java b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
index 7f77fab..505bdbe 100644
--- a/src/main/java/com/android/tools/r8/shaking/InlineRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/InlineRule.java
@@ -13,6 +13,7 @@
public enum Type {
ALWAYS,
NEVER,
+ NEVER_CLASS_INLINE,
NEVER_SINGLE_CALLER
}
@@ -103,6 +104,8 @@
return "alwaysinline";
case NEVER:
return "neverinline";
+ case NEVER_CLASS_INLINE:
+ return "neverclassinlinemethod";
case NEVER_SINGLE_CALLER:
return "neversinglecaller";
}
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
index 36555ac..570ed16 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepInfoCollection.java
@@ -245,6 +245,12 @@
this.methodRuleInstances = methodRuleInstances;
}
+ public void removeKeepInfoForMergedClasses(PrunedItems prunedItems) {
+ if (prunedItems.hasRemovedClasses()) {
+ keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses());
+ }
+ }
+
public void removeKeepInfoForPrunedItems(PrunedItems prunedItems) {
if (prunedItems.hasRemovedClasses()) {
keepClassInfo.keySet().removeAll(prunedItems.getRemovedClasses());
diff --git a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
index 9938bbc..b43c44d 100644
--- a/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
+++ b/src/main/java/com/android/tools/r8/shaking/KeepMethodInfo.java
@@ -24,8 +24,19 @@
return bottom().joiner();
}
+ private final boolean allowClassInlining;
+ private final boolean allowInlining;
+ private final boolean allowMethodStaticizing;
+ private final boolean allowParameterTypeStrengthening;
+ private final boolean allowReturnTypeStrengthening;
+
private KeepMethodInfo(Builder builder) {
super(builder);
+ this.allowClassInlining = builder.isClassInliningAllowed();
+ this.allowInlining = builder.isInliningAllowed();
+ this.allowMethodStaticizing = builder.isMethodStaticizingAllowed();
+ this.allowParameterTypeStrengthening = builder.isParameterTypeStrengtheningAllowed();
+ this.allowReturnTypeStrengthening = builder.isReturnTypeStrengtheningAllowed();
}
// This builder is not private as there are known instances where it is safe to modify keep info
@@ -39,6 +50,53 @@
return isParameterRemovalAllowed(configuration);
}
+ public boolean isClassInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isOptimizationAllowed(configuration) && internalIsClassInliningAllowed();
+ }
+
+ boolean internalIsClassInliningAllowed() {
+ return allowClassInlining;
+ }
+
+ public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isOptimizationAllowed(configuration) && internalIsInliningAllowed();
+ }
+
+ boolean internalIsInliningAllowed() {
+ return allowInlining;
+ }
+
+ public boolean isMethodStaticizingAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isOptimizationAllowed(configuration)
+ && isShrinkingAllowed(configuration)
+ && configuration.isMethodStaticizingEnabled()
+ && internalIsMethodStaticizingAllowed();
+ }
+
+ boolean internalIsMethodStaticizingAllowed() {
+ return allowMethodStaticizing;
+ }
+
+ public boolean isParameterTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isOptimizationAllowed(configuration)
+ && isShrinkingAllowed(configuration)
+ && internalIsParameterTypeStrengtheningAllowed();
+ }
+
+ boolean internalIsParameterTypeStrengtheningAllowed() {
+ return allowParameterTypeStrengthening;
+ }
+
+ public boolean isReturnTypeStrengtheningAllowed(GlobalKeepInfoConfiguration configuration) {
+ return isOptimizationAllowed(configuration)
+ && isShrinkingAllowed(configuration)
+ && internalIsReturnTypeStrengtheningAllowed();
+ }
+
+ boolean internalIsReturnTypeStrengtheningAllowed() {
+ return allowReturnTypeStrengthening;
+ }
+
public Joiner joiner() {
assert !isTop();
return new Joiner(this);
@@ -54,18 +112,110 @@
return this.equals(bottom());
}
- public boolean isInliningAllowed(GlobalKeepInfoConfiguration configuration) {
- return isOptimizationAllowed(configuration);
- }
-
public static class Builder extends KeepInfo.Builder<Builder, KeepMethodInfo> {
+ private boolean allowClassInlining;
+ private boolean allowInlining;
+ private boolean allowMethodStaticizing;
+ private boolean allowParameterTypeStrengthening;
+ private boolean allowReturnTypeStrengthening;
+
private Builder() {
super();
}
private Builder(KeepMethodInfo original) {
super(original);
+ allowClassInlining = original.internalIsClassInliningAllowed();
+ allowInlining = original.internalIsInliningAllowed();
+ allowMethodStaticizing = original.internalIsMethodStaticizingAllowed();
+ allowParameterTypeStrengthening = original.internalIsParameterTypeStrengtheningAllowed();
+ allowReturnTypeStrengthening = original.internalIsReturnTypeStrengtheningAllowed();
+ }
+
+ public boolean isClassInliningAllowed() {
+ return allowClassInlining;
+ }
+
+ public Builder setAllowClassInlining(boolean allowClassInlining) {
+ this.allowClassInlining = allowClassInlining;
+ return self();
+ }
+
+ public Builder allowClassInlining() {
+ return setAllowClassInlining(true);
+ }
+
+ public Builder disallowClassInlining() {
+ return setAllowClassInlining(false);
+ }
+
+ public boolean isInliningAllowed() {
+ return allowInlining;
+ }
+
+ public Builder setAllowInlining(boolean allowInlining) {
+ this.allowInlining = allowInlining;
+ return self();
+ }
+
+ public Builder allowInlining() {
+ return setAllowInlining(true);
+ }
+
+ public Builder disallowInlining() {
+ return setAllowInlining(false);
+ }
+
+ public boolean isMethodStaticizingAllowed() {
+ return allowMethodStaticizing;
+ }
+
+ public Builder setAllowMethodStaticizing(boolean allowMethodStaticizing) {
+ this.allowMethodStaticizing = allowMethodStaticizing;
+ return self();
+ }
+
+ public Builder allowMethodStaticizing() {
+ return setAllowMethodStaticizing(true);
+ }
+
+ public Builder disallowMethodStaticizing() {
+ return setAllowMethodStaticizing(false);
+ }
+
+ public boolean isParameterTypeStrengtheningAllowed() {
+ return allowParameterTypeStrengthening;
+ }
+
+ public Builder setAllowParameterTypeStrengthening(boolean allowParameterTypeStrengthening) {
+ this.allowParameterTypeStrengthening = allowParameterTypeStrengthening;
+ return self();
+ }
+
+ public Builder allowParameterTypeStrengthening() {
+ return setAllowParameterTypeStrengthening(true);
+ }
+
+ public Builder disallowParameterTypeStrengthening() {
+ return setAllowParameterTypeStrengthening(false);
+ }
+
+ public boolean isReturnTypeStrengtheningAllowed() {
+ return allowReturnTypeStrengthening;
+ }
+
+ public Builder setAllowReturnTypeStrengthening(boolean allowReturnTypeStrengthening) {
+ this.allowReturnTypeStrengthening = allowReturnTypeStrengthening;
+ return self();
+ }
+
+ public Builder allowReturnTypeStrengthening() {
+ return setAllowReturnTypeStrengthening(true);
+ }
+
+ public Builder disallowReturnTypeStrengthening() {
+ return setAllowReturnTypeStrengthening(false);
}
@Override
@@ -89,9 +239,40 @@
}
@Override
+ boolean internalIsEqualTo(KeepMethodInfo other) {
+ return super.internalIsEqualTo(other)
+ && isClassInliningAllowed() == other.internalIsClassInliningAllowed()
+ && isInliningAllowed() == other.internalIsInliningAllowed()
+ && isMethodStaticizingAllowed() == other.internalIsMethodStaticizingAllowed()
+ && isParameterTypeStrengtheningAllowed()
+ == other.internalIsParameterTypeStrengtheningAllowed()
+ && isReturnTypeStrengtheningAllowed() == other.internalIsReturnTypeStrengtheningAllowed();
+ }
+
+ @Override
public KeepMethodInfo doBuild() {
return new KeepMethodInfo(this);
}
+
+ @Override
+ public Builder makeTop() {
+ return super.makeTop()
+ .disallowClassInlining()
+ .disallowInlining()
+ .disallowMethodStaticizing()
+ .disallowParameterTypeStrengthening()
+ .disallowReturnTypeStrengthening();
+ }
+
+ @Override
+ public Builder makeBottom() {
+ return super.makeBottom()
+ .allowClassInlining()
+ .allowInlining()
+ .allowMethodStaticizing()
+ .allowParameterTypeStrengthening()
+ .allowReturnTypeStrengthening();
+ }
}
public static class Joiner extends KeepInfo.Joiner<Joiner, Builder, KeepMethodInfo> {
@@ -100,6 +281,31 @@
super(info.builder());
}
+ public Joiner disallowClassInlining() {
+ builder.disallowClassInlining();
+ return self();
+ }
+
+ public Joiner disallowInlining() {
+ builder.disallowInlining();
+ return self();
+ }
+
+ public Joiner disallowMethodStaticizing() {
+ builder.disallowMethodStaticizing();
+ return self();
+ }
+
+ public Joiner disallowParameterTypeStrengthening() {
+ builder.disallowParameterTypeStrengthening();
+ return self();
+ }
+
+ public Joiner disallowReturnTypeStrengthening() {
+ builder.disallowReturnTypeStrengthening();
+ return self();
+ }
+
@Override
public Joiner asMethodJoiner() {
return this;
@@ -108,7 +314,16 @@
@Override
public Joiner merge(Joiner joiner) {
// Should be extended to merge the fields of this class in case any are added.
- return super.merge(joiner);
+ return super.merge(joiner)
+ .applyIf(!joiner.builder.isClassInliningAllowed(), Joiner::disallowClassInlining)
+ .applyIf(!joiner.builder.isInliningAllowed(), Joiner::disallowInlining)
+ .applyIf(!joiner.builder.isMethodStaticizingAllowed(), Joiner::disallowMethodStaticizing)
+ .applyIf(
+ !joiner.builder.isParameterTypeStrengtheningAllowed(),
+ Joiner::disallowParameterTypeStrengthening)
+ .applyIf(
+ !joiner.builder.isReturnTypeStrengtheningAllowed(),
+ Joiner::disallowReturnTypeStrengthening);
}
@Override
diff --git a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
index 8062beb..5a07f2a 100644
--- a/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
+++ b/src/main/java/com/android/tools/r8/shaking/MinimumKeepInfoCollection.java
@@ -90,6 +90,11 @@
});
}
+ public KeepInfo.Joiner<?, ?, ?> getOrDefault(
+ DexReference reference, KeepInfo.Joiner<?, ?, ?> defaultValue) {
+ return minimumKeepInfo.getOrDefault(reference, defaultValue);
+ }
+
public KeepInfo.Joiner<?, ?, ?> getOrCreateMinimumKeepInfoFor(DexReference reference) {
return minimumKeepInfo.computeIfAbsent(
reference, ignoreKey(() -> KeepInfo.newEmptyJoinerFor(reference)));
diff --git a/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java b/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java
new file mode 100644
index 0000000..84fd67d
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoMethodStaticizingRule.java
@@ -0,0 +1,84 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoMethodStaticizingRule extends NoOptimizationBaseRule<NoMethodStaticizingRule> {
+
+ public static final String RULE_NAME = "nomethodstaticizing";
+
+ public static class Builder
+ extends NoOptimizationBaseRule.Builder<NoMethodStaticizingRule, Builder> {
+
+ Builder() {
+ super();
+ }
+
+ @Override
+ public NoMethodStaticizingRule.Builder self() {
+ return this;
+ }
+
+ @Override
+ public NoMethodStaticizingRule build() {
+ return new NoMethodStaticizingRule(
+ origin,
+ getPosition(),
+ source,
+ buildClassAnnotations(),
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ buildInheritanceAnnotations(),
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ NoMethodStaticizingRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return RULE_NAME;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java b/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java
new file mode 100644
index 0000000..a397a7e
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoOptimizationBaseRule.java
@@ -0,0 +1,51 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public abstract class NoOptimizationBaseRule<R extends NoOptimizationBaseRule<R>>
+ extends ProguardConfigurationRule {
+
+ public abstract static class Builder<R extends NoOptimizationBaseRule<R>, B extends Builder<R, B>>
+ extends ProguardConfigurationRule.Builder<R, B> {
+
+ Builder() {
+ super();
+ }
+ }
+
+ NoOptimizationBaseRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
new file mode 100644
index 0000000..e0b6594
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoParameterTypeStrengtheningRule.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoParameterTypeStrengtheningRule
+ extends NoOptimizationBaseRule<NoParameterTypeStrengtheningRule> {
+
+ public static final String RULE_NAME = "noparametertypestrengthening";
+
+ public static class Builder
+ extends NoOptimizationBaseRule.Builder<NoParameterTypeStrengtheningRule, Builder> {
+
+ Builder() {
+ super();
+ }
+
+ @Override
+ public NoParameterTypeStrengtheningRule.Builder self() {
+ return this;
+ }
+
+ @Override
+ public NoParameterTypeStrengtheningRule build() {
+ return new NoParameterTypeStrengtheningRule(
+ origin,
+ getPosition(),
+ source,
+ buildClassAnnotations(),
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ buildInheritanceAnnotations(),
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ NoParameterTypeStrengtheningRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return RULE_NAME;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java b/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java
new file mode 100644
index 0000000..21c6371
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/shaking/NoReturnTypeStrengtheningRule.java
@@ -0,0 +1,85 @@
+// Copyright (c) 2022, 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.shaking;
+
+import com.android.tools.r8.origin.Origin;
+import com.android.tools.r8.position.Position;
+import java.util.List;
+
+public class NoReturnTypeStrengtheningRule
+ extends NoOptimizationBaseRule<NoReturnTypeStrengtheningRule> {
+
+ public static final String RULE_NAME = "noreturntypestrengthening";
+
+ public static class Builder
+ extends NoOptimizationBaseRule.Builder<NoReturnTypeStrengtheningRule, Builder> {
+
+ Builder() {
+ super();
+ }
+
+ @Override
+ public NoReturnTypeStrengtheningRule.Builder self() {
+ return this;
+ }
+
+ @Override
+ public NoReturnTypeStrengtheningRule build() {
+ return new NoReturnTypeStrengtheningRule(
+ origin,
+ getPosition(),
+ source,
+ buildClassAnnotations(),
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ buildInheritanceAnnotations(),
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+ }
+
+ NoReturnTypeStrengtheningRule(
+ Origin origin,
+ Position position,
+ String source,
+ List<ProguardTypeMatcher> classAnnotations,
+ ProguardAccessFlags classAccessFlags,
+ ProguardAccessFlags negatedClassAccessFlags,
+ boolean classTypeNegated,
+ ProguardClassType classType,
+ ProguardClassNameList classNames,
+ List<ProguardTypeMatcher> inheritanceAnnotations,
+ ProguardTypeMatcher inheritanceClassName,
+ boolean inheritanceIsExtends,
+ List<ProguardMemberRule> memberRules) {
+ super(
+ origin,
+ position,
+ source,
+ classAnnotations,
+ classAccessFlags,
+ negatedClassAccessFlags,
+ classTypeNegated,
+ classType,
+ classNames,
+ inheritanceAnnotations,
+ inheritanceClassName,
+ inheritanceIsExtends,
+ memberRules);
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ @Override
+ String typeString() {
+ return RULE_NAME;
+ }
+}
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
index b92040b..b1fdef2 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardConfigurationParser.java
@@ -500,6 +500,24 @@
configurationBuilder.addRule(rule);
return true;
}
+ if (acceptString(NoMethodStaticizingRule.RULE_NAME)) {
+ ProguardConfigurationRule rule =
+ parseNoOptimizationRule(optionStart, NoMethodStaticizingRule.builder());
+ configurationBuilder.addRule(rule);
+ return true;
+ }
+ if (acceptString(NoParameterTypeStrengtheningRule.RULE_NAME)) {
+ ProguardConfigurationRule rule =
+ parseNoOptimizationRule(optionStart, NoParameterTypeStrengtheningRule.builder());
+ configurationBuilder.addRule(rule);
+ return true;
+ }
+ if (acceptString(NoReturnTypeStrengtheningRule.RULE_NAME)) {
+ ProguardConfigurationRule rule =
+ parseNoOptimizationRule(optionStart, NoReturnTypeStrengtheningRule.builder());
+ configurationBuilder.addRule(rule);
+ return true;
+ }
if (acceptString("neverpropagatevalue")) {
MemberValuePropagationRule rule =
parseMemberValuePropagationRule(MemberValuePropagationRule.Type.NEVER, optionStart);
@@ -828,6 +846,16 @@
return keepRuleBuilder.build();
}
+ private <R extends NoOptimizationBaseRule<R>, B extends NoOptimizationBaseRule.Builder<R, B>>
+ R parseNoOptimizationRule(Position start, B builder) throws ProguardRuleParserException {
+ builder.setOrigin(origin).setStart(start);
+ parseClassSpec(builder, false);
+ Position end = getPosition();
+ builder.setSource(getSourceSnippet(contents, start, end));
+ builder.setEnd(end);
+ return builder.build();
+ }
+
private MemberValuePropagationRule parseMemberValuePropagationRule(
MemberValuePropagationRule.Type type, Position start)
throws ProguardRuleParserException {
diff --git a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
index f61b4ee..f060d22 100644
--- a/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
+++ b/src/main/java/com/android/tools/r8/shaking/ProguardIfRule.java
@@ -219,7 +219,8 @@
* <p>Therefore, each time the subsequent rule of an -if rule is applied, we also apply a
* -neverinline rule for the condition of the -if rule.
*/
- protected InlineRule neverInlineRuleForCondition(DexItemFactory dexItemFactory) {
+ protected InlineRule neverInlineRuleForCondition(
+ DexItemFactory dexItemFactory, InlineRule.Type type) {
if (getMemberRules() == null || getMemberRules().isEmpty()) {
return null;
}
@@ -242,7 +243,7 @@
.filter(rule -> rule.getRuleType().includesMethods())
.map(memberRule -> memberRule.materialize(dexItemFactory))
.collect(Collectors.toList()),
- InlineRule.Type.NEVER);
+ type);
}
protected NoHorizontalClassMergingRule noHorizontalClassMergingRuleForCondition(
diff --git a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
index 3644e3d..9629fb2 100644
--- a/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
+++ b/src/main/java/com/android/tools/r8/shaking/RootSetUtils.java
@@ -267,6 +267,9 @@
markMatchingFields(clazz, memberKeepRules, rule, null, ifRule);
} else if (rule instanceof InlineRule
|| rule instanceof ConstantArgumentRule
+ || rule instanceof NoMethodStaticizingRule
+ || rule instanceof NoParameterTypeStrengtheningRule
+ || rule instanceof NoReturnTypeStrengtheningRule
|| rule instanceof UnusedArgumentRule
|| rule instanceof ReprocessMethodRule
|| rule instanceof WhyAreYouNotInliningRule) {
@@ -1189,7 +1192,16 @@
alwaysInline.add(reference);
break;
case NEVER:
- neverInline.add(reference);
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asMethodJoiner()
+ .disallowInlining();
+ break;
+ case NEVER_CLASS_INLINE:
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asMethodJoiner()
+ .disallowClassInlining();
break;
case NEVER_SINGLE_CALLER:
neverInlineDueToSingleCaller.add(reference);
@@ -1243,6 +1255,27 @@
} else if (context instanceof NoHorizontalClassMergingRule) {
noHorizontalClassMerging.add(item.asClass().type);
context.markAsUsed();
+ } else if (context instanceof NoMethodStaticizingRule) {
+ assert item.isProgramMethod();
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asMethodJoiner()
+ .disallowMethodStaticizing();
+ context.markAsUsed();
+ } else if (context instanceof NoParameterTypeStrengtheningRule) {
+ assert item.isProgramMethod();
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asMethodJoiner()
+ .disallowParameterTypeStrengthening();
+ context.markAsUsed();
+ } else if (context instanceof NoReturnTypeStrengtheningRule) {
+ assert item.isProgramMethod();
+ dependentMinimumKeepInfo
+ .getOrCreateUnconditionalMinimumKeepInfoFor(item.getReference())
+ .asMethodJoiner()
+ .disallowReturnTypeStrengthening();
+ context.markAsUsed();
} else if (context instanceof MemberValuePropagationRule) {
switch (((MemberValuePropagationRule) context).getType()) {
case NEVER:
@@ -1578,7 +1611,6 @@
abstract static class RootSetBase {
- final Set<DexMethod> neverInline;
final Set<DexMethod> neverInlineDueToSingleCaller;
final Set<DexType> neverClassInline;
private final DependentMinimumKeepInfoCollection dependentMinimumKeepInfo;
@@ -1587,14 +1619,12 @@
public final ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse;
RootSetBase(
- Set<DexMethod> neverInline,
Set<DexMethod> neverInlineDueToSingleCaller,
Set<DexType> neverClassInline,
DependentMinimumKeepInfoCollection dependentMinimumKeepInfo,
Map<DexType, Set<ProguardKeepRuleBase>> dependentKeepClassCompatRule,
List<DelayedRootSetActionItem> delayedRootSetActionItems,
ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
- this.neverInline = neverInline;
this.neverInlineDueToSingleCaller = neverInlineDueToSingleCaller;
this.neverClassInline = neverClassInline;
this.dependentMinimumKeepInfo = dependentMinimumKeepInfo;
@@ -1660,7 +1690,6 @@
List<DelayedRootSetActionItem> delayedRootSetActionItems,
ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
super(
- neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
dependentMinimumKeepInfo,
@@ -1713,7 +1742,6 @@
}
void addConsequentRootSet(ConsequentRootSet consequentRootSet) {
- neverInline.addAll(consequentRootSet.neverInline);
neverInlineDueToSingleCaller.addAll(consequentRootSet.neverInlineDueToSingleCaller);
neverClassInline.addAll(consequentRootSet.neverClassInline);
consequentRootSet.dependentKeepClassCompatRule.forEach(
@@ -1999,7 +2027,6 @@
List<DelayedRootSetActionItem> delayedRootSetActionItems,
ProgramMethodMap<ProgramMethod> pendingMethodMoveInverse) {
super(
- neverInline,
neverInlineDueToSingleCaller,
neverClassInline,
dependentMinimumKeepInfo,
diff --git a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
index 6d51c04..c882cf4 100644
--- a/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
+++ b/src/main/java/com/android/tools/r8/shaking/VerticalClassMerger.java
@@ -723,7 +723,7 @@
KeepInfoCollection keepInfo = appView.appInfo().getKeepInfo();
keepInfo.mutate(
mutator ->
- mutator.removeKeepInfoForPrunedItems(
+ mutator.removeKeepInfoForMergedClasses(
PrunedItems.builder().setRemovedClasses(mergedClasses.keySet()).build()));
timing.end();
diff --git a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
index b9cbec8..e911b47 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/TraceReferences.java
@@ -67,7 +67,9 @@
for (ProgramResourceProvider provider : command.getSource()) {
forEachDescriptor(provider, targetDescriptors::remove);
}
- Tracer tracer = new Tracer(targetDescriptors, builder.build(), command.getReporter());
+ InternalOptions options = new InternalOptions();
+ options.lookupLibraryBeforeProgram = true;
+ Tracer tracer = new Tracer(targetDescriptors, builder.build(), command.getReporter(), options);
tracer.run(command.getConsumer());
}
diff --git a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
index 43bdb0f..4650eb7 100644
--- a/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
+++ b/src/main/java/com/android/tools/r8/tracereferences/Tracer.java
@@ -56,14 +56,16 @@
private final DiagnosticsHandler diagnostics;
private final Predicate<DexType> targetPredicate;
- Tracer(Set<String> targetDescriptors, AndroidApp inputApp, DiagnosticsHandler diagnostics)
+ Tracer(
+ Set<String> targetDescriptors,
+ AndroidApp inputApp,
+ DiagnosticsHandler diagnostics,
+ InternalOptions options)
throws IOException {
this(
AppView.createForTracer(
AppInfoWithClassHierarchy.createInitialAppInfoWithClassHierarchy(
- new ApplicationReader(inputApp, new InternalOptions(), Timing.empty())
- .read()
- .toDirect(),
+ new ApplicationReader(inputApp, options, Timing.empty()).read().toDirect(),
ClassToFeatureSplitMap.createEmptyClassToFeatureSplitMap(),
MainDexInfo.none())),
diagnostics,
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 1000a75..f3dcf92 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -690,6 +690,11 @@
&& getProguardConfiguration().isAccessModificationAllowed();
}
+ @Override
+ public boolean isMethodStaticizingEnabled() {
+ return callSiteOptimizationOptions().isMethodStaticizingEnabled();
+ }
+
public boolean keepInnerClassStructure() {
return getProguardConfiguration().getKeepAttributes().signature
|| getProguardConfiguration().getKeepAttributes().innerClasses;
@@ -1258,21 +1263,12 @@
public class CallSiteOptimizationOptions {
private boolean enabled = true;
-
- // Each time we see an invoke with more dispatch targets than the threshold, we stop call site
- // propagation for all these dispatch targets. The motivation for this is that it is expensive
- // and that we are somewhat unlikely to have precise knowledge about the value of arguments when
- // there are many (possibly spurious) call graph edges.
- private final int maxNumberOfDispatchTargetsBeforeAbandoning = 10;
+ private boolean enableMethodStaticizing = true;
public void disableOptimization() {
enabled = false;
}
- public int getMaxNumberOfDispatchTargetsBeforeAbandoning() {
- return maxNumberOfDispatchTargetsBeforeAbandoning;
- }
-
public int getMaxNumberOfInParameters() {
return 10;
}
@@ -1284,6 +1280,10 @@
return enabled;
}
+ public boolean isMethodStaticizingEnabled() {
+ return enableMethodStaticizing;
+ }
+
public CallSiteOptimizationOptions setEnabled(boolean enabled) {
if (enabled) {
assert isEnabled();
@@ -1292,6 +1292,11 @@
}
return this;
}
+
+ public CallSiteOptimizationOptions setEnableMethodStaticizing(boolean enableMethodStaticizing) {
+ this.enableMethodStaticizing = enableMethodStaticizing;
+ return this;
+ }
}
public class ClassInlinerOptions {
@@ -1915,6 +1920,7 @@
}
public boolean canUseSuppressedExceptions() {
+ // TODO(b/214239152): Suppressed exceptions are @hide from at least 4.0.1 / Android I / API 14.
return !isDesugaring() || hasMinApi(AndroidApiLevel.K);
}
diff --git a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
index 00749bb..cc8bc7f 100644
--- a/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
+++ b/src/main/java/com/android/tools/r8/utils/collections/DexClassAndMethodSetBase.java
@@ -46,6 +46,10 @@
return backing.get(method);
}
+ public T getFirst() {
+ return iterator().next();
+ }
+
public boolean contains(DexMethod method) {
return backing.containsKey(method);
}
diff --git a/src/test/examplesAndroidO/trywithresources/TryWithResources.java b/src/test/examplesAndroidO/trywithresources/TryWithResources.java
index ac779d6..2c407cf 100644
--- a/src/test/examplesAndroidO/trywithresources/TryWithResources.java
+++ b/src/test/examplesAndroidO/trywithresources/TryWithResources.java
@@ -36,20 +36,12 @@
dumpException(cause, indent + " cause: ");
}
- // Dump suppressed UNLESS it is a desugared code running
- // on JVM, in which case we avoid dumping suppressed, since
- // the output will be used for comparison with desugared code
- // running on device.
- if (!desugaredCodeRunningOnJvm()) {
- Throwable[] suppressed = e.getSuppressed();
- for (int i = 0; i < suppressed.length; i++) {
- dumpException(suppressed[i], indent + "supp[" + i + "]: ");
- }
+ Throwable[] suppressed = e.getSuppressed();
+ for (int i = 0; i < suppressed.length; i++) {
+ dumpException(suppressed[i], indent + "supp[" + i + "]: ");
}
}
- abstract boolean desugaredCodeRunningOnJvm();
-
// --- TEST SYMBOLS ---
static class Resource implements Closeable {
@@ -190,9 +182,7 @@
packer.act(new RuntimeException("original exception Z"));
for (Throwable unpacked : unpacker.get()) {
- if (!desugaredCodeRunningOnJvm()) {
- dumpException(unpacked);
- }
+ dumpException(unpacked);
}
}
}
diff --git a/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java b/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java
index 10a96f9..65e7221 100644
--- a/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java
+++ b/src/test/examplesAndroidO/trywithresources/TryWithResourcesDesugaredTests.java
@@ -4,19 +4,6 @@
package trywithresources;
public class TryWithResourcesDesugaredTests extends TryWithResources {
- private boolean isAndroid() {
- try {
- Class.forName("dalvik.system.VMRuntime");
- return true;
- } catch (Exception ignored) {
- }
- return false;
- }
-
- @Override
- boolean desugaredCodeRunningOnJvm() {
- return !isAndroid();
- }
public static void main(String[] args) throws Exception {
new TryWithResourcesDesugaredTests().test();
diff --git a/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java b/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java
index d2a05c3..8180fb1 100644
--- a/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java
+++ b/src/test/examplesAndroidO/trywithresources/TryWithResourcesNotDesugaredTests.java
@@ -4,10 +4,6 @@
package trywithresources;
public class TryWithResourcesNotDesugaredTests extends TryWithResources {
- @Override
- boolean desugaredCodeRunningOnJvm() {
- return false;
- }
public static void main(String[] args) throws Exception {
new TryWithResourcesNotDesugaredTests().test();
diff --git a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
index dfebfd7..6006f32 100644
--- a/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
+++ b/src/test/java/com/android/tools/r8/D8IncrementalRunExamplesAndroidOTest.java
@@ -99,6 +99,7 @@
Assert.assertTrue(
descriptor.endsWith(getCompanionClassNameSuffix() + ";")
|| SyntheticItemsTestUtils.isExternalTwrCloseMethod(reference)
+ || SyntheticItemsTestUtils.isMaybeExternalSuppressedExceptionMethod(reference)
|| SyntheticItemsTestUtils.isExternalLambda(reference)
|| SyntheticItemsTestUtils.isExternalStaticInterfaceCall(reference)
|| descriptor.equals(mainClassDescriptor));
diff --git a/src/test/java/com/android/tools/r8/NoMethodStaticizing.java b/src/test/java/com/android/tools/r8/NoMethodStaticizing.java
new file mode 100644
index 0000000..ecfd3af
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoMethodStaticizing.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoMethodStaticizing {}
diff --git a/src/test/java/com/android/tools/r8/NoParameterTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoParameterTypeStrengthening.java
new file mode 100644
index 0000000..c862859
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoParameterTypeStrengthening.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.PARAMETER})
+public @interface NoParameterTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java b/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java
new file mode 100644
index 0000000..faf89d0
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/NoReturnTypeStrengthening.java
@@ -0,0 +1,11 @@
+// Copyright (c) 2022, 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;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD})
+public @interface NoReturnTypeStrengthening {}
diff --git a/src/test/java/com/android/tools/r8/R8TestBuilder.java b/src/test/java/com/android/tools/r8/R8TestBuilder.java
index 1fa7630..090ebb5 100644
--- a/src/test/java/com/android/tools/r8/R8TestBuilder.java
+++ b/src/test/java/com/android/tools/r8/R8TestBuilder.java
@@ -18,6 +18,9 @@
import com.android.tools.r8.shaking.CollectingGraphConsumer;
import com.android.tools.r8.shaking.NoFieldTypeStrengtheningRule;
import com.android.tools.r8.shaking.NoHorizontalClassMergingRule;
+import com.android.tools.r8.shaking.NoMethodStaticizingRule;
+import com.android.tools.r8.shaking.NoParameterTypeStrengtheningRule;
+import com.android.tools.r8.shaking.NoReturnTypeStrengtheningRule;
import com.android.tools.r8.shaking.NoUnusedInterfaceRemovalRule;
import com.android.tools.r8.shaking.NoVerticalClassMergingRule;
import com.android.tools.r8.shaking.ProguardConfiguration;
@@ -436,6 +439,11 @@
"-" + name + " class * { @" + annotation.getTypeName() + " <fields>; }");
}
+ T addInternalMatchAnnotationOnMethodRule(String name, Class<? extends Annotation> annotation) {
+ return addInternalKeepRules(
+ "-" + name + " class * { @" + annotation.getTypeName() + " <methods>; }");
+ }
+
T addInternalMatchInterfaceRule(String name, Class<?> matchInterface) {
return addInternalKeepRules("-" + name + " @" + matchInterface.getTypeName() + " class *");
}
@@ -502,6 +510,24 @@
NoFieldTypeStrengtheningRule.RULE_NAME, NoFieldTypeStrengthening.class);
}
+ public T enableNoMethodStaticizingAnnotations() {
+ return addNoMethodStaticizingAnnotation()
+ .addInternalMatchAnnotationOnMethodRule(
+ NoMethodStaticizingRule.RULE_NAME, NoMethodStaticizing.class);
+ }
+
+ public T enableNoParameterTypeStrengtheningAnnotations() {
+ return addNoParameterTypeStrengtheningAnnotation()
+ .addInternalMatchAnnotationOnMethodRule(
+ NoParameterTypeStrengtheningRule.RULE_NAME, NoParameterTypeStrengthening.class);
+ }
+
+ public T enableNoReturnTypeStrengtheningAnnotations() {
+ return addNoReturnTypeStrengtheningAnnotation()
+ .addInternalMatchAnnotationOnMethodRule(
+ NoReturnTypeStrengtheningRule.RULE_NAME, NoReturnTypeStrengthening.class);
+ }
+
public T enableNoUnusedInterfaceRemovalAnnotations() {
return addNoUnusedInterfaceRemovalAnnotations()
.addInternalMatchInterfaceRule(
diff --git a/src/test/java/com/android/tools/r8/TestBase.java b/src/test/java/com/android/tools/r8/TestBase.java
index 21113a9..d72151f 100644
--- a/src/test/java/com/android/tools/r8/TestBase.java
+++ b/src/test/java/com/android/tools/r8/TestBase.java
@@ -1788,6 +1788,10 @@
return AndroidApiLevel.K;
}
+ public static AndroidApiLevel apiLevelWithSuppressedExceptionsSupport() {
+ return AndroidApiLevel.K;
+ }
+
public static AndroidApiLevel apiLevelWithPcAsLineNumberSupport() {
return AndroidApiLevel.O;
}
diff --git a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
index 7fc1ecc..0c3b2ae 100644
--- a/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
+++ b/src/test/java/com/android/tools/r8/TestShrinkerBuilder.java
@@ -466,6 +466,18 @@
return addTestingAnnotation(NoHorizontalClassMerging.class);
}
+ public final T addNoMethodStaticizingAnnotation() {
+ return addTestingAnnotation(NoMethodStaticizing.class);
+ }
+
+ public final T addNoParameterTypeStrengtheningAnnotation() {
+ return addTestingAnnotation(NoParameterTypeStrengthening.class);
+ }
+
+ public final T addNoReturnTypeStrengtheningAnnotation() {
+ return addTestingAnnotation(NoReturnTypeStrengthening.class);
+ }
+
public final T addNoUnusedInterfaceRemovalAnnotations() {
return addTestingAnnotation(NoUnusedInterfaceRemoval.class);
}
diff --git a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
index 91c55e4..76e8b57 100644
--- a/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
+++ b/src/test/java/com/android/tools/r8/apimodel/ApiModelNoOutlineForFullyMockedTest.java
@@ -13,6 +13,7 @@
import static org.junit.Assume.assumeFalse;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
@@ -60,6 +61,7 @@
.apply(ApiModelingTestHelper::enableOutliningOfMethods)
.apply(ApiModelingTestHelper::enableStubbingOfClasses)
.enableInliningAnnotations()
+ .enableNoReturnTypeStrengtheningAnnotations()
.compile()
.applyIf(
parameters.isDexRuntime()
@@ -93,6 +95,8 @@
public static class Main {
@NeverInline
+ // TODO(b/214329925): Type strengthening should consult API database.
+ @NoReturnTypeStrengthening
public static Object create() {
return AndroidBuildVersion.VERSION >= 23 ? new LibraryClass() : null;
}
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
index f1498b6..2472119 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/bridgestokeep/KeepNonVisibilityBridgeMethodsTest.java
@@ -56,7 +56,8 @@
.addKeepRules(
"-alwaysinline class * { @"
+ AlwaysInline.class.getTypeName()
- + " !synthetic <methods>; }")
+ + " !synthetic <methods>; }",
+ "-noparametertypestrengthening class * { synthetic <methods>; }")
.enableNeverClassInliningAnnotations()
// TODO(b/120764902): MemberSubject.getOriginalName() is not working without the @NeverMerge
// annotation on DataAdapter.Observer.
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
index 45855d8..bf166ba 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/BridgeHoistingAccessibilityTest.java
@@ -77,6 +77,8 @@
.enableNoHorizontalClassMergingAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+ .noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
index fc6ac6b..e45bdcc 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/KeptBridgeHoistingTest.java
@@ -45,6 +45,8 @@
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+ .noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
index e987a43..2819311 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/NonSuperclassBridgeHoistingTest.java
@@ -47,6 +47,8 @@
.enableInliningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+ .noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
index ae77a45..bdd55fc 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/PositiveBridgeHoistingTest.java
@@ -56,6 +56,8 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+ .noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
diff --git a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
index 64c19e5..36cc8be 100644
--- a/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/bridgeremoval/hoisting/testclasses/BridgeHoistingAccessibilityTestClasses.java
@@ -31,6 +31,7 @@
}
}
+ @NoHorizontalClassMerging
@NoVerticalClassMerging
public static class AWithRangedInvoke {
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
index 497e4df..1d6c93f 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithDifferentInterfacesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -26,6 +27,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
@@ -78,7 +80,7 @@
public static class Main {
@NeverInline
- public static void foo(I i) {
+ public static void foo(@NoParameterTypeStrengthening I i) {
i.foo();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
index 9a5f503..29e7e0e 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/ClassesWithIdenticalInterfacesTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.TestParameters;
import org.junit.Test;
@@ -25,6 +26,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
inspector ->
@@ -72,7 +74,7 @@
public static class Main {
@NeverInline
- public static void foo(I i) {
+ public static void foo(@NoParameterTypeStrengthening I i) {
i.foo();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
index b03e236..508b10b 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/InterfacesVisibilityTest.java
@@ -32,6 +32,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNoHorizontalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
index 1ec170d..51b6b55 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/dispatch/OverrideMergeAbsentTest.java
@@ -10,6 +10,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.classmerging.horizontal.HorizontalClassMergingTestBase;
@@ -27,6 +28,7 @@
.addKeepMainRule(Main.class)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.addHorizontallyMergedClassesInspector(
@@ -79,7 +81,7 @@
public static class Main {
@NeverInline
- public static void doI(J i) {
+ public static void doI(@NoParameterTypeStrengthening J i) {
i.m();
}
diff --git a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
index d5a5c14..c857046 100644
--- a/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
+++ b/src/test/java/com/android/tools/r8/classmerging/horizontal/testclasses/InterfacesVisibilityTestClasses.java
@@ -7,12 +7,13 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
public class InterfacesVisibilityTestClasses {
public static class Invoker {
@NeverInline
- public static void invokeFoo(PackagePrivateInterface i) {
+ public static void invokeFoo(@NoParameterTypeStrengthening PackagePrivateInterface i) {
i.foo();
}
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
index d458f22..c5c6480 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BasicConstantDynamicTest.java
@@ -109,9 +109,9 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantName", Object.class)
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy2", A.class, "myConstant", "constantName", Object.class)
+ "condy2", A.class, "myConstant", false, "constantName", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
index 7b204ac..4cea83c 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodNotFoundConstantDynamicTest.java
@@ -102,7 +102,7 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "methodNotFound", "constantName", Object.class)
+ "condy1", A.class, "methodNotFound", false, "constantName", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
index 5f7e125..169a85d 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodPrivateConstantDynamicTest.java
@@ -101,7 +101,7 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantName", Object.class)
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
index 3f9a957..a693c21 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/BootstrapMethodVirtualConstantDynamicTest.java
@@ -101,7 +101,7 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantName", Object.class)
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
index 4810b1d..7deff9c 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicGetDeclaredMethodsTest.java
@@ -126,7 +126,7 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy", A.class, "myConstant", "constantName", Object.class)
+ "condy", A.class, "myConstant", false, "constantName", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodICCETest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodICCETest.java
new file mode 100644
index 0000000..feff719
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodICCETest.java
@@ -0,0 +1,141 @@
+// Copyright (c) 2022, 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.desugar.constantdynamic;
+
+import static com.android.tools.r8.DiagnosticsMatcher.diagnosticMessage;
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.junit.Assert.assertThrows;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.CompilationFailedException;
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestRuntime.CfVm;
+import com.android.tools.r8.cf.CfVersion;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import java.lang.invoke.MethodHandles;
+import java.util.List;
+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 ConstantDynamicInDefaultInterfaceMethodICCETest extends TestBase {
+
+ @Parameter() public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build());
+ }
+
+ private static final Class<?> MAIN_CLASS = A.class;
+
+ @Test
+ public void testReference() throws Exception {
+ assumeTrue(parameters.isCfRuntime());
+ assumeTrue(parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11));
+ assumeTrue(parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForJvm()
+ .addProgramClasses(MAIN_CLASS)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ }
+
+ @Test
+ public void testDesugaring() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClasses(MAIN_CLASS)
+ .addProgramClassFileData(getTransformedClasses())
+ .run(parameters.getRuntime(), MAIN_CLASS)
+ .applyIf(
+ // When not desugaring the CF code requires JDK 11.
+ DesugarTestConfiguration::isNotDesugared,
+ r -> {
+ if (parameters.isCfRuntime()
+ && parameters.getRuntime().asCf().isNewerThanOrEqual(CfVm.JDK11)) {
+ r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class);
+ } else {
+ r.assertFailureWithErrorThatThrows(UnsupportedClassVersionError.class);
+ }
+ })
+ .applyIf(
+ DesugarTestConfiguration::isDesugared,
+ r -> r.assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(parameters.isDexRuntime() || parameters.getApiLevel().isEqualTo(AndroidApiLevel.B));
+
+ testForR8(parameters.getBackend())
+ .addProgramClasses(MAIN_CLASS)
+ .addProgramClassFileData(getTransformedClasses())
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(MAIN_CLASS)
+ // TODO(b/198142625): Support CONSTANT_Dynamic output for class files.
+ .applyIf(
+ parameters.isCfRuntime(),
+ b -> {
+ assertThrows(
+ CompilationFailedException.class,
+ () ->
+ b.compileWithExpectedDiagnostics(
+ diagnostics -> {
+ diagnostics.assertOnlyErrors();
+ diagnostics.assertErrorsMatch(
+ diagnosticMessage(
+ containsString(
+ "Unsupported dynamic constant (not desugaring)")));
+ }));
+ },
+ b ->
+ b.run(parameters.getRuntime(), MAIN_CLASS)
+ .assertFailureWithErrorThatThrows(IncompatibleClassChangeError.class));
+ }
+
+ // Create a constant dynamic without the interface bit targeting an interface bootstrap method.
+ private byte[] getTransformedClasses() throws Exception {
+ return transformer(I.class)
+ .setVersion(CfVersion.V11)
+ .transformConstStringToConstantDynamic(
+ "condy1", I.class, "myConstant", false, "constantName", Object.class)
+ .transformConstStringToConstantDynamic(
+ "condy2", I.class, "myConstant", false, "constantName", Object.class)
+ .setPrivate(
+ I.class.getDeclaredMethod(
+ "myConstant", MethodHandles.Lookup.class, String.class, Class.class))
+ .transform();
+ }
+
+ public interface I {
+
+ default Object f() {
+ return "condy1"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ default Object g() {
+ return "condy2"; // Will be transformed to Constant_DYNAMIC.
+ }
+
+ /* private */ static Object myConstant(
+ MethodHandles.Lookup lookup, String name, Class<?> type) {
+ return new Object();
+ }
+ }
+
+ public static class A implements I {
+ public static void main(String[] args) {
+ A a = new A();
+ System.out.println(a.f() != null);
+ System.out.println(a.f() == a.g());
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
index a09ae6a..ad46d90 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/ConstantDynamicInDefaultInterfaceMethodTest.java
@@ -116,9 +116,9 @@
return transformer(I.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", I.class, "myConstant", "constantName", Object.class)
+ "condy1", I.class, "myConstant", true, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy2", I.class, "myConstant", "constantName", Object.class)
+ "condy2", I.class, "myConstant", true, "constantName", Object.class)
.setPrivate(
I.class.getDeclaredMethod(
"myConstant", MethodHandles.Lookup.class, String.class, Class.class))
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
index edc3cab..b7e540b 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/HierarchyConstantDynamicTest.java
@@ -102,12 +102,12 @@
transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantName", Object.class)
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
.transform(),
transformer(B.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", B.class, "myConstant", "constantName", Object.class)
+ "condy1", B.class, "myConstant", false, "constantName", Object.class)
.transform());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
index b3f900a..a7df375 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleBootstrapMethodConstantDynamicTest.java
@@ -110,13 +110,13 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant1", "constantName", Object.class)
+ "condy1", A.class, "myConstant1", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy2", A.class, "myConstant1", "constantName", Object.class)
+ "condy2", A.class, "myConstant1", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy3", A.class, "myConstant2", "constantName", Object.class)
+ "condy3", A.class, "myConstant2", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy4", A.class, "myConstant2", "constantName", Object.class)
+ "condy4", A.class, "myConstant2", false, "constantName", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
index 667473e..5ebc8da 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleNamedConstantDynamicTest.java
@@ -112,13 +112,13 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantF", Object.class)
+ "condy1", A.class, "myConstant", false, "constantF", Object.class)
.transformConstStringToConstantDynamic(
- "condy2", A.class, "myConstant", "constantF", Object.class)
+ "condy2", A.class, "myConstant", false, "constantF", Object.class)
.transformConstStringToConstantDynamic(
- "condy3", A.class, "myConstant", "constantG", Object.class)
+ "condy3", A.class, "myConstant", false, "constantG", Object.class)
.transformConstStringToConstantDynamic(
- "condy4", A.class, "myConstant", "constantG", Object.class)
+ "condy4", A.class, "myConstant", false, "constantG", Object.class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
index 48e0aab..fab07ce 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/MultipleTypesConstantDynamicTest.java
@@ -111,13 +111,13 @@
return transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantName", Object.class)
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy2", A.class, "myConstant", "constantName", Object.class)
+ "condy2", A.class, "myConstant", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy3", A.class, "myConstant", "constantName", boolean[].class)
+ "condy3", A.class, "myConstant", false, "constantName", boolean[].class)
.transformConstStringToConstantDynamic(
- "condy4", A.class, "myConstant", "constantName", boolean[].class)
+ "condy4", A.class, "myConstant", false, "constantName", boolean[].class)
.transform();
}
diff --git a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
index bff5968..1ae1ccf 100644
--- a/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/constantdynamic/SharedBootstrapMethodConstantDynamicTest.java
@@ -123,16 +123,16 @@
transformer(A.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy1", A.class, "myConstant", "constantName", Object.class)
+ "condy1", A.class, "myConstant", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy2", A.class, "myConstant", "constantName", Object.class)
+ "condy2", A.class, "myConstant", false, "constantName", Object.class)
.transform(),
transformer(B.class)
.setVersion(CfVersion.V11)
.transformConstStringToConstantDynamic(
- "condy3", A.class, "myConstant", "constantName", Object.class)
+ "condy3", A.class, "myConstant", false, "constantName", Object.class)
.transformConstStringToConstantDynamic(
- "condy4", A.class, "myConstant", "constantName", Object.class)
+ "condy4", A.class, "myConstant", false, "constantName", Object.class)
.transform());
}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
new file mode 100644
index 0000000..a74132f
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeClasspathTest.java
@@ -0,0 +1,60 @@
+// Copyright (c) 2022, 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.desugar.sealed;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.examples.jdk17.Sealed;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import java.util.List;
+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 SealedAttributeClasspathTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ private void runTest(TestCompilerBuilder<?, ?, ?, ?, ?> builder) throws Exception {
+ builder
+ .addClasspathFiles(Sealed.jar())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .run(parameters.getRuntime(), TestRunner.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ runTest(testForD8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTest(testForR8(parameters.getBackend()).addKeepMainRule(TestRunner.class));
+ }
+
+ public static class TestRunner {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
new file mode 100644
index 0000000..4eb4bd2
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeLibraryTest.java
@@ -0,0 +1,61 @@
+// Copyright (c) 2022, 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.desugar.sealed;
+
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestCompilerBuilder;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.examples.jdk17.Sealed;
+import com.android.tools.r8.utils.InternalOptions.TestingOptions;
+import java.util.List;
+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 SealedAttributeLibraryTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ private void runTest(TestCompilerBuilder<?, ?, ?, ?, ?> builder) throws Exception {
+ builder
+ .addDefaultRuntimeLibrary(parameters)
+ .addLibraryFiles(Sealed.jar())
+ .addInnerClasses(getClass())
+ .setMinApi(parameters.getApiLevel())
+ .addOptionsModification(TestingOptions::allowExperimentClassFileVersion)
+ .run(parameters.getRuntime(), TestRunner.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ assumeTrue(parameters.isDexRuntime());
+ runTest(testForD8(parameters.getBackend()));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ runTest(testForR8(parameters.getBackend()).addKeepMainRule(TestRunner.class));
+ }
+
+ public static class TestRunner {
+
+ public static void main(String[] args) {
+ System.out.println("Hello, world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
index 710f6f5..35293b5 100644
--- a/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/sealed/SealedAttributeTest.java
@@ -58,7 +58,8 @@
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics.assertErrorThatMatches(
- diagnosticMessage(containsString("Sealed classes are not supported")));
+ diagnosticMessage(
+ containsString("Sealed classes are not supported as program classes")));
});
});
}
@@ -75,7 +76,8 @@
.compileWithExpectedDiagnostics(
diagnostics -> {
diagnostics.assertErrorThatMatches(
- diagnosticMessage(containsString("Sealed classes are not supported")));
+ diagnosticMessage(
+ containsString("Sealed classes are not supported as program classes")));
});
});
}
diff --git a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/SuppressedExceptionsTest.java b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/SuppressedExceptionsTest.java
new file mode 100644
index 0000000..e1d397a
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/SuppressedExceptionsTest.java
@@ -0,0 +1,143 @@
+// Copyright (c) 2022, 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.desugar.suppressedexceptions;
+
+import static com.android.tools.r8.desugar.suppressedexceptions.TwrSuppressedExceptionsTest.getInvokesTo;
+import static com.android.tools.r8.desugar.suppressedexceptions.TwrSuppressedExceptionsTest.hasInvokesTo;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.StringUtils;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class SuppressedExceptionsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public SuppressedExceptionsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public boolean runtimeHasSuppressedExceptionsSupport() {
+ // TODO(b/214239152): Update this if desugaring is changed.
+ // Despite 4.0.4 being API level 15 and add suppressed being officially added in 19 it is
+ // actually implemented. Thus, the backport implementation will use the functionality and run
+ // as expected by RI.
+ return parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V4_0_4);
+ }
+
+ public boolean apiLevelHasSuppressedExceptionsSupport() {
+ return parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithSuppressedExceptionsSupport());
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClasses(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("FOO") : "NONE")
+ .inspectIf(
+ DesugarTestConfiguration::isDesugared,
+ inspector ->
+ hasInvokesTo(
+ inspector.clazz(TestClass.class).uniqueMethodWithName("main"),
+ "getSuppressed",
+ apiLevelHasSuppressedExceptionsSupport() ? 1 : 0))
+ .inspectIf(
+ DesugarTestConfiguration::isNotDesugared,
+ inspector ->
+ hasInvokesTo(
+ inspector.clazz(TestClass.class).uniqueMethodWithName("main"),
+ "getSuppressed",
+ 1));
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(
+ "R8 does not desugar CF so only run the high API variant.",
+ parameters.isDexRuntime() || parameters.getApiLevel().isGreaterThan(AndroidApiLevel.B));
+ testForR8(parameters.getBackend())
+ .addInnerClasses(SuppressedExceptionsTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("FOO") : "NONE")
+ .inspect(
+ inspector -> {
+ hasInvokesTo(
+ inspector.clazz(TestClass.class).uniqueMethodWithName("main"),
+ "getSuppressed",
+ apiLevelHasSuppressedExceptionsSupport() ? 1 : 0);
+ IntBox gets = new IntBox(0);
+ IntBox adds = new IntBox(0);
+ inspector.forAllClasses(
+ c ->
+ c.forAllMethods(
+ m -> {
+ gets.increment(getInvokesTo(m, "getSuppressed").size());
+ adds.increment(getInvokesTo(m, "addSuppressed").size());
+ }));
+ if (apiLevelHasSuppressedExceptionsSupport()) {
+ assertEquals(1, gets.get());
+ assertEquals(1, adds.get());
+ } else {
+ assertEquals(0, gets.get());
+ assertEquals(0, adds.get());
+ }
+ });
+ }
+
+ static class TestClass {
+
+ public static void foo() {
+ throw new RuntimeException("FOO");
+ }
+
+ public static void bar() {
+ try {
+ foo();
+ } catch (RuntimeException e) {
+ RuntimeException bar = new RuntimeException("BAR");
+ bar.addSuppressed(e);
+ throw bar;
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ bar();
+ } catch (RuntimeException e) {
+ Throwable[] suppressed = e.getSuppressed();
+ if (suppressed.length == 0) {
+ System.out.println("NONE");
+ } else {
+ for (Throwable throwable : suppressed) {
+ System.out.println(throwable.getMessage());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
new file mode 100644
index 0000000..e80af63
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/desugar/suppressedexceptions/TwrSuppressedExceptionsTest.java
@@ -0,0 +1,188 @@
+// Copyright (c) 2022, 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.desugar.suppressedexceptions;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assume.assumeTrue;
+
+import com.android.tools.r8.DesugarTestConfiguration;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.utils.AndroidApiLevel;
+import com.android.tools.r8.utils.IntBox;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import java.io.Closeable;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class TwrSuppressedExceptionsTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().withAllApiLevelsAlsoForCf().build();
+ }
+
+ public TwrSuppressedExceptionsTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public boolean runtimeHasSuppressedExceptionsSupport() {
+ // TODO(b/214239152): Update this if desugaring is changed.
+ // Despite 4.0.4 being API level 15 and add suppressed being officially added in 19 it is
+ // actually implemented. Thus, the backport implementation will use the functionality and run
+ // as expected by RI.
+ return parameters.isCfRuntime()
+ || parameters.getDexRuntimeVersion().isNewerThanOrEqual(Version.V4_0_4);
+ }
+
+ public boolean apiLevelHasSuppressedExceptionsSupport() {
+ return parameters
+ .getApiLevel()
+ .isGreaterThanOrEqualTo(apiLevelWithSuppressedExceptionsSupport());
+ }
+
+ public boolean apiLevelHasTwrCloseResourceSupport() {
+ return parameters.getApiLevel().isGreaterThanOrEqualTo(apiLevelWithTwrCloseResourceSupport());
+ }
+
+ @Test
+ public void testD8() throws Exception {
+ testForDesugaring(parameters)
+ .addProgramClasses(TestClass.class, MyClosable.class)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("CLOSE") : "NONE")
+ .inspectIf(
+ DesugarTestConfiguration::isDesugared,
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(TestClass.class);
+ hasInvokesTo(
+ clazz.uniqueMethodWithName("bar"),
+ "$closeResource",
+ apiLevelHasTwrCloseResourceSupport() ? 4 : 0);
+ if (apiLevelHasSuppressedExceptionsSupport()) {
+ hasInvokesTo(clazz.mainMethod(), "getSuppressed", 1);
+ } else {
+ inspector.forAllClasses(
+ c ->
+ c.forAllMethods(
+ m -> {
+ hasInvokesTo(m, "getSuppressed", 0);
+ hasInvokesTo(m, "addSuppressed", 0);
+ }));
+ }
+ })
+ .inspectIf(
+ DesugarTestConfiguration::isNotDesugared,
+ inspector -> {
+ ClassSubject clazz = inspector.clazz(TestClass.class);
+ hasInvokesTo(clazz.uniqueMethodWithName("bar"), "$closeResource", 4);
+ hasInvokesTo(clazz.mainMethod(), "getSuppressed", 1);
+ });
+ }
+
+ @Test
+ public void testR8() throws Exception {
+ assumeTrue(
+ "R8 does not desugar CF so only run the high API variant.",
+ parameters.isDexRuntime() || parameters.getApiLevel().isGreaterThan(AndroidApiLevel.B));
+ testForR8(parameters.getBackend())
+ .addInnerClasses(TwrSuppressedExceptionsTest.class)
+ .setMinApi(parameters.getApiLevel())
+ .addKeepMainRule(TestClass.class)
+ // TODO(b/214250388): Don't warn about AutoClosable in synthesized code.
+ .apply(
+ b -> {
+ if (!parameters.isCfRuntime() && !apiLevelHasTwrCloseResourceSupport()) {
+ b.addDontWarn(AutoCloseable.class);
+ }
+ })
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(
+ runtimeHasSuppressedExceptionsSupport() ? StringUtils.lines("CLOSE") : "NONE")
+ .inspect(
+ inspector -> {
+ IntBox gets = new IntBox(0);
+ IntBox adds = new IntBox(0);
+ inspector.forAllClasses(
+ c ->
+ c.forAllMethods(
+ m -> {
+ gets.increment(getInvokesTo(m, "getSuppressed").size());
+ adds.increment(getInvokesTo(m, "addSuppressed").size());
+ }));
+ if (apiLevelHasSuppressedExceptionsSupport()) {
+ hasInvokesTo(inspector.clazz(TestClass.class).mainMethod(), "getSuppressed", 1);
+ assertEquals(1, gets.get());
+ assertEquals(1, adds.get());
+ } else {
+ assertEquals(0, gets.get());
+ assertEquals(0, adds.get());
+ }
+ });
+ }
+
+ public static void hasInvokesTo(MethodSubject method, String callee, int count) {
+ List<InstructionSubject> getSuppressedCalls = getInvokesTo(method, callee);
+ assertEquals(count, getSuppressedCalls.size());
+ }
+
+ public static List<InstructionSubject> getInvokesTo(MethodSubject method, String callee) {
+ return method
+ .streamInstructions()
+ .filter(i -> i.isInvoke() && i.getMethod().getName().toString().equals(callee))
+ .collect(Collectors.toList());
+ }
+
+ static class MyClosable implements Closeable {
+
+ @Override
+ public void close() {
+ throw new RuntimeException("CLOSE");
+ }
+ }
+
+ static class TestClass {
+
+ public static void foo() {
+ throw new RuntimeException("FOO");
+ }
+
+ public static void bar() {
+ // Use twr twice to have javac generate a shared $closeResource helper.
+ try (MyClosable closable = new MyClosable()) {
+ foo();
+ }
+ try (MyClosable closable = new MyClosable()) {
+ foo();
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ bar();
+ } catch (Exception e) {
+ Throwable[] suppressed = e.getSuppressed();
+ if (suppressed.length == 0) {
+ System.out.println("NONE");
+ } else {
+ for (Throwable throwable : suppressed) {
+ System.out.println(throwable.getMessage());
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
index 1f15b61..10d15e9 100644
--- a/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
+++ b/src/test/java/com/android/tools/r8/desugar/twr/TwrCloseResourceDuplicationTest.java
@@ -75,10 +75,14 @@
.assertSuccessWithOutput(EXPECTED)
.inspect(
inspector -> {
- // There should be exactly one synthetic class besides the three program classes.
+ // There should be two synthetic classes besides the three program classes.
+ // One for the desugar version of TWR $closeResource and one for the
+ // Throwable.addSuppressed that is still present in the original $closeResource.
+ // TODO(b/214329923): If the original $closeResource is pruned this will decrease.
+ // TODO(b/168568827): Once we support a nested addSuppressed this will increase.
int expectedSynthetics =
parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport())
- ? 1
+ ? 2
: 0;
assertEquals(INPUT_CLASSES + expectedSynthetics, inspector.allClasses().size());
});
@@ -91,6 +95,7 @@
.addInnerClasses(getClass())
.addKeepMainRule(TestClass.class)
.addKeepClassAndMembersRules(Foo.class, Bar.class)
+ // TODO(b/214250388): Don't warn about synthetic code.
.applyIf(
parameters.getApiLevel().isLessThan(apiLevelWithTwrCloseResourceSupport()),
builder -> builder.addDontWarn("java.lang.AutoCloseable"))
diff --git a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
index ce3e3f9..0a00994 100644
--- a/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
+++ b/src/test/java/com/android/tools/r8/enumunboxing/FailingMethodEnumUnboxingTest.java
@@ -64,6 +64,8 @@
FailingParameterType.MyEnum.class))
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+ .noMinification()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::assertEnumsAsExpected);
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
index 1167e5a..fb6a937 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/CloseResourceMethod.java
@@ -49,9 +49,18 @@
}
}
} catch (Throwable e) {
- // NOTE: we don't call addSuppressed(...) since the call will be removed
- // by try-with-resource desugar anyways.
- throw throwable != null ? throwable : e;
+ if (throwable != null) {
+ // TODO(b/168568827): Directly call Throwable.addSuppressed once fixed.
+ try {
+ Method method = Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class);
+ method.invoke(throwable, e);
+ } catch (Exception ignore) {
+ // Don't add anything when not natively supported.
+ }
+ throw throwable;
+ } else {
+ throw e;
+ }
}
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
index ce75bd9..f955ca3 100644
--- a/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/GenerateBackportMethods.java
@@ -51,6 +51,7 @@
ShortMethods.class,
StreamMethods.class,
StringMethods.class,
+ ThrowableMethods.class,
UnsafeMethods.class);
protected final TestParameters parameters;
diff --git a/src/test/java/com/android/tools/r8/ir/desugar/backports/ThrowableMethods.java b/src/test/java/com/android/tools/r8/ir/desugar/backports/ThrowableMethods.java
new file mode 100644
index 0000000..ba17959
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/desugar/backports/ThrowableMethods.java
@@ -0,0 +1,29 @@
+// Copyright (c) 2022, 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.desugar.backports;
+
+import java.lang.reflect.Method;
+
+public final class ThrowableMethods {
+
+ public static void addSuppressed(Throwable receiver, Throwable suppressed) {
+ try {
+ Method method = Throwable.class.getDeclaredMethod("addSuppressed", Throwable.class);
+ method.invoke(receiver, suppressed);
+ } catch (Exception e) {
+ // Don't add anything when not natively supported.
+ }
+ }
+
+ public static Throwable[] getSuppressed(Throwable receiver) {
+ try {
+ Method method = Throwable.class.getDeclaredMethod("getSuppressed");
+ return (Throwable[]) method.invoke(receiver);
+ } catch (Exception e) {
+ // Don't return any when not natively supported.
+ return new Throwable[0];
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
index fba166a..62a44a1 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/R8InliningTest.java
@@ -326,7 +326,11 @@
m = clazz.method("int", "inlinable", ImmutableList.of("inlining.A"));
assertCounters(INLINABLE, INLINABLE, countInvokes(inspector, m));
- m = clazz.method("int", "notInlinable", ImmutableList.of("inlining.A"));
+ m =
+ clazz.method(
+ "int",
+ "notInlinable",
+ ImmutableList.of("inlining." + (allowAccessModification ? "B" : "A")));
assertCounters(INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
m = clazz.method("int", "notInlinableDueToMissingNpe", ImmutableList.of("inlining.A"));
@@ -338,14 +342,16 @@
NEVER_INLINABLE,
countInvokes(inspector, m));
- m = clazz.method("int", "notInlinableOnThrow", ImmutableList.of("java.lang.Throwable"));
+ m =
+ clazz.method(
+ "int", "notInlinableOnThrow", ImmutableList.of("java.lang.IllegalArgumentException"));
assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE, countInvokes(inspector, m));
m =
clazz.method(
"int",
"notInlinableDueToMissingNpeBeforeThrow",
- ImmutableList.of("java.lang.Throwable"));
+ ImmutableList.of("java.lang.IllegalArgumentException"));
assertCounters(ALWAYS_INLINABLE, NEVER_INLINABLE * 2, countInvokes(inspector, m));
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
index 5811471..0cd06dc 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeDirectPositiveTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -43,6 +44,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeDirectPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -107,6 +109,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private void test(Base arg) {
if (arg instanceof Sub1) {
System.out.println("Sub1");
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
index a32daad..2dafd1b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeStaticPositiveTest.java
@@ -8,6 +8,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -42,6 +43,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeStaticPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.addOptionsModification(
@@ -95,6 +97,7 @@
test(new Sub1()); // calls test with Sub1.
}
+ @NoParameterTypeStrengthening
@NeverInline
static void test(Base arg) {
if (arg instanceof Sub1) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
index 99a3ddf..382be79 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/dynamicupperboundtype/InvokeVirtualPositiveTest.java
@@ -9,6 +9,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -43,6 +44,7 @@
testForR8(parameters.getBackend())
.addInnerClasses(InvokeVirtualPositiveTest.class)
.addKeepMainRule(MAIN)
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableNeverClassInliningAnnotations()
.enableInliningAnnotations()
@@ -110,6 +112,7 @@
@NoVerticalClassMerging
@NeverClassInline
static class A {
+ @NoParameterTypeStrengthening
@NeverInline
void m(Base arg) {
if (arg instanceof Sub1) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
index fd605ac..11c44ad 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeDirectPositiveTest.java
@@ -57,7 +57,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicType(1).isNotNullType();
+ assertTrue(callSiteOptimizationInfo.getDynamicType(1).getNullability().isDefinitelyNotNull());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
index cf5cbe1..d181d3b 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/callsites/nullability/InvokeStaticPositiveTest.java
@@ -55,7 +55,7 @@
: "Unexpected revisit: " + method.toSourceString();
CallSiteOptimizationInfo callSiteOptimizationInfo =
method.getOptimizationInfo().getArgumentInfos();
- assert callSiteOptimizationInfo.getDynamicType(0).isNotNullType();
+ assertTrue(callSiteOptimizationInfo.getDynamicType(0).getNullability().isDefinitelyNotNull());
}
private void inspect(CodeInspector inspector) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
index 52736da..f3db3a7 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/dynamictype/DynamicTypeOptimizationTest.java
@@ -10,6 +10,7 @@
import static org.junit.Assert.assertTrue;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.StringUtils;
@@ -43,6 +44,7 @@
// Keep B to ensure that we will treat it as being instantiated.
.addKeepClassRulesWithAllowObfuscation(B.class)
.enableInliningAnnotations()
+ .enableNoReturnTypeStrengtheningAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::inspect)
@@ -134,6 +136,7 @@
}
@NeverInline
+ @NoReturnTypeStrengthening
private static I get() {
return new A();
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
index 78a0882..a6f80c4 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedarguments/UnusedArgumentsMixedTest.java
@@ -78,8 +78,11 @@
Assert.assertTrue(
method.getFinalName().equals("main")
|| (method.getFinalSignature().parameters.length == 1
- && (method.getFinalSignature().parameters[0].equals("int")
- || method.getFinalSignature().parameters[0].equals("java.lang.Object"))));
+ && (method.getFinalSignature().parameters[0].equals("int")
+ || method
+ .getFinalSignature()
+ .parameters[0]
+ .equals("java.lang.Integer"))));
});
}
}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
index c10d1db..fdefaac 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/unusedinterfaces/UnusedInterfaceWithDefaultMethodTest.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -45,6 +46,7 @@
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -82,6 +84,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private static void indirection(I obj) {
obj.m();
}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
index f72637c..f9e965c 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineChainTest.java
@@ -78,7 +78,11 @@
MethodSubject main = mainClass.mainMethod();
long checkParameterIsNotNull = countCall(main, "checkParameterIsNotNull");
long checkNotNullParameter = countCall(main, "checkNotNullParameter");
- if (parameters.isDexRuntime()
+ if (kotlinc.getCompilerVersion().isGreaterThan(KotlinCompilerVersion.KOTLINC_1_6_0)) {
+ assertEquals(
+ BooleanUtils.intValue(!allowAccessModification), checkNotNullParameter);
+ assertEquals(0, checkParameterIsNotNull);
+ } else if (parameters.isDexRuntime()
&& parameters.getApiLevel().isGreaterThanOrEqualTo(AndroidApiLevel.I)) {
assertEquals(0, checkNotNullParameter);
assertEquals(0, checkParameterIsNotNull);
diff --git a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
index eae75ac..472435a 100644
--- a/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/metadata/MetadataRewriteInCompanionTest.java
@@ -128,6 +128,8 @@
.addKeepRules("-keepclassmembers class **.B { *** Companion; }")
// Keep the class of the companion class.
.addKeepRules("-keep class **.*$Companion")
+ // TODO(b/173398086): uniqueMethodWithName() does not work with signature changes.
+ .addKeepRules("-noreturntypestrengthening class **.B { *** access$getElt1$cp(...); }")
// No rule for Super, but will be kept and renamed.
.addKeepAttributes(ProguardKeepAttributes.RUNTIME_VISIBLE_ANNOTATIONS)
// To keep @JvmField annotation
@@ -135,6 +137,7 @@
// To keep ...$Companion structure
.addKeepAttributes(ProguardKeepAttributes.INNER_CLASSES)
.addKeepAttributes(ProguardKeepAttributes.ENCLOSING_METHOD)
+ .enableProguardTestOptions()
.compile()
.inspect(codeInspector -> inspect(codeInspector, false))
.writeToZip();
diff --git a/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java b/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
index ad0cf84..783b9f1 100644
--- a/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
+++ b/src/test/java/com/android/tools/r8/maindexlist/MainDexDevirtualizerTest.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.R8FullTestBuilder;
import com.android.tools.r8.TestBase;
@@ -108,6 +109,7 @@
Box<String> mainDexStringList = new Box<>("");
testForR8(parameters.getBackend())
.addProgramClasses(I.class, Provider.class, A.class, Main.class)
+ .enableNoReturnTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
@@ -156,6 +158,7 @@
public static class Provider {
@NeverInline
+ @NoReturnTypeStrengthening
public static I getImpl() {
return new A(); // <-- We will call-site optimize getImpl() to always return A.
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
index f1fc86d..25674a2 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceBridgeWithSubTypeTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -51,6 +52,7 @@
.addKeepClassAndMembersRules(I.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.addHorizontallyMergedClassesInspector(
HorizontallyMergedClassesInspector::assertNoClassesMerged)
@@ -94,6 +96,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private static void callJ(A a) {
a.foo();
}
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
index e11bdfe..4036e4a 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingRemoveInterfaceDefaultBridgeTest.java
@@ -11,6 +11,7 @@
import com.android.tools.r8.NeverInline;
import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -49,6 +50,7 @@
.addKeepClassAndMembersRules(I.class)
.enableInliningAnnotations()
.enableNoHorizontalClassMergingAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.run(parameters.getRuntime(), newMainTypeName)
.assertSuccessWithOutputLines("I::foo")
@@ -85,6 +87,7 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
private static void callJ(J j) {
j.foo();
}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
index 298c597..c18b3d7 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/B.java
@@ -4,6 +4,7 @@
package com.android.tools.r8.naming.overloadaggressively;
import com.android.tools.r8.NeverPropagateValue;
+import com.android.tools.r8.NoReturnTypeStrengthening;
public class B {
volatile int f1 = 8;
@@ -16,6 +17,7 @@
}
@NeverPropagateValue
+ @NoReturnTypeStrengthening
public Object getF2() {
return f2;
}
diff --git a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
index cbd22a0..c54b51b 100644
--- a/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
+++ b/src/test/java/com/android/tools/r8/naming/overloadaggressively/OverloadAggressivelyTest.java
@@ -11,6 +11,8 @@
import com.android.tools.r8.R8Command;
import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.ToolHelper;
import com.android.tools.r8.ToolHelper.ProcessResult;
import com.android.tools.r8.graph.DexEncodedField;
@@ -27,19 +29,18 @@
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 OverloadAggressivelyTest extends TestBase {
- private Backend backend;
+ @Parameter(0)
+ public TestParameters parameters;
- @Parameterized.Parameters(name = "Backend: {0}")
- public static Backend[] data() {
- return ToolHelper.getBackends();
- }
-
- public OverloadAggressivelyTest(Backend backend) {
- this.backend = backend;
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withCfRuntimes().build();
}
private AndroidApp runR8(AndroidApp app, Class<?> main, Path out, boolean overloadaggressively)
@@ -56,8 +57,8 @@
keepMainProguardConfiguration(main),
overloadaggressively ? "-overloadaggressively" : ""),
Origin.unknown())
- .setOutput(out, outputMode(backend))
- .addLibraryFiles(TestBase.runtimeJar(backend))
+ .setOutput(out, outputMode(parameters.getBackend()))
+ .addLibraryFiles(parameters.getDefaultRuntimeLibrary())
.build();
return ToolHelper.runR8(
command,
@@ -68,10 +69,10 @@
}
private ProcessResult runRaw(AndroidApp app, String main) throws IOException {
- if (backend == Backend.DEX) {
+ if (parameters.isDexRuntime()) {
return runOnArtRaw(app, main);
} else {
- assert backend == Backend.CF;
+ assert parameters.isCfRuntime();
return runOnJavaRaw(app, main, Collections.emptyList());
}
}
@@ -178,19 +179,20 @@
String expected = StringUtils.lines("diff: 0", "d8 v.s. d8", "r8 v.s. r8");
String expectedOverloadAggressively = StringUtils.lines("diff: 0", "d8 v.s. 8", "r8 v.s. 8");
- if (backend.isCf()) {
+ if (parameters.isCfRuntime()) {
testForJvm().addTestClasspath().run(MethodResolution.class).assertSuccessWithOutput(expected);
}
- testForR8Compat(backend)
+ testForR8Compat(parameters.getBackend())
.addProgramClasses(MethodResolution.class, B.class)
.addKeepMainRule(MethodResolution.class)
.addOptionsModification(options -> options.inlinerOptions().enableInlining = false)
.applyIf(overloadaggressively, builder -> builder.addKeepRules("-overloadaggressively"))
.enableMemberValuePropagationAnnotations()
+ .enableNoReturnTypeStrengtheningAnnotations()
.compile()
.inspect(inspector -> inspect(inspector, overloadaggressively))
- .run(MethodResolution.class)
+ .run(parameters.getRuntime(), MethodResolution.class)
.applyIf(
overloadaggressively,
runResult -> runResult.assertSuccessWithOutput(expectedOverloadAggressively),
@@ -218,7 +220,7 @@
@Test
public void testMethodResolution_aggressively() throws Exception {
- assumeTrue(backend == Backend.CF);
+ assumeTrue(parameters.isCfRuntime());
methodResolution(true);
}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
new file mode 100644
index 0000000..91abde9
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ParameterTypeStrengtheningTest.java
@@ -0,0 +1,124 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 ParameterTypeStrengtheningTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .enableNoHorizontalClassMergingAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ ClassSubject bClassSubject = inspector.clazz(B.class);
+ assertThat(bClassSubject, isPresent());
+
+ // Method testA(I) should be rewritten to testA(A).
+ MethodSubject testAMethodSubject = mainClassSubject.uniqueMethodWithName("testA");
+ assertThat(testAMethodSubject, isPresent());
+ assertEquals(
+ aClassSubject.getFinalName(),
+ testAMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+
+ // Method testB(I) should be rewritten to testB(B).
+ MethodSubject testBMethodSubject = mainClassSubject.uniqueMethodWithName("testB");
+ assertThat(testBMethodSubject, isPresent());
+ assertEquals(
+ bClassSubject.getFinalName(),
+ testBMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "B");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ testA(new A());
+ testB(getB());
+ }
+
+ @NeverInline
+ static void testA(I i) {
+ i.m();
+ }
+
+ @NeverInline
+ static void testB(I i) {
+ i.m();
+ }
+
+ @NeverInline
+ static I getB() {
+ return new B();
+ }
+ }
+
+ interface I {
+
+ void m();
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class A implements I {
+
+ @Override
+ public void m() {
+ System.out.println("A");
+ }
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ static class B implements I {
+
+ @Override
+ public void m() {
+ System.out.println("B");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
new file mode 100644
index 0000000..89dccdb
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/optimize/argumentpropagation/ReturnTypeStrengtheningTest.java
@@ -0,0 +1,116 @@
+// Copyright (c) 2022, the R8 project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+package com.android.tools.r8.optimize.argumentpropagation;
+
+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.NeverInline;
+import com.android.tools.r8.NoVerticalClassMerging;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundClassSubject;
+import com.android.tools.r8.utils.codeinspector.FoundMethodSubject;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 ReturnTypeStrengtheningTest extends TestBase {
+
+ @Parameter(0)
+ public TestParameters parameters;
+
+ @Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .enableInliningAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ // TODO(b/173398086): uniqueMethodWithName() does not work with argument changes.
+ .noMinification()
+ .setMinApi(parameters.getApiLevel())
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ ClassSubject aClassSubject = inspector.clazz(A.class);
+ assertThat(aClassSubject, isPresent());
+
+ // Return type of get() should be strengthened to A.
+ MethodSubject getMethodSubject = mainClassSubject.uniqueMethodWithName("get");
+ assertThat(getMethodSubject, isPresent());
+ assertEquals(
+ aClassSubject.getFinalName(),
+ getMethodSubject.getProgramMethod().getReturnType().getTypeName());
+
+ // Method consume(I) should be rewritten to consume(A).
+ MethodSubject testBMethodSubject = mainClassSubject.uniqueMethodWithName("consume");
+ assertThat(testBMethodSubject, isPresent());
+ assertEquals(
+ aClassSubject.getFinalName(),
+ testBMethodSubject.getProgramMethod().getParameter(0).getTypeName());
+
+ // There should be no casts in the application.
+ for (FoundClassSubject classSubject : inspector.allClasses()) {
+ for (FoundMethodSubject methodSubject : classSubject.allMethods()) {
+ assertTrue(
+ methodSubject
+ .streamInstructions()
+ .noneMatch(InstructionSubject::isCheckCast));
+ }
+ }
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ consume(get());
+ }
+
+ @NeverInline
+ static I get() {
+ return new A();
+ }
+
+ @NeverInline
+ static void consume(I i) {
+ i.m();
+ }
+ }
+
+ @NoVerticalClassMerging
+ interface I {
+
+ void m();
+ }
+
+ static class A implements I {
+
+ @Override
+ public void m() {
+ System.out.println("A");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
index 951a2b6..4eff919 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfPackagePrivateMethodsTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -31,6 +32,7 @@
.apply(this::configureRepackaging)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -52,11 +54,13 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
static void greet(HelloGreeterBase greeter) {
greeter.greet();
}
@NeverInline
+ @NoParameterTypeStrengthening
static void greet(WorldGreeterBase greeter) {
greeter.greet();
}
diff --git a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
index 7747cbd..56f6a41 100644
--- a/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
+++ b/src/test/java/com/android/tools/r8/repackage/RepackageWithOverridesOfProtectedMethodsTest.java
@@ -8,6 +8,7 @@
import com.android.tools.r8.NeverClassInline;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoParameterTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.utils.codeinspector.CodeInspector;
@@ -31,6 +32,7 @@
.apply(this::configureRepackaging)
.enableInliningAnnotations()
.enableNeverClassInliningAnnotations()
+ .enableNoParameterTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.setMinApi(parameters.getApiLevel())
.compile()
@@ -52,11 +54,13 @@
}
@NeverInline
+ @NoParameterTypeStrengthening
static void greet(HelloGreeterBase greeter) {
greeter.greet();
}
@NeverInline
+ @NoParameterTypeStrengthening
static void greet(WorldGreeterBase greeter) {
greeter.greet();
}
diff --git a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
index 873b1e4..eeb91af 100644
--- a/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ParameterTypeTest.java
@@ -3,6 +3,7 @@
// BSD-style license that can be found in the LICENSE file.
package com.android.tools.r8.shaking;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isAbsent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresentAndRenamed;
import static org.hamcrest.CoreMatchers.containsString;
@@ -141,7 +142,11 @@
.inspector();
ClassSubject superInterface1 = inspector.clazz(B112452064SuperInterface1.class);
- assertThat(superInterface1, isPresentAndRenamed());
+ if (enableUnusedInterfaceRemoval && enableVerticalClassMerging) {
+ assertThat(superInterface1, isAbsent());
+ } else {
+ assertThat(superInterface1, isPresentAndRenamed());
+ }
MethodSubject foo = superInterface1.uniqueMethodWithName("foo");
assertThat(foo, not(isPresent()));
ClassSubject superInterface2 = inspector.clazz(B112452064SuperInterface2.class);
diff --git a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
index e377809..93f8a34 100644
--- a/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/ifrule/membervaluepropagation/IfWithMethodValuePropagationTest.java
@@ -23,7 +23,7 @@
@Parameterized.Parameters(name = "{0}")
public static TestParametersCollection data() {
- return getTestParameters().withAllRuntimes().build();
+ return getTestParameters().withAllRuntimesAndApiLevels().build();
}
public IfWithMethodValuePropagationTest(TestParameters parameters) {
@@ -41,8 +41,8 @@
"}",
"-keep class " + Layout.class.getTypeName())
.addLibraryClasses(Library.class)
- .addLibraryFiles(runtimeJar(parameters))
- .setMinApi(parameters.getRuntime())
+ .addDefaultRuntimeLibrary(parameters)
+ .setMinApi(parameters.getApiLevel())
.compile()
.inspect(this::verifyOutput)
.addRunClasspathFiles(
@@ -50,7 +50,7 @@
.addProgramClasses(Library.class)
.addClasspathClasses(Layout.class)
.addKeepAllClassesRule()
- .setMinApi(parameters.getRuntime())
+ .setMinApi(parameters.getApiLevel())
.compile()
.writeToZip())
.run(parameters.getRuntime(), TestClass.class)
@@ -58,7 +58,7 @@
}
private void verifyOutput(CodeInspector inspector) {
- // R.ID has been inlined.
+ // R.ID has been removed by dead code removal after member value propagation.
assertThat(inspector.clazz(R.class), not(isPresent()));
// Layout is kept by the conditional rule.
diff --git a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
index 528a4ac..b848b1c 100644
--- a/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
+++ b/src/test/java/com/android/tools/r8/shaking/keptgraph/KeptByReachableSubclassTest.java
@@ -7,6 +7,7 @@
import static org.junit.Assert.assertEquals;
import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NoReturnTypeStrengthening;
import com.android.tools.r8.NoVerticalClassMerging;
import com.android.tools.r8.TestBase;
import com.android.tools.r8.TestParameters;
@@ -44,6 +45,7 @@
GraphInspector inspector =
testForR8(parameters.getBackend())
.enableGraphInspector()
+ .enableNoReturnTypeStrengtheningAnnotations()
.enableNoVerticalClassMergingAnnotations()
.enableInliningAnnotations()
.addProgramClasses(CLASS, A.class, B.class)
@@ -95,6 +97,7 @@
public static class TestClass {
@NeverInline
+ @NoReturnTypeStrengthening
static A create() {
return new B();
}
diff --git a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
index 6264a4b..bb46834 100644
--- a/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
+++ b/src/test/java/com/android/tools/r8/synthesis/SyntheticItemsTestUtils.java
@@ -109,6 +109,11 @@
return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.TWR_CLOSE_RESOURCE);
}
+ public static boolean isMaybeExternalSuppressedExceptionMethod(ClassReference reference) {
+ // The suppressed exception methods are grouped with the backports.
+ return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.BACKPORT);
+ }
+
public static boolean isExternalOutlineClass(ClassReference reference) {
return SyntheticNaming.isSynthetic(reference, Phase.EXTERNAL, SyntheticKind.OUTLINE);
}
diff --git a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
index 8644e9a..d8557a0 100644
--- a/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
+++ b/src/test/java/com/android/tools/r8/transformers/ClassFileTransformer.java
@@ -978,6 +978,7 @@
String constantName,
Class<?> bootstrapMethodHolder,
String bootstrapMethodName,
+ boolean isInterfaceInvoke,
String name,
Class<?> type) {
return addMethodTransformer(
@@ -996,7 +997,7 @@
DescriptorUtils.getClassBinaryName(bootstrapMethodHolder),
bootstrapMethodName,
bootstrapMethodSignature,
- bootstrapMethodHolder.isInterface()),
+ isInterfaceInvoke),
new Object[] {}));
} else {
super.visitLdcInsn(value);
diff --git a/tools/git_sync_cl_chain.py b/tools/git_sync_cl_chain.py
index ea67f63..09da33e 100755
--- a/tools/git_sync_cl_chain.py
+++ b/tools/git_sync_cl_chain.py
@@ -44,6 +44,9 @@
help='Delete closed branches',
choices=['y', 'n', 'ask'],
default='ask')
+ result.add_option('--from_branch', '-f',
+ help='Uppermost upstream to sync from',
+ default='main')
result.add_option('--leave_upstream', '--leave-upstream',
help='To not update the upstream of the first open branch',
action='store_true')
@@ -81,14 +84,14 @@
break
assert current_branch is not None
- if current_branch.upstream == None:
+ if is_root_branch(current_branch, options):
print('Nothing to sync')
return
stack = []
while current_branch:
stack.append(current_branch)
- if current_branch.upstream is None:
+ if is_root_branch(current_branch, options):
break
current_branch = get_branch_with_name(current_branch.upstream, branches)
@@ -169,6 +172,9 @@
def get_status_for_current_branch():
return utils.RunCmd(['git', 'cl', 'status', '--field', 'status'], quiet=True)[0].strip()
+def is_root_branch(branch, options):
+ return branch == options.from_branch or branch.upstream is None
+
def pull_for_current_branch(branch, options):
if branch.name == 'main' and options.skip_main:
return