Reapply "Fix type of outline arguments""
This reverts commit ade6f79b7c9064aac80cc58ab4cc6ebea103145c.
On top of the revert the handling of primitive types as outline arguments
has been fixed.
Bug: 133215941
Change-Id: I655c047d8bce44d3d0669df341fb816459d4d896
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..81236f8 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 {
@@ -899,8 +907,8 @@
}
private DexType argumentTypeFromInvoke(InvokeMethod invoke, int index) {
- assert invoke.isInvokeMethodWithReceiver() || invoke.isInvokePolymorphic();
- if (index == 0) {
+ boolean withReceiver = invoke.isInvokeMethodWithReceiver() || invoke.isInvokePolymorphic();
+ if (withReceiver && index == 0) {
return invoke.getInvokedMethod().holder;
}
DexProto methodProto;
@@ -910,8 +918,70 @@
} else {
methodProto = invoke.getInvokedMethod().proto;
}
- // -1 due to receiver.
- return methodProto.parameters.values[index - 1];
+ // Skip receiver if present.
+ return methodProto.parameters.values[index - (withReceiver ? 1 : 0)];
+ }
+
+ private boolean supportedArgumentType(Value value) {
+ // All non array types are supported.
+ if (!value.getTypeLattice().isArrayType()) {
+ return true;
+ }
+ // 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, InvokeMethod invoke, int argumentIndex) {
+ 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 {
+ assert valueLatticeElement.isPrimitive();
+ assert valueLatticeElement.asPrimitiveTypeLatticeElement().hasDexType();
+ DexType type = valueLatticeElement.asPrimitiveTypeLatticeElement().toDexType(itemFactory);
+ if (valueLatticeElement.isInt()) {
+ // In the type lattice boolean, byte, short and char are all int. However, as the
+ // outline argument type use the actual primitive type at the call site.
+ assert type == itemFactory.intType;
+ type = argumentTypeFromInvoke(invoke, argumentIndex);
+ } else {
+ assert type == argumentTypeFromInvoke(invoke, argumentIndex);
+ }
+ return type;
+ }
}
// Add the current instruction to the outline.
@@ -949,29 +1019,20 @@
continue;
}
if (instruction.isInvokeMethodWithReceiver() || instruction.isInvokePolymorphic()) {
- InvokeMethod invoke = instruction.asInvokeMethod();
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, instruction.asInvokeMethod(), i));
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, instruction.asInvokeMethod(), i));
} 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 7be0aaa..b456b95 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -819,6 +819,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/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java b/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
new file mode 100644
index 0000000..aba1f31
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/outliner/primitivetypes/PrimitiveTypesTest.java
@@ -0,0 +1,213 @@
+// 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.primitivetypes;
+
+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.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 PrimitiveTypesTest extends TestBase {
+
+ private final TestParameters parameters;
+
+ @Parameterized.Parameters(name = "{0}")
+ public static TestParametersCollection data() {
+ return getTestParameters().withAllRuntimes().build();
+ }
+
+ public PrimitiveTypesTest(TestParameters parameters) {
+ this.parameters = parameters;
+ }
+
+ private void validateOutlining(CodeInspector inspector, Class<?> testClass, String argumentType) {
+ ClassSubject outlineClass = inspector.clazz(OutlineOptions.CLASS_NAME);
+ MethodSubject outline0Method =
+ outlineClass.method(
+ "java.lang.String", "outline0", ImmutableList.of(argumentType, argumentType));
+ assertThat(outline0Method, isPresent());
+ ClassSubject classSubject = inspector.clazz(testClass);
+ assertThat(
+ classSubject.uniqueMethodWithName("method1"), CodeMatchers.invokesMethod(outline0Method));
+ assertThat(
+ classSubject.uniqueMethodWithName("method2"), CodeMatchers.invokesMethod(outline0Method));
+ }
+
+ public void runTest(Class<?> testClass, String argumentType, String expectedOutput)
+ throws Exception {
+ testForR8(parameters.getBackend())
+ .enableInliningAnnotations()
+ .enableClassInliningAnnotations()
+ .addProgramClasses(testClass)
+ .addProgramClasses(MyStringBuilder.class)
+ .addKeepMainRule(testClass)
+ .setMinApi(parameters.getRuntime())
+ .noMinification()
+ .addOptionsModification(
+ options -> {
+ options.outline.threshold = 2;
+ options.outline.minSize = 2;
+ })
+ .compile()
+ .inspect(inspector -> validateOutlining(inspector, testClass, argumentType))
+ .run(parameters.getRuntime(), testClass)
+ .assertSuccessWithOutput(expectedOutput);
+ }
+
+ @Test
+ public void testBoolean() throws Exception {
+ runTest(TestClassBoolean.class, "boolean", StringUtils.lines("truetrue", "falsefalse"));
+ }
+
+ @Test
+ public void testShort() throws Exception {
+ runTest(TestClassShort.class, "short", StringUtils.lines("11", "22"));
+ }
+
+ @Test
+ public void testByte() throws Exception {
+ runTest(TestClassByte.class, "byte", StringUtils.lines("33", "44"));
+ }
+
+ @Test
+ public void testChar() throws Exception {
+ runTest(TestClassChar.class, "char", StringUtils.lines("AA", "BB"));
+ }
+
+ // StringBuilder wrapper for testing, as StringBuilder does not have append methods with
+ // byte or short but only int.
+ @NeverClassInline
+ public static class MyStringBuilder {
+ private final StringBuilder sb = new StringBuilder();
+
+ @NeverInline
+ public MyStringBuilder append(byte b) {
+ sb.append(b);
+ return this;
+ }
+
+ @NeverInline
+ public MyStringBuilder append(short s) {
+ sb.append(s);
+ return this;
+ }
+
+ @NeverInline
+ public String toString() {
+ return sb.toString();
+ }
+ }
+
+ static class TestClassBoolean {
+
+ @NeverInline
+ public static String method1(boolean b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(b);
+ sb.append(b);
+ return sb.toString();
+ }
+
+ @NeverInline
+ public static String method2(boolean b) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(b);
+ sb.append(b);
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(method1(true));
+ System.out.println(method2(false));
+ }
+ }
+
+ static class TestClassByte {
+
+ @NeverInline
+ public static String method1(byte b) {
+ MyStringBuilder sb = new MyStringBuilder();
+ sb.append(b);
+ sb.append(b);
+ return sb.toString();
+ }
+
+ @NeverInline
+ public static String method2(byte b) {
+ MyStringBuilder sb = new MyStringBuilder();
+ sb.append(b);
+ sb.append(b);
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(method1((byte) 3));
+ System.out.println(method2((byte) 4));
+ }
+ }
+
+ static class TestClassShort {
+
+ @NeverInline
+ public static String method1(short s) {
+ MyStringBuilder sb = new MyStringBuilder();
+ sb.append(s);
+ sb.append(s);
+ return sb.toString();
+ }
+
+ @NeverInline
+ public static String method2(short s) {
+ MyStringBuilder sb = new MyStringBuilder();
+ sb.append(s);
+ sb.append(s);
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(method1((short) 1));
+ System.out.println(method2((short) 2));
+ }
+ }
+
+ static class TestClassChar {
+
+ @NeverInline
+ public static String method1(char c) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(c);
+ sb.append(c);
+ return sb.toString();
+ }
+
+ @NeverInline
+ public static String method2(char c) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(c);
+ sb.append(c);
+ return sb.toString();
+ }
+
+ public static void main(String[] args) {
+ System.out.println(method1('A'));
+ System.out.println(method2('B'));
+ }
+ }
+}
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 8987418..d88bd4c 100644
--- a/src/test/java/com/android/tools/r8/smali/OutlineTest.java
+++ b/src/test/java/com/android/tools/r8/smali/OutlineTest.java
@@ -1786,7 +1786,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();
}