Version 1.5.43

Cherry pick: Reapply "Fix type of outline arguments""
CL: https://r8-review.googlesource.com/c/r8/+/38949

Bug: 133215941
Change-Id: Ia67d80f6595ced11989603ed15077229f6b0f60f
diff --git a/src/main/java/com/android/tools/r8/R8Command.java b/src/main/java/com/android/tools/r8/R8Command.java
index a508b43..74a7fea 100644
--- a/src/main/java/com/android/tools/r8/R8Command.java
+++ b/src/main/java/com/android/tools/r8/R8Command.java
@@ -648,6 +648,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/Version.java b/src/main/java/com/android/tools/r8/Version.java
index f301864..668e101 100644
--- a/src/main/java/com/android/tools/r8/Version.java
+++ b/src/main/java/com/android/tools/r8/Version.java
@@ -11,7 +11,7 @@
 
   // This field is accessed from release scripts using simple pattern matching.
   // Therefore, changing this field could break our release scripts.
-  public static final String LABEL = "1.5.42";
+  public static final String LABEL = "1.5.43";
 
   private Version() {
   }
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 c631e11..90c112c 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 1765a64..5901ee9 100644
--- a/src/main/java/com/android/tools/r8/utils/InternalOptions.java
+++ b/src/main/java/com/android/tools/r8/utils/InternalOptions.java
@@ -680,6 +680,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 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();
   }