Fix type of outline arguments
Use the already calculated type lattice element of the values flowing into
the outline to determine the outline argument type. The previous calculation
based on subtypes only did not work in all situations.
Fixed equals of oultines as it was missing the outline argument types. The
fix with the correct argument types revealed this.
Bug: 133215941
Change-Id: I02d980f9dacb57adabe96c1ec8e840fcdcf5b871
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index 3d6fb19..f7e3a72 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -655,6 +655,7 @@
@Override
InternalOptions getInternalOptions() {
InternalOptions internal = new InternalOptions(proguardConfiguration, getReporter());
+ assert !internal.testing.allowOutlinerInterfaceArrayArguments; // Only allow in tests.
assert !internal.debug;
internal.debug = getMode() == CompilationMode.DEBUG;
internal.programConsumer = getProgramConsumer();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
index 5e16020..f72aef7 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/ArrayTypeLatticeElement.java
@@ -65,7 +65,7 @@
return memberTypeLattice.isFineGrainedType() ? INT : memberTypeLattice;
}
- private TypeLatticeElement getArrayBaseTypeLattice() {
+ public TypeLatticeElement getArrayBaseTypeLattice() {
TypeLatticeElement base = getArrayMemberTypeAsMemberType();
while (base.isArrayType()) {
base = base.asArrayTypeLatticeElement().getArrayMemberTypeAsMemberType();
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
index 50b1513..569c903 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/type/PrimitiveTypeLatticeElement.java
@@ -34,7 +34,7 @@
return fromTypeDescriptorChar((char) type.descriptor.content[0], asArrayElementType);
}
- DexType toDexType(DexItemFactory factory) {
+ public DexType toDexType(DexItemFactory factory) {
if (isBoolean()) {
return factory.booleanType;
}
@@ -62,6 +62,17 @@
throw new Unreachable("Imprecise primitive type '" + toString() + "'");
}
+ public boolean hasDexType() {
+ return isBoolean()
+ || isByte()
+ || isShort()
+ || isChar()
+ || isInt()
+ || isFloat()
+ || isLong()
+ || isDouble();
+ }
+
private static PrimitiveTypeLatticeElement fromTypeDescriptorChar(
char descriptor, boolean asArrayElementType) {
switch (descriptor) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
index 7d5618b..2a05f6c 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/Outliner.java
@@ -16,6 +16,7 @@
import com.android.tools.r8.graph.DexAnnotationSet;
import com.android.tools.r8.graph.DexEncodedField;
import com.android.tools.r8.graph.DexEncodedMethod;
+import com.android.tools.r8.graph.DexItemFactory;
import com.android.tools.r8.graph.DexMethod;
import com.android.tools.r8.graph.DexProgramClass;
import com.android.tools.r8.graph.DexProto;
@@ -26,6 +27,8 @@
import com.android.tools.r8.graph.MethodAccessFlags;
import com.android.tools.r8.graph.ParameterAnnotationsList;
import com.android.tools.r8.graph.UseRegistry;
+import com.android.tools.r8.ir.analysis.type.ArrayTypeLatticeElement;
+import com.android.tools.r8.ir.analysis.type.ClassTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.PrimitiveTypeLatticeElement;
import com.android.tools.r8.ir.analysis.type.TypeLatticeElement;
import com.android.tools.r8.ir.code.Add;
@@ -581,8 +584,9 @@
if (!(other instanceof Outline)) {
return false;
}
+ Outline otherOutline = (Outline) other;
List<OutlineInstruction> instructions0 = this.templateInstructions;
- List<OutlineInstruction> instructions1 = ((Outline) other).templateInstructions;
+ List<OutlineInstruction> instructions1 = otherOutline.templateInstructions;
if (instructions0.size() != instructions1.size()) {
return false;
}
@@ -593,8 +597,9 @@
return false;
}
}
- return argumentMap.equals(((Outline) other).argumentMap)
- && returnType == ((Outline) other).returnType;
+ return argumentTypes.equals(otherOutline.argumentTypes)
+ && argumentMap.equals(otherOutline.argumentMap)
+ && returnType == otherOutline.returnType;
}
@Override
@@ -861,6 +866,9 @@
if (value == returnValue) {
continue;
}
+ if (!supportedArgumentType(value)) {
+ return false;
+ }
if (invoke.isInvokeStatic()) {
newArgumentRegisters += value.requiredRegisters();
} else {
@@ -898,20 +906,57 @@
return true;
}
- private DexType argumentTypeFromInvoke(InvokeMethod invoke, int index) {
- assert invoke.isInvokeMethodWithReceiver() || invoke.isInvokePolymorphic();
- if (index == 0) {
- return invoke.getInvokedMethod().holder;
+ private boolean supportedArgumentType(Value value) {
+ // All non array types are supported.
+ if (!value.getTypeLattice().isArrayType()) {
+ return true;
}
- DexProto methodProto;
- if (invoke.isInvokePolymorphic()) {
- // Type of argument of a polymorphic call must be taken from the call site.
- methodProto = invoke.asInvokePolymorphic().getProto();
+ // Avoid array type elements which have interfaces, as Art does not have the same semantics
+ // for array types as the Java VM, see b/132420510.
+ if (appView.options().testing.allowOutlinerInterfaceArrayArguments
+ && appView.options().isGeneratingClassFiles()) {
+ return true;
+ }
+ ArrayTypeLatticeElement arrayTypeLatticeElement =
+ value.getTypeLattice().asArrayTypeLatticeElement();
+ TypeLatticeElement arrayBaseType = arrayTypeLatticeElement.getArrayBaseTypeLattice();
+ if (arrayBaseType.isPrimitive()) {
+ return true;
+ }
+ if (arrayBaseType.isClassType()) {
+ return arrayBaseType.asClassTypeLatticeElement().getInterfaces().size() == 0;
+ }
+ return false;
+ }
+
+ private DexType argumentTypeFromValue(Value value) {
+ assert supportedArgumentType(value);
+ DexItemFactory itemFactory = appView.options().itemFactory;
+ DexType objectType = itemFactory.objectType;
+ TypeLatticeElement valueLatticeElement = value.getTypeLattice();
+ if (valueLatticeElement.isClassType()) {
+ ClassTypeLatticeElement valueClassTypeLatticeElement =
+ value.getTypeLattice().asClassTypeLatticeElement();
+ // For values of lattice type java.lang.Object and only one interface use the interface as
+ // the type of the outline argument. If there are several interfaces these interfaces don't
+ // have a common super interface nor are they implemented by a common superclass so the
+ // argument type of the ouline will be java.lang.Object.
+ if (valueClassTypeLatticeElement.getClassType() == objectType
+ && valueClassTypeLatticeElement.getInterfaces().size() == 1) {
+ return valueClassTypeLatticeElement.getInterfaces().iterator().next();
+ } else {
+ return valueClassTypeLatticeElement.getClassType();
+ }
+ } else if (valueLatticeElement.isArrayType()) {
+ return value.getTypeLattice().asArrayTypeLatticeElement().getArrayType(itemFactory);
+ } else if (valueLatticeElement.isNullType()) {
+ // For values which are always null use java.lang.Object as the outline argument type.
+ return objectType;
} else {
- methodProto = invoke.getInvokedMethod().proto;
+ assert valueLatticeElement.isPrimitive();
+ assert valueLatticeElement.asPrimitiveTypeLatticeElement().hasDexType();
+ return valueLatticeElement.asPrimitiveTypeLatticeElement().toDexType(itemFactory);
}
- // -1 due to receiver.
- return methodProto.parameters.values[index - 1];
}
// Add the current instruction to the outline.
@@ -953,25 +998,17 @@
int argumentIndex = arguments.indexOf(value);
// For virtual calls only re-use the receiver argument.
if (i == 0 && argumentIndex != -1) {
- DexType receiverType = argumentTypeFromInvoke(invoke, i);
- // Ensure that the outline argument type is specific enough.
- if (receiverType.isClassType()) {
- if (appView.appInfo().isSubtype(receiverType, argumentTypes.get(argumentIndex))) {
- argumentTypes.set(argumentIndex, receiverType);
- }
- }
argumentsMap.add(argumentIndex);
} else {
arguments.add(value);
argumentRegisters += value.requiredRegisters();
- argumentTypes.add(argumentTypeFromInvoke(invoke, i));
+ argumentTypes.add(argumentTypeFromValue(value));
argumentsMap.add(argumentTypes.size() - 1);
}
} else {
arguments.add(value);
if (instruction.isInvokeMethod()) {
- argumentTypes
- .add(instruction.asInvokeMethod().getInvokedMethod().proto.parameters.values[i]);
+ argumentTypes.add(argumentTypeFromValue(value));
} else {
argumentTypes.add(
instruction.asBinop().getNumericType().dexTypeFor(appView.dexItemFactory()));
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 f320337..0964666 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -818,6 +818,9 @@
return enableStatefulLambdaCreateInstanceMethod;
}
+ // Option for testing outlining with interface array arguments, see b/132420510.
+ public boolean allowOutlinerInterfaceArrayArguments = false;
+
public MinifierTestingOptions minifier = new MinifierTestingOptions();
public static class MinifierTestingOptions {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
new file mode 100644
index 0000000..604e3fe
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithClassArrayTypeArguments.java
@@ -0,0 +1,107 @@
+// Copyright (c) 2019, 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.outliner.arraytypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlinesWithClassArrayTypeArguments extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public OutlinesWithClassArrayTypeArguments(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void validateOutlining(CodeInspector inspector) {
+ ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ MethodSubject outline0Method =
+ outlineClass.method(
+ "void",
+ "outline0",
+ ImmutableList.of(
+ TestClass.class.getTypeName() + "[]", TestClass.class.getTypeName() + "[]"));
+ assertThat(outline0Method, isPresent());
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(
+ classSubject.uniqueMethodWithName("method1"), CodeMatchers.invokesMethod(outline0Method));
+ assertThat(
+ classSubject.uniqueMethodWithName("method2"), CodeMatchers.invokesMethod(outline0Method));
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("1", "1", "1", "1");
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .addInnerClasses(OutlinesWithClassArrayTypeArguments.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getRuntime())
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .compile()
+ .inspect(this::validateOutlining)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ public static class TestClass {
+ @NeverInline
+ public static void useArray(TestClass[] array) {
+ System.out.println(array[0].method());
+ }
+
+ @NeverInline
+ public int method() {
+ return 1;
+ }
+
+ @NeverInline
+ public static void method1(TestClass[] array) {
+ // These two invokes are expected to be outlined.
+ useArray(array);
+ useArray(array);
+ }
+
+ @NeverInline
+ public static void method2(TestClass[] array) {
+ // These two invokes are expected to be outlined.
+ useArray(array);
+ useArray(array);
+ }
+
+ public static void main(String[] args) {
+ TestClass[] array1 = new TestClass[1];
+ array1[0] = new TestClass();
+ method1(array1);
+ method2(array1);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
new file mode 100644
index 0000000..33e5f96
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithInterfaceArrayTypeArguments.java
@@ -0,0 +1,133 @@
+// Copyright (c) 2019, 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.outliner.arraytypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.CoreMatchers.not;
+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.utils.BooleanUtils;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+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.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlinesWithInterfaceArrayTypeArguments extends TestBase {
+
+ private final boolean allowOutlinerInterfaceArrayArguments;
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{1}, allow interface array types in outlining on JVM: {0}")
+ public static List<Object[]> data() {
+ return buildParameters(BooleanUtils.values(), getTestParameters().withAllRuntimes().build());
+ }
+
+ public OutlinesWithInterfaceArrayTypeArguments(
+ boolean allowOutlinerInterfaceArrayArguments, TestParameters parameters) {
+ this.allowOutlinerInterfaceArrayArguments = allowOutlinerInterfaceArrayArguments;
+ this.parameters = parameters;
+ }
+
+ private void validateOutlining(CodeInspector inspector) {
+ // No outlining when arrays of interfaces are involved, see b/132420510 - unless the testing
+ // option is set.
+ ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ if (allowOutlinerInterfaceArrayArguments && parameters.isCfRuntime()) {
+ assertThat(outlineClass, isPresent());
+ MethodSubject outline0Method =
+ outlineClass.method(
+ "void", "outline0", ImmutableList.of("java.lang.Object[]", "java.lang.Object[]"));
+ assertThat(outline0Method, isPresent());
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(
+ classSubject.uniqueMethodWithName("method1"), CodeMatchers.invokesMethod(outline0Method));
+ assertThat(
+ classSubject.uniqueMethodWithName("method2"), CodeMatchers.invokesMethod(outline0Method));
+ } else {
+ assertThat(outlineClass, not(isPresent()));
+ }
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("1", "1", "2", "2");
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .addInnerClasses(OutlinesWithInterfaceArrayTypeArguments.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(ClassImplementingIface.class)
+ .addKeepClassAndMembersRules(OtherClassImplementingIface.class)
+ .setMinApi(parameters.getRuntime())
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ options.testing.allowOutlinerInterfaceArrayArguments =
+ allowOutlinerInterfaceArrayArguments;
+ })
+ .compile()
+ .inspect(this::validateOutlining)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ interface Iface {
+ int interfaceMethod();
+ }
+
+ public static class ClassImplementingIface implements Iface {
+ public int interfaceMethod() {
+ return 1;
+ }
+ }
+
+ public static class OtherClassImplementingIface implements Iface {
+ public int interfaceMethod() {
+ return 2;
+ }
+ }
+
+ public static class TestClass {
+ @NeverInline
+ public static void useArray(Iface[] ifaceArray) {
+ System.out.println(ifaceArray[0].interfaceMethod());
+ }
+
+ @NeverInline
+ public static void method1(Iface[] ifaceArray) {
+ // These two invokes are expected to be outlined, when the testing option is set.
+ useArray(ifaceArray);
+ useArray(ifaceArray);
+ }
+
+ @NeverInline
+ public static void method2(Iface[] ifaceArray) {
+ // These two invokes are expected to be outlined, when the testing option is set.
+ useArray(ifaceArray);
+ useArray(ifaceArray);
+ }
+
+ public static void main(String[] args) {
+ Iface[] ifaceArray1 = new ClassImplementingIface[1];
+ ifaceArray1[0] = new ClassImplementingIface();
+ method1(ifaceArray1);
+ Iface[] ifaceArray2 = new OtherClassImplementingIface[1];
+ ifaceArray2[0] = new OtherClassImplementingIface();
+ method2(ifaceArray2);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
new file mode 100644
index 0000000..8a78d77
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/arraytypes/OutlinesWithPrimitiveArrayTypeArguments.java
@@ -0,0 +1,96 @@
+// Copyright (c) 2019, 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.outliner.arraytypes;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+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.TestParametersCollection;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class OutlinesWithPrimitiveArrayTypeArguments extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public OutlinesWithPrimitiveArrayTypeArguments(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void validateOutlining(CodeInspector inspector) {
+ ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ MethodSubject outline0Method =
+ outlineClass.method("void", "outline0", ImmutableList.of("int[]", "int[]"));
+ assertThat(outline0Method, isPresent());
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(
+ classSubject.uniqueMethodWithName("method1"), CodeMatchers.invokesMethod(outline0Method));
+ assertThat(
+ classSubject.uniqueMethodWithName("method2"), CodeMatchers.invokesMethod(outline0Method));
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("1", "1", "2", "2");
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .addInnerClasses(OutlinesWithPrimitiveArrayTypeArguments.class)
+ .addKeepMainRule(TestClass.class)
+ .setMinApi(parameters.getRuntime())
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .compile()
+ .inspect(this::validateOutlining)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ public static class TestClass {
+ @NeverInline
+ public static void printArrayLength(int[] intArray) {
+ System.out.println(intArray.length);
+ }
+
+ @NeverInline
+ public static void method1(int[] intArray) {
+ // These two invokes are expected to be outlined.
+ printArrayLength(intArray);
+ printArrayLength(intArray);
+ }
+
+ @NeverInline
+ public static void method2(int[] intArray) {
+ // These two invokes are expected to be outlined.
+ printArrayLength(intArray);
+ printArrayLength(intArray);
+ }
+
+ public static void main(String[] args) {
+ method1(new int[1]);
+ method2(new int[2]);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
new file mode 100644
index 0000000..fc887f5
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/b133215941/B133215941.java
@@ -0,0 +1,136 @@
+// Copyright (c) 2019, 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.outliner.b133215941;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+
+import com.android.tools.r8.NeverClassInline;
+import com.android.tools.r8.NeverInline;
+import com.android.tools.r8.NeverMerge;
+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.outliner.b133215941.B133215941.TestClass.ClassWithStaticMethod;
+import com.android.tools.r8.utils.InternalOptions.OutlineOptions;
+import com.android.tools.r8.utils.StringUtils;
+import com.android.tools.r8.utils.codeinspector.ClassSubject;
+import com.android.tools.r8.utils.codeinspector.CodeInspector;
+import com.android.tools.r8.utils.codeinspector.CodeMatchers;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import com.google.common.collect.ImmutableList;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class B133215941 extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public B133215941(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void validateOutlining(CodeInspector inspector) {
+ ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ MethodSubject outline0Method =
+ outlineClass.method(
+ "void",
+ "outline0",
+ ImmutableList.of(TestClass.class.getTypeName(), TestClass.class.getTypeName()));
+ assertThat(outline0Method, isPresent());
+ ClassSubject classSubject = inspector.clazz(TestClass.class);
+ assertThat(
+ classSubject.uniqueMethodWithName("ifaceMethod"),
+ CodeMatchers.invokesMethod(outline0Method));
+ assertThat(
+ classSubject.uniqueMethodWithName("methodWithOutlineContent"),
+ CodeMatchers.invokesMethod(outline0Method));
+ }
+
+ @Test
+ public void test() throws Exception {
+ String expectedOutput = StringUtils.lines("Hello, world 5");
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .enableClassInliningAnnotations()
+ .enableMergeAnnotations()
+ .addInnerClasses(B133215941.class)
+ .addKeepMainRule(TestClass.class)
+ .addKeepClassAndMembersRules(ClassWithStaticMethod.class)
+ .setMinApi(parameters.getRuntime())
+ .noMinification()
+ .addOptionsModification(options -> options.outline.threshold = 2)
+ .compile()
+ .inspect(this::validateOutlining)
+ .run(parameters.getRuntime(), TestClass.class)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ @NeverMerge
+ public interface Iface {
+ void ifaceMethod();
+ }
+
+ @NeverMerge
+ public static class TestClassSuper {
+ @NeverInline
+ public void superMethod() {}
+ }
+
+ @NeverClassInline
+ public static class TestClass extends TestClassSuper implements Iface {
+
+ @NeverInline
+ public void ifaceMethod() {
+ // These three invokes are expected to be outlined.
+ ClassWithStaticMethod.staticMethodWithInterfaceArgumentType(this);
+ superMethod();
+ ClassWithStaticMethod.staticMethodWithInterfaceArgumentType(this);
+ }
+
+ @NeverInline
+ public void methodWithOutlineContent() {
+ // These three invokes are expected to be outlined.
+ ClassWithStaticMethod.staticMethodWithInterfaceArgumentType(this);
+ superMethod();
+ ClassWithStaticMethod.staticMethodWithInterfaceArgumentType(this);
+ }
+
+ @NeverClassInline
+ static class AnotherClassImplementingIface implements Iface {
+ @NeverInline
+ public void ifaceMethod() {
+ // Do nothing.
+ }
+ }
+
+ public static class ClassWithStaticMethod {
+ private static Iface iface;
+ public static int count;
+
+ @NeverInline
+ public static void staticMethodWithInterfaceArgumentType(Iface iface) {
+ ClassWithStaticMethod.iface = iface;
+ count++;
+ }
+ }
+
+ public static void main(String[] args) {
+ TestClass tc = new TestClass();
+ tc.methodWithOutlineContent();
+ tc.ifaceMethod();
+ ClassWithStaticMethod.staticMethodWithInterfaceArgumentType(
+ new AnotherClassImplementingIface());
+ System.out.println("Hello, world " + ClassWithStaticMethod.count);
+ }
+ }
+}
diff --git a/src/test/java/com/android/tools/r8/smali/OutlineTest.java b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
index 47e0cd0..c47b7a2 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -1785,7 +1785,7 @@
assertThat(
new CodeInspector(processedApplication)
.clazz(OutlineOptions.CLASS_NAME)
- .method("boolean", "outline0", ImmutableList.of("java.util.Collection")),
+ .method("boolean", "outline0", ImmutableList.of("java.util.List")),
isPresent());
// Run code and check result.
diff --git a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
index b230cea..717c98d 100644
--- a/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
+++ b/src/test/java/com/android/tools/r8/utils/codeinspector/TypeSubject.java
@@ -40,6 +40,10 @@
return dexType == type.dexType;
}
+ public boolean is(ClassSubject type) {
+ return dexType == type.getDexClass().type;
+ }
+
public String toString() {
return dexType.toSourceString();
}