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);
+ }
+}