Only run class inliner analysis for parameters that may be class inlined

Change-Id: Ib4250d633944bb105400c89f809078fa3ce945bf
diff --git a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
index f4f1c31..da0f128 100644
--- a/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
+++ b/src/main/java/com/android/tools/r8/graph/ClassDefinition.java
@@ -21,6 +21,8 @@
 
   DexType getType();
 
+  boolean isInterface();
+
   @Override
   default boolean isClass() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/graph/DexClass.java b/src/main/java/com/android/tools/r8/graph/DexClass.java
index f0745c8..130dbcf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexClass.java
+++ b/src/main/java/com/android/tools/r8/graph/DexClass.java
@@ -598,6 +598,7 @@
     return isFinal();
   }
 
+  @Override
   public boolean isInterface() {
     return accessFlags.isInterface();
   }
diff --git a/src/main/java/com/android/tools/r8/ir/code/IRCode.java b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
index cbc8c6b..506e8af 100644
--- a/src/main/java/com/android/tools/r8/ir/code/IRCode.java
+++ b/src/main/java/com/android/tools/r8/ir/code/IRCode.java
@@ -1096,6 +1096,14 @@
     return arguments;
   }
 
+  public Argument getLastArgument() {
+    InstructionIterator instructionIterator = entryBlock().iterator(getNumberOfArguments() - 1);
+    Argument lastArgument = instructionIterator.next().asArgument();
+    assert lastArgument != null;
+    assert !instructionIterator.peekNext().isArgument();
+    return lastArgument;
+  }
+
   public Value getThis() {
     if (method().accessFlags.isStatic()) {
       return null;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
index a0ce6b0..b8f406a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/ClassInlinerMethodConstraintAnalysis.java
@@ -25,7 +25,7 @@
     // Analyze code.
     IntraproceduralDataflowAnalysis<ParameterUsages> analysis =
         new IntraproceduralDataflowAnalysis<>(
-            ParameterUsages.bottom(), new TransferFunction(appView, method));
+            ParameterUsages.bottom(), new TransferFunction(appView, method, code));
     SuccessfulDataflowAnalysisResult<ParameterUsages> result =
         analysis.run(code.entryBlock()).asSuccessfulAnalysisResult();
     if (result == null) {
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
index 8878976..f7b2cae 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/NonEmptyParameterUsages.java
@@ -9,6 +9,7 @@
 import com.android.tools.r8.utils.ArrayUtils;
 import com.android.tools.r8.utils.Int2ObjectMapUtils;
 import com.android.tools.r8.utils.IntObjConsumer;
+import com.android.tools.r8.utils.IntObjPredicate;
 import com.android.tools.r8.utils.IntObjToObjFunction;
 import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
 import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@@ -77,6 +78,15 @@
     return backing.isEmpty() ? bottom() : new NonEmptyParameterUsages(backing);
   }
 
+  public boolean allMatch(IntObjPredicate<ParameterUsagePerContext> predicate) {
+    for (Int2ObjectMap.Entry<ParameterUsagePerContext> entry : backing.int2ObjectEntrySet()) {
+      if (!predicate.test(entry.getIntKey(), entry.getValue())) {
+        return false;
+      }
+    }
+    return true;
+  }
+
   @Override
   public NonEmptyParameterUsages asNonEmpty() {
     return this;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
index 740a8cd..8cbade6 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/classinliner/analysis/TransferFunction.java
@@ -17,17 +17,25 @@
 import static com.android.tools.r8.ir.code.Opcodes.RETURN;
 
 import com.android.tools.r8.graph.AppView;
+import com.android.tools.r8.graph.ClasspathOrLibraryClass;
+import com.android.tools.r8.graph.DexClass;
 import com.android.tools.r8.graph.DexItemFactory;
+import com.android.tools.r8.graph.DexProgramClass;
+import com.android.tools.r8.graph.DexType;
 import com.android.tools.r8.graph.FieldResolutionResult;
 import com.android.tools.r8.graph.ProgramMethod;
 import com.android.tools.r8.graph.ResolutionResult.SingleResolutionResult;
 import com.android.tools.r8.ir.analysis.framework.intraprocedural.AbstractTransferFunction;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.FailedTransferFunctionResult;
+import com.android.tools.r8.ir.analysis.framework.intraprocedural.TransferFunctionResult;
+import com.android.tools.r8.ir.analysis.type.TypeElement;
 import com.android.tools.r8.ir.code.AliasedValueConfiguration;
 import com.android.tools.r8.ir.code.Argument;
 import com.android.tools.r8.ir.code.Assume;
 import com.android.tools.r8.ir.code.AssumeAndCheckCastAliasedValueConfiguration;
 import com.android.tools.r8.ir.code.BasicBlock;
 import com.android.tools.r8.ir.code.CheckCast;
+import com.android.tools.r8.ir.code.IRCode;
 import com.android.tools.r8.ir.code.If;
 import com.android.tools.r8.ir.code.InstanceGet;
 import com.android.tools.r8.ir.code.InstancePut;
@@ -53,6 +61,9 @@
   private final DexItemFactory dexItemFactory;
   private final ProgramMethod method;
 
+  // The last argument instruction.
+  private final Argument lastArgument;
+
   // Caches the parent or forwarding constructor call (only used in constructors).
   private InvokeDirect constructorInvoke;
 
@@ -65,16 +76,26 @@
   // skipped.
   private Set<Instruction> instructionsOfInterest = Sets.newIdentityHashSet();
 
-  TransferFunction(AppView<AppInfoWithLiveness> appView, ProgramMethod method) {
+  TransferFunction(AppView<AppInfoWithLiveness> appView, ProgramMethod method, IRCode code) {
     this.appView = appView;
     this.dexItemFactory = appView.dexItemFactory();
     this.method = method;
+    this.lastArgument = code.getLastArgument();
   }
 
   @Override
-  public ParameterUsages apply(Instruction instruction, ParameterUsages state) {
+  public TransferFunctionResult<ParameterUsages> apply(
+      Instruction instruction, ParameterUsages state) {
     if (instruction.isArgument()) {
-      return analyzeArgument(instruction.asArgument(), state);
+      Argument argument = instruction.asArgument();
+      ParameterUsages result = analyzeArgument(argument, state);
+      // After analyzing the last argument instruction, only proceed if there is at least one
+      // argument that may be eligible for class inlining.
+      if (argument == lastArgument
+          && result.asNonEmpty().allMatch((context, usagePerContext) -> usagePerContext.isTop())) {
+        return new FailedTransferFunctionResult<>();
+      }
+      return result;
     }
     if (!instructionsOfInterest.contains(instruction)) {
       // The instruction does not use any of the argument values that we are analyzing, so for the
@@ -131,7 +152,7 @@
     // we can't ignore parameters with a library type, since instances of program classes could
     // still flow into such parameters.
     Value value = argument.outValue();
-    if (!value.getType().isClassType() || value.hasPhiUsers()) {
+    if (!isMaybeEligibleForClassInlining(value.getType()) || value.hasPhiUsers()) {
       return state.put(argument.getIndex(), ParameterUsagePerContext.top());
     }
 
@@ -326,4 +347,40 @@
     assert value.getAliasedValue(aliasedValueConfiguration) == value;
     return value.isArgument() && argumentsOfInterest.contains(value);
   }
+
+  private boolean isMaybeEligibleForClassInlining(TypeElement type) {
+    if (!type.isClassType()) {
+      // Primitives and arrays will never be class inlined.
+      return false;
+    }
+    DexClass clazz = appView.definitionFor(type.asClassType().getClassType());
+    if (clazz == null) {
+      // We cannot class inline in presence of missing classes.
+      return false;
+    }
+    return clazz.isProgramClass()
+        ? isMaybeEligibleForClassInlining(clazz.asProgramClass())
+        : isMaybeEligibleForClassInlining(clazz.asClasspathOrLibraryClass());
+  }
+
+  private boolean isMaybeEligibleForClassInlining(DexProgramClass clazz) {
+    // We can only class inline parameters that does not inherit from other classpath or library
+    // classes than java.lang.Object.
+    DexType superType = clazz.getSuperType();
+    do {
+      DexClass superClass = appView.definitionFor(superType);
+      if (superClass == null) {
+        return false;
+      }
+      if (!superClass.isProgramClass()) {
+        return superClass.getType() == dexItemFactory.objectType;
+      }
+      superType = superClass.getSuperType();
+    } while (true);
+  }
+
+  private boolean isMaybeEligibleForClassInlining(ClasspathOrLibraryClass clazz) {
+    // We can only class inline a parameter that is either java.lang.Object or an interface type.
+    return clazz.getType() == dexItemFactory.objectType || clazz.isInterface();
+  }
 }
diff --git a/src/main/java/com/android/tools/r8/utils/IntObjPredicate.java b/src/main/java/com/android/tools/r8/utils/IntObjPredicate.java
new file mode 100644
index 0000000..a73a7be
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/utils/IntObjPredicate.java
@@ -0,0 +1,10 @@
+// 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.utils;
+
+public interface IntObjPredicate<T> {
+
+  boolean test(int i, T obj);
+}