Move adding extra goto blocks to InlineInto

There are some cases in which we need to add extra goto blocks
when inlining to avoid critical edges. The `TryBoundary` of
`kind:exit` instructions will always have more than one successor
(normal flow and exceptional flow). If its normal flow successor
has more than one predecessor, we would be introducing a critical edge.

We can avoid the critical edge in InlineInto instead of doing it in
the builder which helps decoupling those two stages as well as
simplifying surrounding code.

We also have the benefit of adding the extra goto blocks only
when necessary.

Bug: 227283224
Test: art/test/testrunner/testrunner.py --host --64 --optimizing -b
Change-Id: Ibe21623c94c798f7cea60ff892064e63a38a787a
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 85d0fe7..8491c24 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -2893,19 +2893,8 @@
       HInstruction* last = predecessor->GetLastInstruction();
 
       // At this point we might either have:
-      // A) Return/ReturnVoid/Throw as the last instruction
-      // B) `Return/ReturnVoid->TryBoundary->Goto` as the last instruction chain
-      // C) `Return/ReturnVoid->Goto` as the last instruction chain. This exists when we added the
-      //     extra Goto because we had a TryBoundary which we could eliminate in DCE after
-      //     substituting arguments.
-      // D) `Throw->TryBoundary` as the last instruction chain
-
-      const bool saw_goto = last->IsGoto();
-      if (saw_goto) {
-        DCHECK(predecessor->IsSingleGoto());
-        predecessor = predecessor->GetSinglePredecessor();
-        last = predecessor->GetLastInstruction();
-      }
+      // A) Return/ReturnVoid/Throw as the last instruction, or
+      // B) `Return/ReturnVoid/Throw->TryBoundary` as the last instruction chain
 
       const bool saw_try_boundary = last->IsTryBoundary();
       if (saw_try_boundary) {
@@ -2915,14 +2904,7 @@
         last = predecessor->GetLastInstruction();
       }
 
-      // Check that if we have an instruction chain, it is one of the allowed ones.
-      DCHECK_IMPLIES(saw_goto, last->IsReturnVoid() || last->IsReturn());
-
       if (last->IsThrow()) {
-        // The chain `Throw->TryBoundary` is allowed but not `Throw->TryBoundary->Goto` since that
-        // would mean a Goto will point to exit after ReplaceSuccessor.
-        DCHECK(!saw_goto);
-
         if (at->IsTryBlock()) {
           DCHECK(!saw_try_boundary) << "We don't support inlining of try blocks into try blocks.";
           // Create a TryBoundary of kind:exit and point it to the Exit block.
@@ -2982,6 +2964,19 @@
         }
         predecessor->AddInstruction(new (allocator) HGoto(last->GetDexPc()));
         predecessor->RemoveInstruction(last);
+
+        if (saw_try_boundary) {
+          predecessor = to->GetPredecessors()[pred];
+          DCHECK(predecessor->EndsWithTryBoundary());
+          DCHECK_EQ(predecessor->GetNormalSuccessors().size(), 1u);
+          if (predecessor->GetSuccessors()[0]->GetPredecessors().size() > 1) {
+            outer_graph->SplitCriticalEdge(predecessor, to);
+            rerun_dominance = true;
+            if (predecessor->GetLoopInformation() != nullptr) {
+              rerun_loop_analysis = true;
+            }
+          }
+        }
       }
     }
     if (rerun_loop_analysis) {