Update domination tree when removing tries, if the catch doesn't exit

When eliminating the unnecessary try and its catch block we turn the
TryBoundary instructions into Goto instructions. If one of these
instructions is pointing to the exit block, we use its single
predecessor instead. If this TryBoundary-turned-into-Goto instruction
was the only one pointing to the Exit, we also have to update the dominators.

Bug: 260387991
Fixes: 260387991
Test: art/test/testrunner/testrunner.py --host --64 --optimizing -b
Test: dex2oat compiling the app in the bug
Change-Id: If05b60e81b2441c4c2235ffad7649813b98d3d69
diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc
index 4c7dd62..410ef0a 100644
--- a/compiler/optimizing/dead_code_elimination.cc
+++ b/compiler/optimizing/dead_code_elimination.cc
@@ -555,13 +555,22 @@
       DisconnectHandlersAndUpdateTryBoundary(block, any_handler_in_loop);
 
       if (block->GetSingleSuccessor()->IsExitBlock()) {
-        // `predecessor` used to be a single exit TryBoundary that got turned into a Goto. It
+        // `block` used to be a single exit TryBoundary that got turned into a Goto. It
         // is now pointing to the exit which we don't allow. To fix it, we disconnect
-        // `predecessor` from its predecessor and RemoveDeadBlocks will remove it from the
+        // `block` from its predecessor and RemoveDeadBlocks will remove it from the
         // graph.
         DCHECK(block->IsSingleGoto());
         HBasicBlock* predecessor = block->GetSinglePredecessor();
         predecessor->ReplaceSuccessor(block, graph_->GetExitBlock());
+
+        if (!block->GetDominatedBlocks().empty()) {
+          // Update domination tree if `block` dominates a block to keep the graph consistent.
+          DCHECK_EQ(block->GetDominatedBlocks().size(), 1u);
+          DCHECK_EQ(graph_->GetExitBlock()->GetDominator(), block);
+          predecessor->AddDominatedBlock(graph_->GetExitBlock());
+          graph_->GetExitBlock()->SetDominator(predecessor);
+          block->RemoveDominatedBlock(graph_->GetExitBlock());
+        }
       }
     }
   }
diff --git a/test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt b/test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/expected-stderr.txt
diff --git a/test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt b/test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/expected-stdout.txt
diff --git a/test/2248-checker-smali-remove-try-until-the-end/info.txt b/test/2248-checker-smali-remove-try-until-the-end/info.txt
new file mode 100644
index 0000000..0d7ded0
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/info.txt
@@ -0,0 +1,2 @@
+Smali test checking that we correctly set the domination graph when having a
+try until the end of the method, and the catch doesn't flow to the exit.
diff --git a/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali b/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali
new file mode 100644
index 0000000..26d4de7
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/smali/b_260387991.smali
@@ -0,0 +1,45 @@
+#
+# 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.
+.class public LB260387991;
+
+.super Ljava/lang/Object;
+
+# When eliminating the unnecessary try and its catch block we turn the
+# TryBoundary instructions into Goto instructions. If one of these
+# instructions is pointing to the exit block, we use its single
+# predecessor instead. If this TryBoundary-turned-into-Goto instruction
+# was the only one pointing to the Exit, we also have to update the dominators.
+
+## CHECK-START: void B260387991.testInfiniteCatch() dead_code_elimination$initial (before)
+## CHECK: TryBoundary
+## CHECK: TryBoundary
+
+## CHECK-START: void B260387991.testInfiniteCatch() dead_code_elimination$initial (after)
+## CHECK-NOT: TryBoundary
+.method public static testInfiniteCatch()V
+   .registers 4
+    const/4 v0, 0x2
+    const/4 v1, 0x4
+    :try_start
+    div-int v0, v1, v0
+    return-void
+    :try_end
+    .catchall {:try_start .. :try_end} :catch_all
+
+    # Infinite catch block which does not lead to the exit block.
+    :catch_all
+    nop
+    goto :catch_all
+.end method
diff --git a/test/2248-checker-smali-remove-try-until-the-end/src/Main.java b/test/2248-checker-smali-remove-try-until-the-end/src/Main.java
new file mode 100644
index 0000000..1ad1a26
--- /dev/null
+++ b/test/2248-checker-smali-remove-try-until-the-end/src/Main.java
@@ -0,0 +1,19 @@
+/*
+ * 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) {}
+}