Update loop information correctly in MaybeAddExtraGotoBlocks

There are cases in which we have a
  Return->TryBoundary kind:exit->Exit
chain inside of a loop, and that said TryBoundary has loop
information. Set that information in the new goto block.

Bug: 261731237
Fixes: 261731237
Test: dex2oat compiling the apps in the bug
Test: art/test/testrunner/testrunner.py --host --64 --optimizing -b
Change-Id: I224d6cb449d1a7a1a987f4e9022098f84ae4fba2
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 897d8b7..c9609e0 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -124,12 +124,9 @@
     if (NeedsExtraGotoBlock(predecessor)) {
       HBasicBlock* new_goto = graph_->SplitEdgeAndUpdateRPO(predecessor, exit);
       new_goto->AddInstruction(new (graph_->GetAllocator()) HGoto(predecessor->GetDexPc()));
-
-      // No need to update loop info of the new block.
-      DCHECK(!predecessor->IsInLoop())
-          << " we should only add the extra Goto blocks for Return/ReturnVoid->TryBoundary->Exit "
-          << "chains. In those chains, the TryBoundary of kind:exit should never be a part of a "
-          << "loop";
+      if (predecessor->IsInLoop()) {
+        new_goto->SetLoopInformation(predecessor->GetLoopInformation());
+      }
 
       // Update domination chain
       if (!predecessor->GetDominatedBlocks().empty()) {
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stderr.txt
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/expected-stdout.txt
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/info.txt b/test/2249-checker-return-try-boundary-exit-in-loop/info.txt
new file mode 100644
index 0000000..db79b12
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/info.txt
@@ -0,0 +1,3 @@
+Tests that there is a case in which we have a
+  Return->TryBoundary kind:exit->Exit
+chain inside of a loop, and that said TryBoundary has loop information.
diff --git a/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java b/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java
new file mode 100644
index 0000000..070cd9f
--- /dev/null
+++ b/test/2249-checker-return-try-boundary-exit-in-loop/src/Main.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+    public static void main(String[] args) throws Exception {
+        assertNotNull($noinline$testReturnTryBoundaryExitInLoop(new Object()));
+    }
+
+    public static void assertNotNull(Object o) {
+        if (o == null) {
+            throw new Error("Expected not null!");
+        }
+    }
+
+    // Simple method to have a call inside of the synchronized block.
+    private static Object $noinline$call() {
+        return new Object();
+    }
+
+    // Consistency check: Three try boundary kind:exit. One for the explicit try catch, and two for
+    // the synchronized block (normal, and exceptional path).
+
+    /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+    /// CHECK:     TryBoundary kind:exit
+    /// CHECK:     TryBoundary kind:exit
+    /// CHECK:     TryBoundary kind:exit
+    /// CHECK-NOT: TryBoundary kind:exit
+
+    /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+    /// CHECK: Return loop:B2
+
+    /// CHECK-START: java.lang.Object Main.$inline$inner(java.lang.Object) builder (after)
+    /// CHECK: TryBoundary kind:exit loop:B2
+    /// CHECK: TryBoundary kind:exit loop:B2
+    /// CHECK: TryBoundary kind:exit loop:B2
+    private static Object $inline$inner(Object o) {
+        for (int i = 0; i < 4; i++) {
+            try {
+                synchronized (o) {
+                    return $noinline$call();
+                }
+            } catch (Error e) {
+                continue;
+            }
+        }
+        return null;
+    }
+
+    // Simple outer to inline `inner`.
+    private static Object $noinline$testReturnTryBoundaryExitInLoop(Object o) {
+        return $inline$inner(o);
+    }
+}