Version 1.4.97

Cherry pick: Fix workaround for exceptional edges targeting loop headers.

CL: https://r8-review.googlesource.com/c/r8/+/38545
Bug: 133130870
Change-Id: I447803f18e050d93b7367226b44e8dd77d5dcb16
diff --git a/src/main/java/com/android/tools/r8/Version.java b/src/main/java/com/android/tools/r8/Version.java
index 0190082..6fa660a 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.4.96";
+  public static final String LABEL = "1.4.97";
 
   private Version() {
   }
diff --git a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
index 5075b99..d828483 100644
--- a/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
+++ b/src/main/java/com/android/tools/r8/ir/optimize/CodeRewriter.java
@@ -3837,7 +3837,7 @@
           // The loop is conditional if it has at least two normal successors.
           BasicBlock target = handler.endOfGotoChain();
           if (target != null
-              && target.getPredecessors().size() > 2
+              && target.getPredecessors().size() > 1
               && target.getNormalPredecessors().size() > 1
               && target.getNormalSuccessors().size() > 1) {
             Instruction fixit = new AlwaysMaterializingNop();
diff --git a/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestIdenticalNormalAndExceptionalEdge.java b/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestIdenticalNormalAndExceptionalEdge.java
new file mode 100644
index 0000000..ff6f074
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestIdenticalNormalAndExceptionalEdge.java
@@ -0,0 +1,28 @@
+// 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.debuginfo;
+
+import java.util.Arrays;
+
+public class Regress111337896TestIdenticalNormalAndExceptionalEdge {
+
+  public static void regress111337896() {
+    for (Object o : Arrays.asList(new Object())) {
+      try {
+        doThrow();
+      } catch (Exception e) {
+        // Empty handler causes segfault on some ART 5.0 x86 devices.
+      }
+    }
+  }
+
+  public static void doThrow() throws Exception {
+    throw new Exception();
+  }
+
+  public static void main(String[] args) {
+    regress111337896();
+    System.out.print("aok");
+  }
+}
diff --git a/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestRunner.java b/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestRunner.java
index 1404f35..be25d4a 100644
--- a/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestRunner.java
+++ b/src/test/java/com/android/tools/r8/debuginfo/Regress111337896TestRunner.java
@@ -68,4 +68,52 @@
       assertEquals(expectedNops, nopsFound);
     }
   }
+
+  @Test
+  public void testIdenticalNormalAndExceptionalEdge() throws Exception {
+    Class clazz = Regress111337896TestIdenticalNormalAndExceptionalEdge.class;
+
+    // Check the regression test is valid code.
+    String expected = "aok";
+    assertEquals(expected, runOnJava(clazz));
+
+    for (AndroidApiLevel minApi : Arrays.asList(AndroidApiLevel.L, AndroidApiLevel.M)) {
+      for (CompilationMode mode : CompilationMode.values()) {
+        AndroidAppConsumers appSink = new AndroidAppConsumers();
+        D8.run(
+            D8Command.builder()
+                .addProgramFiles(ToolHelper.getClassFileForTestClass(clazz))
+                .setProgramConsumer(appSink.wrapDexIndexedConsumer(null))
+                .setMode(mode)
+                .setMinApiLevel(minApi.getLevel())
+                .build());
+        AndroidApp app = appSink.build();
+        assertEquals(expected, runOnArt(app, clazz.getCanonicalName()));
+
+        // Check that the compiled output contains a nop to workaround the issue.
+        // We can't really check much else as this only reproduces on some physical x86_64 devices.
+        checkIdenticalNormalAndExceptionalEdge(
+            inspectMethod(app, clazz, "void", "regress111337896"), mode, minApi);
+      }
+    }
+  }
+
+  private void checkIdenticalNormalAndExceptionalEdge(
+      DebugInfoInspector info, CompilationMode mode, AndroidApiLevel minApi) {
+    info.checkStartLine(11);
+    assertEquals(1, info.checkLineExists(13));
+    int nopsFound = 0;
+    for (Instruction instruction : info.getMethod().getCode().asDexCode().instructions) {
+      if (instruction instanceof Nop) {
+        nopsFound++;
+      }
+    }
+    if (mode == CompilationMode.DEBUG) {
+      assertEquals(0, nopsFound);
+    } else {
+      // In release mode the workaround will have inserted a nop if below M.
+      int expectedNops = minApi.getLevel() < AndroidApiLevel.M.getLevel() ? 1 : 0;
+      assertEquals(expectedNops, nopsFound);
+    }
+  }
 }