Retrieve single call target without liveness if possible.

Bug: 128421006
Change-Id: Ifb1cbd6c0eaa3ff34b67b0469f12f61f90ede657
diff --git a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
index a66e958..37102ef 100644
--- a/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
+++ b/src/main/java/com/android/tools/r8/ir/conversion/IRConverter.java
@@ -915,7 +915,7 @@
 
     previous = printMethod(code, "IR after disable assertions (SSA)", previous);
 
-    if (options.enableNonNullTracking && nonNullTracker != null) {
+    if (nonNullTracker != null) {
       nonNullTracker.addNonNull(code);
       assert code.isConsistentSSA();
     }
@@ -974,7 +974,7 @@
       invertConditionalsForTesting(code);
     }
 
-    if (options.enableNonNullTracking && nonNullTracker != null) {
+    if (nonNullTracker != null) {
       // Computation of non-null parameters on normal exits rely on the existence of non-null IRs.
       nonNullTracker.computeNonNullParamOnNormalExits(feedback, code);
       nonNullTracker.cleanupNonNull(code);
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
index ec338ca..489891a 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/NonNullTracker.java
@@ -120,14 +120,28 @@
             knownToBeNonNullValues.add(knownToBeNonNullValue);
           }
         }
-        if (current.isInvokeMethod()
-            && !current.isInvokePolymorphic()
-            && appView.appInfo().hasLiveness()) {
-          DexEncodedMethod singleTarget =
-              current
-                  .asInvokeMethod()
-                  .lookupSingleTarget(
-                      appView.appInfo().withLiveness(), code.method.method.holder);
+        if (current.isInvokeMethod() && !current.isInvokePolymorphic()) {
+          DexEncodedMethod singleTarget = null;
+          if (appView.enableWholeProgramOptimizations()) {
+            assert appView.appInfo().hasLiveness();
+            singleTarget =
+                current
+                    .asInvokeMethod()
+                    .lookupSingleTarget(
+                        appView.appInfo().withLiveness(), code.method.method.holder);
+          } else {
+            // Even in D8, invoke-{direct|static} can be resolved without liveness.
+            // Due to the incremental compilation, though, it is allowed only if the holder of the
+            // invoked method is same as that of the method we are processing now.
+            DexMethod invokedMethod = current.asInvokeMethod().getInvokedMethod();
+            if (invokedMethod.holder == code.method.method.holder) {
+              if (current.isInvokeDirect()) {
+                singleTarget = appView.appInfo().lookupDirectTarget(invokedMethod);
+              } else if (current.isInvokeStatic()) {
+                singleTarget = appView.appInfo().lookupStaticTarget(invokedMethod);
+              }
+            }
+          }
           if (singleTarget != null
               && singleTarget.getOptimizationInfo().getNonNullParamOnNormalExits() != null) {
             BitSet facts = singleTarget.getOptimizationInfo().getNonNullParamOnNormalExits();