Preserve line information when targeting VMs with PC reporting.

Bug: b/279555568
Change-Id: I47457625f511799530b90d530f4c8ac56bd1c327
diff --git a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
index e54e691..b3b5680 100644
--- a/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
+++ b/src/main/java/com/android/tools/r8/graph/LazyCfCode.java
@@ -1195,7 +1195,8 @@
             || keep.localVariableTable
             || keep.localVariableTypeTable
             || reachabilitySensitive;
-    boolean lineInfo = keep.lineNumberTable;
+    boolean lineInfo =
+        (keep.lineNumberTable || application.options.canUseNativeDexPcInsteadOfDebugInfo());
     boolean methodParaeters = keep.methodParameters;
 
     if (!localsInfo && !lineInfo && !methodParaeters) {
diff --git a/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java b/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java
new file mode 100644
index 0000000..35ae932
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/NoKeepLineAttributeTest.java
@@ -0,0 +1,71 @@
+// Copyright (c) 2023, 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.debuginfo;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.ToolHelper.DexVm.Version;
+import com.android.tools.r8.naming.retrace.StackTrace.StackTraceLine;
+import java.util.List;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+@RunWith(Parameterized.class)
+public class NoKeepLineAttributeTest extends TestBase {
+
+  private final TestParameters parameters;
+
+  @Parameterized.Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters()
+        // Only run the test on VMs that have native pc support.
+        .withDexRuntimesStartingFromExcluding(Version.V7_0_0)
+        .withAllApiLevels()
+        .build();
+  }
+
+  public NoKeepLineAttributeTest(TestParameters parameters) {
+    this.parameters = parameters;
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(NoKeepLineAttributeTest.class)
+        .addKeepMainRule(TestClass.class)
+        .setMinApi(parameters)
+        .run(parameters.getRuntime(), TestClass.class)
+        .assertFailureWithErrorThatThrows(RuntimeException.class)
+        .inspectStackTrace(
+            stacktrace -> {
+              List<StackTraceLine> stackTraceLines = stacktrace.getStackTraceLines();
+              assertEquals(1, stackTraceLines.size());
+              StackTraceLine stackTraceLine = stackTraceLines.get(0);
+              // The frame will always have a line as the VM is reporting the PC.
+              assertTrue(stackTraceLine.hasLineNumber());
+              if (parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
+                // If the compile-time API is before native support then no line info is present.
+                // The "line" will be the PC and thus small.
+                assertTrue(stackTraceLine.lineNumber < 10);
+              } else {
+                // If the compile-time API is after native support then the compiler will retain and
+                // emit the mapping from PC to original line. Here line 50 is to ensure it is not a
+                // low PC value.
+                assertTrue(stackTraceLine.lineNumber > 50);
+              }
+            });
+  }
+
+  static class TestClass {
+
+    public static void main(String[] args) {
+      throw new RuntimeException("My Exception!");
+    }
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
index cba424a..d32f6f9 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/NonNullParamTest.java
@@ -212,8 +212,11 @@
   private boolean canSharePrintCallInSuccessorBlock() {
     // With API level >= Q we get a register assignment that allows us to share the print call in a
     // successor block. See also InternalOptions.canHaveThisJitCodeDebuggingBug().
+    // Due to including line-info (b/279555568) this is always false. Keeping the conflicting
+    // predicates here for documentation of the previously witnessed difference.
     return parameters.isDexRuntime()
-        && parameters.getApiLevel().getLevel() >= AndroidApiLevel.Q.getLevel();
+        && parameters.getApiLevel().getLevel() >= AndroidApiLevel.Q.getLevel()
+        && parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport());
   }
 
   @Test
@@ -241,6 +244,7 @@
                                     NonNullParamAfterInvokeInterface.class,
                                     NonNullParamInterfaceImpl.class))
                     .addOptionsModification(this::disableDevirtualization)
+                    .addKeepAttributeLineNumberTable()
                     .enableAlwaysInliningAnnotations()
                     .enableInliningAnnotations()
                     .enableNeverClassInliningAnnotations()
@@ -252,8 +256,7 @@
     MethodSubject checkViaCall = mainSubject.uniqueMethodWithOriginalName("checkViaCall");
     assertThat(checkViaCall, isPresent());
     assertEquals(0, countActCall(checkViaCall));
-    // The DEX backend reuses the System.out.println invoke.
-    assertEquals(parameters.isCfRuntime() ? 2 : 1, countPrintCall(checkViaCall));
+    assertEquals(2, countPrintCall(checkViaCall));
   }
 
   private long countCallToParamNullCheck(MethodSubject method) {
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
index 7464aec..a78dbbf 100644
--- a/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
+++ b/src/test/java/com/android/tools/r8/ir/optimize/string/StringBuilderTests.java
@@ -222,10 +222,14 @@
               FoundMethodSubject foundMethodSubject = method.asFoundMethodSubject();
               assertEquals(
                   stringBuilderTest.stringBuilders, countStringBuilderInits(foundMethodSubject));
-              if (parameters.isCfRuntime()
+              if ((parameters.isCfRuntime()
+                      || parameters
+                          .getApiLevel()
+                          .isGreaterThanOrEqualTo(apiLevelWithPcAsLineNumberSupport()))
                   && (stringBuilderTest.getMethodName().equals("diamondWithUseTest")
                       || stringBuilderTest.getMethodName().equals("intoPhiTest"))) {
-                // We are not doing block suffix optimization in CF.
+                // We are not doing block suffix optimization in CF and line/pc info prohibits
+                // sharing.
                 assertEquals(
                     stringBuilderTest.appends + 1, countStringBuilderAppends(foundMethodSubject));
               } else {
diff --git a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
index d56e49c..c4fa84b 100644
--- a/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
+++ b/src/test/java/com/android/tools/r8/regress/Regress160394262Test.java
@@ -59,7 +59,13 @@
   private void checkJoinerIsClassInlined(CodeInspector inspector) {
     assertThat(inspector.clazz(Joiner.class.getTypeName() + "$1"), isAbsent());
     // TODO(b/160640028): Joiner should be class inlined.
-    assertThat(inspector.clazz(Joiner.class), isPresent());
+    //   When line info tables are kept we appear to successfully inline Joiner. Reason unknown.
+    if (parameters.isCfRuntime()
+        || parameters.getApiLevel().isLessThan(apiLevelWithPcAsLineNumberSupport())) {
+      assertThat(inspector.clazz(Joiner.class), isPresent());
+    } else {
+      assertThat(inspector.clazz(Joiner.class), isAbsent());
+    }
   }
 
   static class TestClass {