Merge commit '26c1e34b046bd1e27d04a9fa7829e6f5be09abc7' into dev-release
Change-Id: I05a95232b173abb343230b9f855c707ab8ffeac4
diff --git a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
index 5b9e401..362a9e0 100644
--- a/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
+++ b/src/main/java/com/android/tools/r8/horizontalclassmerging/InstanceInitializerMergerCollection.java
@@ -12,6 +12,7 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexType;
import com.android.tools.r8.horizontalclassmerging.InstanceInitializerMerger.Builder;
+import com.android.tools.r8.utils.IterableUtils;
import com.android.tools.r8.utils.collections.ProgramMethodSet;
import it.unimi.dsi.fastutil.objects.Reference2IntMap;
import java.util.ArrayList;
@@ -25,12 +26,12 @@
public class InstanceInitializerMergerCollection {
private final List<InstanceInitializerMerger> instanceInitializerMergers;
- private final Map<InstanceInitializerDescription, InstanceInitializerMerger>
+ private final Map<InstanceInitializerDescription, List<InstanceInitializerMerger>>
equivalentInstanceInitializerMergers;
private InstanceInitializerMergerCollection(
List<InstanceInitializerMerger> instanceInitializerMergers,
- Map<InstanceInitializerDescription, InstanceInitializerMerger>
+ Map<InstanceInitializerDescription, List<InstanceInitializerMerger>>
equivalentInstanceInitializerMergers) {
this.instanceInitializerMergers = instanceInitializerMergers;
this.equivalentInstanceInitializerMergers = equivalentInstanceInitializerMergers;
@@ -75,7 +76,7 @@
}
}));
- Map<InstanceInitializerDescription, InstanceInitializerMerger>
+ Map<InstanceInitializerDescription, List<InstanceInitializerMerger>>
equivalentInstanceInitializerMergers = new LinkedHashMap<>();
buildersByDescription.forEach(
(description, builder) -> {
@@ -88,7 +89,9 @@
buildersWithoutDescription.addAll(
instanceInitializerMerger.getInstanceInitializers());
} else {
- equivalentInstanceInitializerMergers.put(description, instanceInitializerMerger);
+ equivalentInstanceInitializerMergers
+ .computeIfAbsent(description, ignoreKey(ArrayList::new))
+ .add(instanceInitializerMerger);
}
}
});
@@ -127,7 +130,7 @@
public void forEach(Consumer<InstanceInitializerMerger> consumer) {
instanceInitializerMergers.forEach(consumer);
- equivalentInstanceInitializerMergers.values().forEach(consumer);
+ IterableUtils.flatten(equivalentInstanceInitializerMergers.values()).forEach(consumer);
}
public void setObsolete() {
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java
index c8455fc..c425cb7 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/passes/SplitBranch.java
@@ -27,7 +27,7 @@
public class SplitBranch extends CodeRewriterPass<AppInfo> {
- private static final boolean ALLOW_PARTIAL_REWRITE = true;
+ private static final boolean ALLOW_PARTIAL_REWRITE = false;
public SplitBranch(AppView<?> appView) {
super(appView);
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
index e204f8e..6812690 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClass.java
@@ -24,7 +24,6 @@
import com.android.tools.r8.graph.DexProto;
import com.android.tools.r8.graph.DexString;
import com.android.tools.r8.graph.DexType;
-import com.android.tools.r8.graph.DexValue.DexValueNull;
import com.android.tools.r8.graph.FieldAccessFlags;
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.MethodResolutionResult;
@@ -34,14 +33,10 @@
import com.android.tools.r8.ir.desugar.lambda.ForcefullyMovedLambdaMethodConsumer;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring;
import com.android.tools.r8.ir.desugar.lambda.LambdaInstructionDesugaring.DesugarInvoke;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedback;
-import com.android.tools.r8.ir.optimize.info.OptimizationFeedbackSimple;
import com.android.tools.r8.synthesis.SyntheticProgramClassBuilder;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
-import org.objectweb.asm.Opcodes;
/**
* Represents lambda class generated for a lambda descriptor in context of lambda instantiation
@@ -64,16 +59,12 @@
public static final String JAVAC_EXPECTED_LAMBDA_METHOD_PREFIX = "lambda$";
public static final String R8_LAMBDA_ACCESSOR_METHOD_PREFIX = "$r8$lambda$";
- private static final OptimizationFeedback feedback = OptimizationFeedbackSimple.getInstance();
-
final AppView<?> appView;
final LambdaInstructionDesugaring desugaring;
public final DexType type;
public LambdaDescriptor descriptor;
public final DexMethod constructor;
- final DexMethod classConstructor;
private final DexMethod factoryMethod;
- public final DexField lambdaField;
public final Target target;
// Considered final but is set after due to circularity in allocation.
@@ -101,15 +92,6 @@
this.target = createTarget(accessedFrom);
- boolean statelessSingleton = isStatelessSingleton();
- this.classConstructor =
- statelessSingleton
- ? factory.createMethod(type, constructorProto, factory.classConstructorMethodName)
- : null;
- this.lambdaField =
- statelessSingleton
- ? factory.createField(type, type, factory.lambdaInstanceFieldName)
- : null;
this.factoryMethod =
useFactoryMethodForConstruction
|| appView.options().testing.alwaysGenerateLambdaFactoryMethods
@@ -147,7 +129,6 @@
private void synthesizeLambdaClass(
SyntheticProgramClassBuilder builder, DesugarInvoke desugarInvoke) {
builder.setInterfaces(descriptor.interfaces);
- synthesizeStaticFields(builder);
synthesizeInstanceFields(builder);
synthesizeDirectMethods(builder);
synthesizeVirtualMethods(builder, desugarInvoke);
@@ -162,10 +143,6 @@
appView.dexItemFactory().createString("f$" + index));
}
- public final boolean isStatelessSingleton() {
- return appView.options().createSingletonsForStatelessLambdas && descriptor.isStateless();
- }
-
public boolean hasFactoryMethod() {
return factoryMethod != null;
}
@@ -217,15 +194,12 @@
// Synthesize direct methods.
private void synthesizeDirectMethods(SyntheticProgramClassBuilder builder) {
- boolean statelessSingleton = isStatelessSingleton();
- List<DexEncodedMethod> methods = new ArrayList<>(statelessSingleton ? 2 : 1);
+ List<DexEncodedMethod> methods = new ArrayList<>(hasFactoryMethod() ? 2 : 1);
// Constructor.
MethodAccessFlags accessFlags =
MethodAccessFlags.fromSharedAccessFlags(
- (statelessSingleton ? Constants.ACC_PRIVATE : Constants.ACC_PUBLIC)
- | Constants.ACC_SYNTHETIC,
- true);
+ Constants.ACC_PUBLIC | Constants.ACC_SYNTHETIC, true);
methods.add(
DexEncodedMethod.syntheticBuilder()
.setMethod(constructor)
@@ -236,19 +210,6 @@
.build());
// Class constructor for stateless lambda classes.
- if (statelessSingleton) {
- methods.add(
- DexEncodedMethod.syntheticBuilder()
- .setMethod(classConstructor)
- .setAccessFlags(
- MethodAccessFlags.fromSharedAccessFlags(
- Constants.ACC_SYNTHETIC | Constants.ACC_STATIC, true))
- .setCode(LambdaClassConstructorSourceCode.build(this))
- // The api level is computed when tracing.
- .disableAndroidApiLevelCheck()
- .build());
- feedback.classInitializerMayBePostponed(methods.get(1));
- }
if (hasFactoryMethod()) {
methods.add(
DexEncodedMethod.syntheticBuilder()
@@ -283,43 +244,6 @@
builder.setInstanceFields(fields);
}
- // Synthesize static fields to represent singleton instance.
- private void synthesizeStaticFields(SyntheticProgramClassBuilder builder) {
- if (isStatelessSingleton()) {
- // Create instance field for stateless lambda.
- assert this.lambdaField != null;
- builder.setStaticFields(
- Collections.singletonList(
- DexEncodedField.syntheticBuilder()
- .setField(this.lambdaField)
- .setAccessFlags(
- FieldAccessFlags.fromSharedAccessFlags(
- Constants.ACC_PUBLIC
- | Constants.ACC_FINAL
- | Constants.ACC_SYNTHETIC
- | Constants.ACC_STATIC))
- .setStaticValue(DexValueNull.NULL)
- // The api level is computed when tracing.
- .disableAndroidApiLevelCheck()
- .build()));
- }
- }
-
- public static int getAsmOpcodeForInvokeType(MethodHandleType type) {
- switch (type) {
- case INVOKE_INTERFACE:
- return Opcodes.INVOKEINTERFACE;
- case INVOKE_STATIC:
- return Opcodes.INVOKESTATIC;
- case INVOKE_DIRECT:
- return Opcodes.INVOKESPECIAL;
- case INVOKE_INSTANCE:
- return Opcodes.INVOKEVIRTUAL;
- default:
- throw new Unreachable("Unexpected method handle type: " + type);
- }
- }
-
// Creates a delegation target for this particular lambda class. Note that we
// should always be able to create targets for the lambdas we support.
private Target createTarget(ProgramMethod accessedFrom) {
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java b/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
deleted file mode 100644
index ddcb68d..0000000
--- a/src/main/java/com/android/tools/r8/ir/desugar/LambdaClassConstructorSourceCode.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// Copyright (c) 2017, 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;
-
-import com.android.tools.r8.cf.code.CfInvoke;
-import com.android.tools.r8.cf.code.CfNew;
-import com.android.tools.r8.cf.code.CfReturnVoid;
-import com.android.tools.r8.cf.code.CfStackInstruction;
-import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
-import com.android.tools.r8.cf.code.CfStaticFieldWrite;
-import com.android.tools.r8.graph.CfCode;
-import com.google.common.collect.ImmutableList;
-import org.objectweb.asm.Opcodes;
-
-// Source code representing synthesized lambda class constructor.
-// Used for stateless lambdas to instantiate singleton instance.
-final class LambdaClassConstructorSourceCode {
-
- public static CfCode build(LambdaClass lambda) {
- int maxStack = 2;
- int maxLocals = 0;
- return new CfCode(
- lambda.type,
- maxStack,
- maxLocals,
- ImmutableList.of(
- new CfNew(lambda.type),
- new CfStackInstruction(Opcode.Dup),
- new CfInvoke(Opcodes.INVOKESPECIAL, lambda.constructor, false),
- new CfStaticFieldWrite(lambda.lambdaField, lambda.lambdaField),
- new CfReturnVoid()));
- }
-}
diff --git a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
index b49e21a..d92a51a 100644
--- a/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
+++ b/src/main/java/com/android/tools/r8/ir/desugar/lambda/LambdaInstructionDesugaring.java
@@ -11,7 +11,6 @@
import com.android.tools.r8.cf.code.CfNew;
import com.android.tools.r8.cf.code.CfStackInstruction;
import com.android.tools.r8.cf.code.CfStackInstruction.Opcode;
-import com.android.tools.r8.cf.code.CfStaticFieldRead;
import com.android.tools.r8.cf.code.CfStore;
import com.android.tools.r8.contexts.CompilationContext.MethodProcessingContext;
import com.android.tools.r8.graph.AppView;
@@ -144,11 +143,6 @@
new CfInvoke(Opcodes.INVOKESTATIC, lambdaClass.getFactoryMethod(), false));
}
- if (lambdaClass.isStatelessSingleton()) {
- return ImmutableList.of(
- new CfStaticFieldRead(lambdaClass.lambdaField, lambdaClass.lambdaField));
- }
-
DexTypeList captureTypes = lambdaClass.descriptor.captures;
Deque<CfInstruction> replacement = new ArrayDeque<>(3 + captureTypes.size() * 2);
replacement.add(new CfNew(lambdaClass.getType()));
diff --git a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
index 2f02791..1a15081 100644
--- a/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
+++ b/src/main/java/com/android/tools/r8/optimize/MemberRebindingAnalysis.java
@@ -170,7 +170,9 @@
}
private boolean isInvokeSuperToAbstractMethod(DexClassAndMethod method, InvokeType invokeType) {
- return method.getAccessFlags().isAbstract() && invokeType.isSuper();
+ return options.canHaveSuperInvokeToAbstractMethodBug()
+ && method.getAccessFlags().isAbstract()
+ && invokeType.isSuper();
}
public static DexField validMemberRebindingTargetFor(
diff --git a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
index 02b5231..a6e1da7 100644
--- a/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
+++ b/src/main/java/com/android/tools/r8/optimize/singlecaller/SingleCallerInliner.java
@@ -68,8 +68,8 @@
}
public void run(ExecutorService executorService) throws ExecutionException {
- ProgramMethodSet monomorphicVirtualMethods =
- computeMonomorphicVirtualRootMethods(executorService);
+ // TODO(b/335584013): Re-enable monomorphic method analysis.
+ ProgramMethodSet monomorphicVirtualMethods = ProgramMethodSet.empty();
ProgramMethodMap<ProgramMethod> singleCallerMethods =
new SingleCallerScanner(appView, monomorphicVirtualMethods)
.getSingleCallerMethods(executorService);
@@ -87,6 +87,7 @@
// deal with (rooted) virtual methods that do not override abstract/interface methods. In order to
// also deal with virtual methods that override abstract/interface methods we would need to record
// calls to the abstract/interface methods as calls to the non-abstract virtual method.
+ @SuppressWarnings("UnusedMethod")
private ProgramMethodSet computeMonomorphicVirtualRootMethods(ExecutorService executorService)
throws ExecutionException {
ImmediateProgramSubtypingInfo immediateSubtypingInfo =
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 707d77a..065408c 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -488,11 +488,6 @@
// Boolean value indicating that byte code pass through may be enabled.
public boolean enableCfByteCodePassThrough = false;
- // Flag to control the representation of stateless lambdas.
- // See b/222081665 for context.
- public boolean createSingletonsForStatelessLambdas =
- System.getProperty("com.android.tools.r8.createSingletonsForStatelessLambdas") != null;
-
// TODO(b/293591931): Remove this flag when records are stable in Platform
// Flag to allow record annotations in DEX. See b/231930852 for context.
private final boolean emitRecordAnnotationsInDex =
@@ -2491,7 +2486,7 @@
* <p>Note that if the compilation is not desugaring to a min-api or targeting DEX at a min-api,
* then the bug is assumed to be present as the CF output could be further compiled to any target.
*/
- private boolean canHaveBugPresentUntil(AndroidApiLevel level) {
+ private boolean canHaveBugPresentUntilExclusive(AndroidApiLevel level) {
if (desugarState.isOn() || isGeneratingDex()) {
return level == null || !hasMinApi(level);
}
@@ -2783,7 +2778,7 @@
//
// See b/69364976 and b/77996377.
public boolean canHaveBoundsCheckEliminationBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.M);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// MediaTek JIT compilers for KitKat phones did not implement the not
@@ -2799,7 +2794,7 @@
// assumed to not change. If the receiver register is reused for something else the verifier
// will fail and the code will not run.
public boolean canHaveThisTypeVerifierBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.M);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// Art crashes if we do dead reference elimination of the receiver in release mode and Art
@@ -2808,13 +2803,13 @@
//
// See b/116683601 and b/116837585.
public boolean canHaveThisJitCodeDebuggingBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.Q);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.Q);
}
// The dalvik jit had a bug where the long operations add, sub, or, xor and and would write
// the first part of the result long before reading the second part of the input longs.
public boolean canHaveOverlappingLongRegisterBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// Some dalvik versions found in the wild perform invalid JIT compilation of cmp-long
@@ -2847,7 +2842,7 @@
//
// See b/75408029.
public boolean canHaveCmpLongBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// Some Lollipop VMs crash if there is a const instruction between a cmp and an if instruction.
@@ -2875,7 +2870,7 @@
//
// See b/115552239.
public boolean canHaveCmpIfFloatBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.M);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// Some Lollipop VMs incorrectly optimize code with mul2addr instructions. In particular,
@@ -2897,7 +2892,7 @@
//
// This issue has only been observed on a Verizon Ellipsis 8 tablet. See b/76115465.
public boolean canHaveMul2AddrBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.M);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// Some Marshmallow VMs create an incorrect doubly-linked list of instructions. When the VM
@@ -2906,7 +2901,7 @@
//
// See b/77842465.
public boolean canHaveDex2OatLinkedListBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.N);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.N);
}
// dex2oat on Marshmallow VMs does aggressive inlining which can eat up all the memory on
@@ -2914,7 +2909,7 @@
//
// See b/111960171
public boolean canHaveDex2OatInliningIssue() {
- return canHaveBugPresentUntil(AndroidApiLevel.N);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.N);
}
// Art 7.0.0 and later Art JIT may perform an invalid optimization if a string new-instance does
@@ -2922,7 +2917,7 @@
//
// See b/78493232 and b/80118070.
public boolean canHaveArtStringNewInitBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.Q);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.Q);
}
// Dalvik tracing JIT may perform invalid optimizations when int/float values are converted to
@@ -2930,7 +2925,7 @@
//
// See b/77496850.
public boolean canHaveNumberConversionRegisterAllocationBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// Some Lollipop mediatek VMs have a peculiar bug where the inliner crashes if there is a
@@ -2943,7 +2938,7 @@
//
// See b/68378480.
public boolean canHaveForwardingInitInliningBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.M);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// Some Lollipop x86_64 VMs have a bug causing a segfault if an exception handler directly targets
@@ -2956,7 +2951,7 @@
// See b/111337896.
public boolean canHaveExceptionTargetingLoopHeaderBug() {
assert isGeneratingDex();
- return !debug && canHaveBugPresentUntil(AndroidApiLevel.M);
+ return !debug && canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// The Dalvik tracing JIT can trace past the end of the instruction stream and end up
@@ -2971,7 +2966,7 @@
// We also could not insert any dead code (e.g. a return) because that would make mediatek
// dominator calculations on 7.0.0 crash. See b/128926846.
public boolean canHaveTracingPastInstructionsStreamBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// The art verifier incorrectly propagates type information for the following pattern:
@@ -2998,7 +2993,7 @@
//
// Fixed in Android Q, see b/120985556.
public boolean canHaveArtInstanceOfVerifierBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.Q);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.Q);
}
// Some Art Lollipop version do not deal correctly with long-to-int conversions.
@@ -3021,7 +3016,7 @@
public boolean canHaveLongToIntBug() {
// We have only seen this happening on Lollipop arm64 backends. We have tested on
// Marshmallow and Nougat arm64 devices and they do not have the bug.
- return canHaveBugPresentUntil(AndroidApiLevel.M);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.M);
}
// The Art VM for Android N through P has a bug in the JIT that means that if the same
@@ -3034,7 +3029,7 @@
//
// See b/120164595.
public boolean canHaveExceptionTypeBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.Q);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.Q);
}
// Art 4.0.4 fails with a verification error when a null-literal is being passed directly to an
@@ -3042,7 +3037,7 @@
// elimination of check-cast instructions where the value being cast is the constant null.
// See b/123269162.
public boolean canHaveArtCheckCastVerifierBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.J);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.J);
}
// The verifier will merge A[] and B[] to Object[], even when both A and B implement an interface
@@ -3066,7 +3061,7 @@
//
// See b/131349148
public boolean canHaveDalvikCatchHandlerVerificationBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// Having an invoke instruction that targets an abstract method on a non-abstract class will fail
@@ -3074,7 +3069,7 @@
//
// See b/132953944.
public boolean canHaveDalvikAbstractMethodOnNonAbstractClassVerificationBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// On dalvik we see issues when using an int value in places where a boolean, byte, char, or short
@@ -3088,14 +3083,14 @@
//
// See also b/134304597 and b/124152497.
public boolean canHaveDalvikIntUsedAsNonIntPrimitiveTypeBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.L);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L);
}
// The standard library prior to API 19 did not contain a ZipFile that implemented Closable.
//
// See b/177532008.
public boolean canHaveZipFileWithMissingCloseableBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.K);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.K);
}
// Some versions of Dalvik had a bug where a switch with a MAX_INT key would still go to
@@ -3103,7 +3098,7 @@
//
// See b/177790310.
public boolean canHaveSwitchMaxIntBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.K);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.K);
}
// On Dalvik the methods Integer.parseInt and Long.parseLong does not support strings with a '+'
@@ -3119,7 +3114,15 @@
//
// See b/215573892.
public boolean canHaveSuperInvokeBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.N);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.N);
+ }
+
+ // Android 5.0 does not correctly handle invoke-super instructions that resolve to an abstract
+ // method.
+ //
+ // See b/213581039.
+ public boolean canHaveSuperInvokeToAbstractMethodBug() {
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.L_MR1);
}
// Some Dalvik and Art MVs does not support interface invokes to Object
@@ -3141,13 +3144,13 @@
//
// See b/326661821 (details in b/326661821#comment33).
public boolean canHaveInvokeInterfaceToObjectMethodBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.U);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.U);
}
// Until we fully drop support for API levels < 16, we have to emit an empty annotation set to
// work around a DALVIK bug. See b/36951668.
public boolean canHaveDalvikEmptyAnnotationSetBug() {
- return canHaveBugPresentUntil(AndroidApiLevel.J_MR1);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.J_MR1);
}
public boolean canHaveNonReboundConstructorInvoke() {
@@ -3160,13 +3163,13 @@
// b/238399429 Some art 6 vms have issues with multiple monitors in the same method
// Don't inline code with monitors into methods that already have monitors.
public boolean canHaveIssueWithInlinedMonitors() {
- return canHaveBugPresentUntil(AndroidApiLevel.N);
+ return canHaveBugPresentUntilExclusive(AndroidApiLevel.N);
}
// b/272725341. ART 11 and 12 re-introduced hard verification errors when unable to compute
// subtype relationship when no other verification issues exists in code.
public boolean canHaveVerifyErrorForUnknownUnusedReturnValue() {
- return isGeneratingDex() && canHaveBugPresentUntil(AndroidApiLevel.T);
+ return isGeneratingDex() && canHaveBugPresentUntilExclusive(AndroidApiLevel.T);
}
public boolean canInitNewInstanceUsingSuperclassConstructor() {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/IllegalSingleCallerInliningOfSiblingTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/IllegalSingleCallerInliningOfSiblingTest.java
new file mode 100644
index 0000000..cd42876
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/IllegalSingleCallerInliningOfSiblingTest.java
@@ -0,0 +1,82 @@
+// Copyright (c) 2024, 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.optimize.inliner;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NoHorizontalClassMerging;
+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.ir.optimize.Inliner.Reason;
+import com.google.common.collect.ImmutableSet;
+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 IllegalSingleCallerInliningOfSiblingTest 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)
+ .addOptionsModification(
+ options -> options.testing.validInliningReasons = ImmutableSet.of(Reason.SINGLE_CALLER))
+ .enableNoHorizontalClassMergingAnnotations()
+ .enableNoVerticalClassMergingAnnotations()
+ .enableNeverClassInliningAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("A", "A");
+ }
+
+ static class Main {
+
+ public static void main(String[] args) {
+ I i = System.currentTimeMillis() > 0 ? new B() : new C();
+ i.m();
+
+ new A().m();
+ }
+ }
+
+ interface I {
+
+ void m();
+ }
+
+ @NeverClassInline
+ @NoHorizontalClassMerging
+ @NoVerticalClassMerging
+ static class A {
+
+ public void m() {
+ System.out.println("A");
+ }
+ }
+
+ static class B extends A implements I {}
+
+ @NoHorizontalClassMerging
+ static class C implements I {
+
+ @Override
+ public void m() {
+ System.out.println("C");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleCallerInlineOutlineTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleCallerInlineOutlineTest.java
new file mode 100644
index 0000000..e87b010
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/SingleCallerInlineOutlineTest.java
@@ -0,0 +1,145 @@
+// Copyright (c) 2024, 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.optimize.inliner;
+
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethod;
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.allOf;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.synthesis.SyntheticItemsTestUtils;
+import com.android.tools.r8.utils.BooleanUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+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 SingleCallerInlineOutlineTest extends TestBase {
+
+ @Parameter(0)
+ public boolean allowInliningOfOutlines;
+
+ @Parameter(1)
+ public TestParameters parameters;
+
+ @Parameters(name = "{1}, allow inlining: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(
+ BooleanUtils.values(), getTestParameters().withAllRuntimesAndApiLevels().build());
+ }
+
+ @Test
+ public void test() throws Exception {
+ testForR8(parameters.getBackend())
+ .addInnerClasses(getClass())
+ .addKeepMainRule(Main.class)
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ options.getTestingOptions().allowInliningOfOutlines = allowInliningOfOutlines;
+ })
+ .enableInliningAnnotations()
+ .setMinApi(parameters)
+ .compile()
+ .inspect(
+ inspector -> {
+ ClassSubject printerClassSubject = inspector.clazz(Printer.class);
+ assertThat(printerClassSubject, isPresent());
+
+ MethodSubject helloMethodSubject =
+ printerClassSubject.uniqueMethodWithOriginalName("hello");
+ assertThat(helloMethodSubject, isPresent());
+
+ MethodSubject worldMethodSubject =
+ printerClassSubject.uniqueMethodWithOriginalName("world");
+ assertThat(worldMethodSubject, isPresent());
+
+ ClassSubject mainClassSubject = inspector.clazz(Main.class);
+ assertThat(mainClassSubject, isPresent());
+
+ MethodSubject outlineCandidateMethodSubject =
+ mainClassSubject.uniqueMethodWithOriginalName("outlineCandidate");
+ assertThat(outlineCandidateMethodSubject, isPresent());
+
+ ClassSubject outlineClassSubject =
+ inspector.clazz(SyntheticItemsTestUtils.syntheticOutlineClass(Main.class, 0));
+ // Note that the outline class is present even when `allowInliningOfOutlines`
+ // is true, since we do not run another round of tree shaking after the single
+ // caller inliner pass.
+ assertThat(outlineClassSubject, isPresent());
+
+ if (allowInliningOfOutlines) {
+ assertThat(
+ outlineCandidateMethodSubject,
+ allOf(invokesMethod(helloMethodSubject), invokesMethod(worldMethodSubject)));
+ } else {
+ assertThat(
+ outlineCandidateMethodSubject,
+ invokesMethod(outlineClassSubject.uniqueMethod()));
+ }
+ })
+ .run(parameters.getRuntime(), Main.class)
+ .assertSuccessWithOutputLines("Hello, world!");
+ }
+
+ static class Main {
+
+ static boolean alwaysFalse;
+
+ public static void main(String[] args) {
+ outlineCandidate();
+
+ // This will be pruned in the primary optimization pass. At this point R8 still does not know
+ // that the `alwaysFalse` field is false.
+ if (alwaysFalse()) {
+ alwaysFalse = true;
+ }
+
+ // This will be pruned in the second optimization pass after the outliner has run. After the
+ // second round of tree shaking, outlineCandidate2() will be removed, and the synthetic
+ // outline will have a single caller.
+ if (alwaysFalse) {
+ outlineCandidate2();
+ }
+ }
+
+ static boolean alwaysFalse() {
+ return false;
+ }
+
+ @NeverInline
+ static void outlineCandidate() {
+ Printer.hello();
+ Printer.world();
+ }
+
+ @NeverInline
+ static void outlineCandidate2() {
+ Printer.hello();
+ Printer.world();
+ }
+ }
+
+ static class Printer {
+
+ @NeverInline
+ static void hello() {
+ System.out.print("Hello");
+ }
+
+ @NeverInline
+ static void world() {
+ System.out.println(", world!");
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
index c7e6cce..5d02b1b 100644
--- a/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
+++ b/src/test/java/com/android/tools/r8/kotlin/KotlinIntrinsicsInlineTest.java
@@ -76,7 +76,11 @@
MethodSubject isSupported = main.uniqueMethodWithOriginalName("isSupported");
assertThat(isSupported, isPresent());
assertEquals(
- kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72) ? 1 : 0,
+ kotlinc.is(KotlinCompilerVersion.KOTLINC_1_3_72)
+ && (parameters.isCfRuntime()
+ || parameters.getApiLevel().equals(AndroidApiLevel.B))
+ ? 1
+ : 0,
countCall(isSupported, "checkParameterIsNotNull"));
// In general cases, null check won't be invoked only once or twice, hence no subtle
diff --git a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
index eb4d99c..bf0e5cf 100644
--- a/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
+++ b/src/test/java/com/android/tools/r8/memberrebinding/MemberRebindingInvokeSuperAbstractTest.java
@@ -6,6 +6,7 @@
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForClass;
import static com.android.tools.r8.apimodel.ApiModelingTestHelper.setMockApiLevelForMethod;
+import static com.android.tools.r8.utils.codeinspector.CodeMatchers.invokesMethodWithHolderAndName;
import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
import static org.hamcrest.MatcherAssert.assertThat;
@@ -14,10 +15,11 @@
import com.android.tools.r8.TestParameters;
import com.android.tools.r8.TestParametersCollection;
import com.android.tools.r8.apimodel.ApiModelingTestHelper;
+import com.android.tools.r8.references.Reference;
import com.android.tools.r8.utils.AndroidApiLevel;
-import com.android.tools.r8.utils.codeinspector.CodeMatchers;
import com.android.tools.r8.utils.codeinspector.MethodSubject;
import com.google.common.collect.ImmutableList;
+import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,6 +61,14 @@
.apply(
setMockApiLevelForMethod(
LibrarySub.class.getDeclaredMethod("getSystemService"), AndroidApiLevel.B))
+ .apply(
+ setMockApiLevelForMethod(
+ Reference.method(
+ Reference.classFromClass(LibrarySubSub.class),
+ "getSystemService",
+ Collections.emptyList(),
+ null),
+ AndroidApiLevel.B))
.compile()
.addRunClasspathClasses(libraryClasses)
.inspect(
@@ -66,13 +76,21 @@
MethodSubject getSystemService =
inspector.clazz(Main.class).uniqueMethodWithOriginalName("getSystemService");
assertThat(getSystemService, isPresent());
- // We should never rebind this call to LibraryBase::getSystemService since this can
- // cause errors when verifying the code on a device where the image has a definition
- // but it is abstract. For more information, see b/213581039.
+ // We should only rebind this call to LibraryBase::getSystemService when compiling to
+ // Android 5.1 or above since this can cause errors when verifying the code on a
+ // device where the image has a definition but it is abstract. For more information,
+ // see b/213581039.
+ //
+ // Due to b/215573892 we also select LibrarySubSub rather than LibraryBase when
+ // compiling to Android 5.1.
+ Class<?> expectedRebindingTarget =
+ parameters.isCfRuntime() || parameters.getApiLevel().isLessThan(AndroidApiLevel.N)
+ ? LibrarySubSub.class
+ : LibraryBase.class;
assertThat(
getSystemService,
- CodeMatchers.invokesMethodWithHolderAndName(
- typeName(LibrarySubSub.class), "getSystemService"));
+ invokesMethodWithHolderAndName(
+ typeName(expectedRebindingTarget), "getSystemService"));
})
.run(parameters.getRuntime(), Main.class)
.assertSuccessWithOutputLines("LibrarySub::getSystemService");