Account for catch handlers in simple inlining constraint analysis

Bug: b/344130644
Change-Id: Ic94763c86dbbe6786690441e7e1a598572dc024b
diff --git a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
index ecc3720..41e9bb0 100644
--- a/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
+++ b/src/main/java/com/android/tools/r8/ir/analysis/inlining/SimpleInliningConstraintAnalysis.java
@@ -88,8 +88,8 @@
 
   private SimpleInliningConstraintWithDepth analyzeInstructionsInBlock(
       BasicBlock block, int instructionDepth, InstructionIterator instructionIterator) {
-    // If we reach a block that has already been seen, give up.
-    if (!seen.add(block)) {
+    // If we reach a block that has already been seen, or one that has catch handlers, then give up.
+    if (!seen.add(block) || block.hasCatchHandlers()) {
       return SimpleInliningConstraintWithDepth.getNever();
     }
 
diff --git a/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningWithCatchHandlersTest.java b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningWithCatchHandlersTest.java
new file mode 100644
index 0000000..e68491d
--- /dev/null
+++ b/src/test/java/com/android/tools/r8/ir/optimize/inliner/conditionalsimpleinlining/ConditionalSimpleInliningWithCatchHandlersTest.java
@@ -0,0 +1,117 @@
+// Copyright (c) 2024, 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.inliner.conditionalsimpleinlining;
+
+import static com.android.tools.r8.utils.codeinspector.Matchers.isPresent;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+
+import com.android.tools.r8.TestBase;
+import com.android.tools.r8.TestParameters;
+import com.android.tools.r8.TestParametersCollection;
+import com.android.tools.r8.utils.codeinspector.InstructionSubject;
+import com.android.tools.r8.utils.codeinspector.MethodSubject;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.Parameterized.Parameter;
+import org.junit.runners.Parameterized.Parameters;
+
+@RunWith(Parameterized.class)
+public class ConditionalSimpleInliningWithCatchHandlersTest extends TestBase {
+
+  @Parameter(0)
+  public TestParameters parameters;
+
+  @Parameters(name = "{0}")
+  public static TestParametersCollection data() {
+    return getTestParameters().withAllRuntimesAndApiLevels().build();
+  }
+
+  @Test
+  public void test() throws Exception {
+    testForR8(parameters.getBackend())
+        .addInnerClasses(getClass())
+        .addKeepClassAndMembersRules(Main.class)
+        .setMinApi(parameters)
+        .compile()
+        .inspect(
+            inspector -> {
+              MethodSubject doStuffMethodSubject =
+                  inspector.clazz(Utils.class).uniqueMethodWithOriginalName("doStuff");
+              assertThat(doStuffMethodSubject, isPresent());
+
+              MethodSubject mainMethodSubject = inspector.clazz(Main.class).mainMethod();
+              assertThat(mainMethodSubject, isPresent());
+              assertEquals(
+                  6,
+                  mainMethodSubject
+                      .streamInstructions()
+                      .filter(InstructionSubject::isInvokeMethod)
+                      .filter(
+                          x ->
+                              x.getMethod()
+                                  .isIdenticalTo(doStuffMethodSubject.getMethod().getReference()))
+                      .count());
+            })
+        .run(parameters.getRuntime(), Main.class)
+        .assertSuccessWithEmptyOutput();
+  }
+
+  static class Main {
+
+    public static void main(String[] args) {
+      Object o = get();
+
+      // Should not be inlined since the catch handler is not simple.
+      if (o != null) {
+        Utils.doStuff(o);
+      }
+
+      // Should not be inlined since the simple inlining constraint is not satisfied.
+      Utils.doStuff(o);
+      Utils.doStuff(o);
+      Utils.doStuff(o);
+      Utils.doStuff(o);
+      Utils.doStuff(o);
+    }
+
+    static Object get() {
+      return new Object();
+    }
+  }
+
+  static class Utils {
+
+    static void doStuff(Object o) {
+      if (o != null) {
+        try {
+          o.toString();
+        } catch (Exception e) {
+          System.out.print('N');
+          System.out.print('o');
+          System.out.print('t');
+          System.out.print(' ');
+          System.out.print('s');
+          System.out.print('i');
+          System.out.print('m');
+          System.out.print('p');
+          System.out.print('l');
+          System.out.print('e');
+        }
+      } else {
+        System.out.print('N');
+        System.out.print('o');
+        System.out.print('t');
+        System.out.print(' ');
+        System.out.print('s');
+        System.out.print('i');
+        System.out.print('m');
+        System.out.print('p');
+        System.out.print('l');
+        System.out.print('e');
+      }
+    }
+  }
+}