Introduce an (unused) abandoned call site optimization instance

Abandoned will be used to signal that the call site optimization has bailed out for a method and all of it overrides, such that there is no need to propagate any arguments to the method or its overrides.

If the resolution target of an invoke is abandoned, then we can avoid computing the set of possible dispatch targets in the first place, which is generally expensive.

This also removes two redundant singleton instances of BOTTOM and TOP in the call site optimization.

Change-Id: I7757cc8e2872c6338c325349a56c28e1e7c69c65
diff --git a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
index c2390fb..3dcfebf 100644
--- a/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
+++ b/src/main/java/com/android/tools/r8/graph/DexEncodedMethod.java
@@ -131,7 +131,7 @@
   //   we need to maintain a set of states with (potentially different) contexts.
   private CompilationState compilationState = CompilationState.NOT_PROCESSED;
   private MethodOptimizationInfo optimizationInfo = DefaultMethodOptimizationInfo.DEFAULT_INSTANCE;
-  private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.BOTTOM;
+  private CallSiteOptimizationInfo callSiteOptimizationInfo = CallSiteOptimizationInfo.bottom();
   private int classFileVersion;
   private KotlinMethodLevelInfo kotlinMemberInfo = NO_KOTLIN_INFO;
 
@@ -1232,6 +1232,11 @@
     optimizationInfo = info;
   }
 
+  public synchronized void abandonCallSiteOptimizationInfo() {
+    checkIfObsolete();
+    callSiteOptimizationInfo = CallSiteOptimizationInfo.abandoned();
+  }
+
   public synchronized CallSiteOptimizationInfo getCallSiteOptimizationInfo() {
     checkIfObsolete();
     return callSiteOptimizationInfo;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
index 997bad7..4afe8e5 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CallSiteOptimizationInfoPropagator.java
@@ -129,7 +129,7 @@
     if (resolution != null && resolution.getResolvedHolder().isProgramClass()) {
       resolution
           .getResolvedMethod()
-          .joinCallSiteOptimizationInfo(CallSiteOptimizationInfo.TOP, appView);
+          .joinCallSiteOptimizationInfo(CallSiteOptimizationInfo.top(), appView);
     }
   }
 
@@ -180,11 +180,11 @@
       ProgramMethod target, List<Value> inValues) {
     // No method body or no argument at all.
     if (target.getDefinition().shouldNotHaveCode() || inValues.size() == 0) {
-      return CallSiteOptimizationInfo.TOP;
+      return CallSiteOptimizationInfo.top();
     }
     // If pinned, that method could be invoked via reflection.
     if (appView.appInfo().isPinned(target.getReference())) {
-      return CallSiteOptimizationInfo.TOP;
+      return CallSiteOptimizationInfo.top();
     }
     // If the method overrides a library method, it is unsure how the method would be invoked by
     // that library.
@@ -192,14 +192,14 @@
       // But, should not be reachable, since we already bail out.
       assert false
           : "Trying to compute call site optimization info for " + target.toSourceString();
-      return CallSiteOptimizationInfo.TOP;
+      return CallSiteOptimizationInfo.top();
     }
     // If the program already has illegal accesses, method resolution results will reflect that too.
     // We should avoid recording arguments in that case. E.g., b/139823850: static methods can be a
     // result of virtual call targets, if that's the only method that matches name and signature.
     int argumentOffset = target.getDefinition().isStatic() ? 0 : 1;
     if (inValues.size() != argumentOffset + target.getReference().getArity()) {
-      return CallSiteOptimizationInfo.BOTTOM;
+      return CallSiteOptimizationInfo.bottom();
     }
     return ConcreteCallSiteOptimizationInfo.fromArguments(appView, target, inValues);
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java
new file mode 100644
index 0000000..a1dece6
--- /dev/null
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/AbandonedCallSiteOptimizationInfo.java
@@ -0,0 +1,25 @@
+// 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.info;
+
+/**
+ * Used to represent that nothing is known about the argument values of a given method and all of
+ * its overrides.
+ */
+public class AbandonedCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
+
+  private static final AbandonedCallSiteOptimizationInfo INSTANCE =
+      new AbandonedCallSiteOptimizationInfo();
+
+  private AbandonedCallSiteOptimizationInfo() {}
+
+  static AbandonedCallSiteOptimizationInfo getInstance() {
+    return INSTANCE;
+  }
+
+  @Override
+  public boolean isAbandoned() {
+    return true;
+  }
+}
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
index 20ee5df..14d9dc0 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/BottomCallSiteOptimizationInfo.java
@@ -5,10 +5,16 @@
 
 // Nothing is known about arguments at call sites.
 public class BottomCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
-  static BottomCallSiteOptimizationInfo INSTANCE = new BottomCallSiteOptimizationInfo();
+
+  private static final BottomCallSiteOptimizationInfo INSTANCE =
+      new BottomCallSiteOptimizationInfo();
 
   private BottomCallSiteOptimizationInfo() {}
 
+  static BottomCallSiteOptimizationInfo getInstance() {
+    return INSTANCE;
+  }
+
   @Override
   public boolean isBottom() {
     return true;
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
index e85cf9e..96e229f 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/CallSiteOptimizationInfo.java
@@ -13,8 +13,22 @@
 // A flat lattice structure:
 //   BOTTOM, TOP, and a lattice element that holds accumulated argument info.
 public abstract class CallSiteOptimizationInfo {
-  public static BottomCallSiteOptimizationInfo BOTTOM = BottomCallSiteOptimizationInfo.INSTANCE;
-  public static TopCallSiteOptimizationInfo TOP = TopCallSiteOptimizationInfo.INSTANCE;
+
+  public static AbandonedCallSiteOptimizationInfo abandoned() {
+    return AbandonedCallSiteOptimizationInfo.getInstance();
+  }
+
+  public static BottomCallSiteOptimizationInfo bottom() {
+    return BottomCallSiteOptimizationInfo.getInstance();
+  }
+
+  public static TopCallSiteOptimizationInfo top() {
+    return TopCallSiteOptimizationInfo.getInstance();
+  }
+
+  public boolean isAbandoned() {
+    return false;
+  }
 
   public boolean isBottom() {
     return false;
@@ -34,15 +48,15 @@
 
   public CallSiteOptimizationInfo join(
       CallSiteOptimizationInfo other, AppView<?> appView, DexEncodedMethod encodedMethod) {
-    if (isBottom()) {
+    if (isAbandoned() || other.isAbandoned()) {
+      return abandoned();
+    }
+    if (isBottom() || other.isTop()) {
       return other;
     }
-    if (other.isBottom()) {
+    if (isTop() || other.isBottom()) {
       return this;
     }
-    if (isTop() || other.isTop()) {
-      return TOP;
-    }
     assert isConcreteCallSiteOptimizationInfo() && other.isConcreteCallSiteOptimizationInfo();
     return asConcreteCallSiteOptimizationInfo()
         .join(other.asConcreteCallSiteOptimizationInfo(), appView, encodedMethod);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
index 86d107a..59d1768 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/ConcreteCallSiteOptimizationInfo.java
@@ -77,7 +77,7 @@
     }
     // As soon as we know the argument collection so far does not have any useful optimization info,
     // move to TOP so that further collection can be simply skipped.
-    return TOP;
+    return top();
   }
 
   private TypeElement[] getStaticTypes(AppView<?> appView, DexEncodedMethod encodedMethod) {
@@ -184,7 +184,7 @@
     }
     // As soon as we know the current call site does not have any useful optimization info,
     // return TOP so that further collection can be simply skipped.
-    return TOP;
+    return top();
   }
 
   @Override
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java b/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java
index 5e1fdd8..241b934 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/info/TopCallSiteOptimizationInfo.java
@@ -5,10 +5,15 @@
 
 // Nothing should not be assumed about arguments at call sites.
 class TopCallSiteOptimizationInfo extends CallSiteOptimizationInfo {
-  static TopCallSiteOptimizationInfo INSTANCE = new TopCallSiteOptimizationInfo();
+
+  private static final TopCallSiteOptimizationInfo INSTANCE = new TopCallSiteOptimizationInfo();
 
   private TopCallSiteOptimizationInfo() {}
 
+  static TopCallSiteOptimizationInfo getInstance() {
+    return INSTANCE;
+  }
+
   @Override
   public boolean isTop() {
     return true;