ART: Changes to try-catch in GraphBuilder

This patch adds an additional case into the insertion algorithm for
HTryBoundary inside HGraphBuilder in order to better handle catch
blocks covered by a TryItem.

Building SSA form also required to stop combining HTryBoundaries for
neighbouring TryItems because it was not clear which exception
handlers belong to which try block.

Change-Id: Ic68bd6ef98fee784609fa593cb08dca1f00a15e0
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index bde2c70..883c983 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -306,6 +306,22 @@
   }
 }
 
+void HGraphBuilder::SplitTryBoundaryEdge(HBasicBlock* predecessor,
+                                         HBasicBlock* successor,
+                                         HTryBoundary::BoundaryKind kind,
+                                         const DexFile::CodeItem& code_item,
+                                         const DexFile::TryItem& try_item) {
+  // Split the edge with a single TryBoundary instruction.
+  HTryBoundary* try_boundary = new (arena_) HTryBoundary(kind);
+  HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, successor);
+  try_entry_block->AddInstruction(try_boundary);
+
+  // Link the TryBoundary to the handlers of `try_item`.
+  for (CatchHandlerIterator it(code_item, try_item); it.HasNext(); it.Next()) {
+    try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
+  }
+}
+
 void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) {
   if (code_item.tries_size_ == 0) {
     return;
@@ -326,47 +342,46 @@
         continue;
       }
 
-      // Find predecessors which are not covered by the same TryItem range. Such
-      // edges enter the try block and will have a TryBoundary inserted.
-      for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
-        HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
-        HTryBoundary* try_boundary = nullptr;
-        if (predecessor->IsSingleTryBoundary()) {
-          try_boundary = predecessor->GetLastInstruction()->AsTryBoundary();
-          if (try_boundary->GetNormalFlowSuccessor() == try_block
-              && try_block->IsFirstIndexOfPredecessor(predecessor, i)) {
+      if (try_block->IsCatchBlock()) {
+        // Catch blocks are always considered an entry point into the TryItem in
+        // order to avoid splitting exceptional edges (they might not have been
+        // created yet). We separate the move-exception (if present) from the
+        // rest of the block and insert a TryBoundary after it.
+        HInstruction* split_position = try_block->GetFirstInstruction();
+        if (split_position->IsLoadException()) {
+          DCHECK(split_position->GetNext()->IsStoreLocal());
+          split_position = split_position->GetNext()->GetNext();
+        }
+        DCHECK(split_position != nullptr);
+        HBasicBlock* catch_block = try_block;
+        try_block = catch_block->SplitBefore(split_position);
+        SplitTryBoundaryEdge(catch_block, try_block, HTryBoundary::kEntry, code_item, *try_item);
+      } else {
+        // For non-catch blocks, find predecessors which are not covered by the
+        // same TryItem range. Such edges enter the try block and will have
+        // a TryBoundary inserted.
+        for (size_t i = 0; i < try_block->GetPredecessors().Size(); ++i) {
+          HBasicBlock* predecessor = try_block->GetPredecessors().Get(i);
+          if (predecessor->IsSingleTryBoundary()) {
             // The edge was already split because of an exit from a neighbouring
-            // TryItem and `predecessor` is the block with a TryBoundary created
-            // between the two original blocks. We do not split the edge again.
-            DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end));
-            DCHECK(try_boundary->IsTryExit());
-            DCHECK(!try_boundary->IsTryEntry());
-            try_boundary->SetIsTryEntry();
+            // TryItem. We split it again and insert an entry point.
+            if (kIsDebugBuild) {
+              HTryBoundary* last_insn = predecessor->GetLastInstruction()->AsTryBoundary();
+              DCHECK(!last_insn->IsEntry());
+              DCHECK_EQ(last_insn->GetNormalFlowSuccessor(), try_block);
+              DCHECK(try_block->IsFirstIndexOfPredecessor(predecessor, i));
+              DCHECK(!IsBlockInPcRange(predecessor->GetSinglePredecessor(), try_start, try_end));
+            }
+          } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) {
+            // This is an entry point into the TryItem and the edge has not been
+            // split yet. That means that `predecessor` is not in a TryItem, or
+            // it is in a different TryItem and we happened to iterate over this
+            // block first. We split the edge and insert an entry point.
           } else {
-            // This is an edge between a previously created TryBoundary and its
-            // handler. We skip it because it is exceptional flow.
-            DCHECK(try_block->IsCatchBlock());
-            DCHECK(try_boundary->HasExceptionHandler(try_block));
+            // Not an edge on the boundary of the try block.
             continue;
           }
-        } else if (!IsBlockInPcRange(predecessor, try_start, try_end)) {
-          // This is an entry point into the TryItem and the edge has not been
-          // split yet. That means that either `predecessor` is not in a TryItem,
-          // or it is in a different TryItem and we happened to iterate over
-          // this block first. We split the edge and `predecessor` may add its
-          // own exception handlers later.
-          try_boundary = new (arena_) HTryBoundary(/* is_entry */ true, /* is_exit */ false);
-          HBasicBlock* try_entry_block = graph_->SplitEdge(predecessor, try_block);
-          try_entry_block->AddInstruction(try_boundary);
-        } else {
-          // Not an edge on the boundary of the try block.
-          continue;
-        }
-        DCHECK(try_boundary != nullptr);
-
-        // Link the TryBoundary block to the handlers of this TryItem.
-        for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
-          try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
+          SplitTryBoundaryEdge(predecessor, try_block, HTryBoundary::kEntry, code_item, *try_item);
         }
       }
 
@@ -374,45 +389,37 @@
       // edges exit the try block and will have a TryBoundary inserted.
       for (size_t i = 0; i < try_block->GetSuccessors().Size(); ++i) {
         HBasicBlock* successor = try_block->GetSuccessors().Get(i);
-        HTryBoundary* try_boundary = nullptr;
-        if (successor->IsSingleTryBoundary()) {
+        if (successor->IsCatchBlock()) {
+          // A catch block is always considered an entry point into its TryItem.
+          // We therefore assume this is an exit point, regardless of whether
+          // the catch block is in a different TryItem or not.
+        } else if (successor->IsSingleTryBoundary()) {
           // The edge was already split because of an entry into a neighbouring
-          // TryItem. We do not split the edge again.
-          try_boundary = successor->GetLastInstruction()->AsTryBoundary();
-          DCHECK_EQ(try_block, successor->GetSinglePredecessor());
-          DCHECK(try_boundary->IsTryEntry());
-          DCHECK(!try_boundary->IsTryExit());
-          DCHECK(!IsBlockInPcRange(try_boundary->GetNormalFlowSuccessor(), try_start, try_end));
-          try_boundary->SetIsTryExit();
+          // TryItem. We split it again and insert an exit.
+          if (kIsDebugBuild) {
+            HTryBoundary* last_insn = successor->GetLastInstruction()->AsTryBoundary();
+            DCHECK_EQ(try_block, successor->GetSinglePredecessor());
+            DCHECK(last_insn->IsEntry());
+            DCHECK(!IsBlockInPcRange(last_insn->GetNormalFlowSuccessor(), try_start, try_end));
+          }
         } else if (!IsBlockInPcRange(successor, try_start, try_end)) {
           // This is an exit out of the TryItem and the edge has not been split
           // yet. That means that either `successor` is not in a TryItem, or it
           // is in a different TryItem and we happened to iterate over this
-          // block first. We split the edge and `successor` may add its own
-          // exception handlers later.
+          // block first. We split the edge and insert an exit.
           HInstruction* last_instruction = try_block->GetLastInstruction();
           if (last_instruction->IsReturn() || last_instruction->IsReturnVoid()) {
             DCHECK_EQ(successor, exit_block_);
             // Control flow exits the try block with a Return(Void). Because
             // splitting the edge would invalidate the invariant that Return
             // always jumps to Exit, we move the Return outside the try block.
-            HBasicBlock* return_block = try_block->SplitBefore(last_instruction);
-            graph_->AddBlock(return_block);
-            successor = return_block;
+            successor = try_block->SplitBefore(last_instruction);
           }
-          try_boundary = new (arena_) HTryBoundary(/* is_entry */ false, /* is_exit */ true);
-          HBasicBlock* try_exit_block = graph_->SplitEdge(try_block, successor);
-          try_exit_block->AddInstruction(try_boundary);
         } else {
           // Not an edge on the boundary of the try block.
           continue;
         }
-        DCHECK(try_boundary != nullptr);
-
-        // Link the TryBoundary block to the handlers of this TryItem.
-        for (CatchHandlerIterator it(code_item, *try_item); it.HasNext(); it.Next()) {
-          try_boundary->AddExceptionHandler(FindBlockStartingAt(it.GetHandlerAddress()));
-        }
+        SplitTryBoundaryEdge(try_block, successor, HTryBoundary::kExit, code_item, *try_item);
       }
     }
   }
diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h
index 58d85e9..9744a55 100644
--- a/compiler/optimizing/builder.h
+++ b/compiler/optimizing/builder.h
@@ -97,9 +97,15 @@
   void MaybeUpdateCurrentBlock(size_t dex_pc);
   HBasicBlock* FindBlockStartingAt(int32_t dex_pc) const;
   HBasicBlock* FindOrCreateBlockStartingAt(int32_t dex_pc);
+
   bool IsBlockInPcRange(HBasicBlock* block, uint32_t dex_pc_start, uint32_t dex_pc_end);
   void CreateBlocksForTryCatch(const DexFile::CodeItem& code_item);
   void InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item);
+  void SplitTryBoundaryEdge(HBasicBlock* predecessor,
+                            HBasicBlock* successor,
+                            HTryBoundary::BoundaryKind kind,
+                            const DexFile::CodeItem& code_item,
+                            const DexFile::TryItem& try_item);
 
   void InitializeLocals(uint16_t count);
   HLocal* GetLocalAt(int register_index) const;
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index c41574c..5cb9772 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -380,12 +380,7 @@
   }
 
   void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE {
-    StartAttributeStream("is_entry") << std::boolalpha
-                                     << try_boundary->IsTryEntry()
-                                     << std::noboolalpha;
-    StartAttributeStream("is_exit") << std::boolalpha
-                                    << try_boundary->IsTryExit()
-                                    << std::noboolalpha;
+    StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
   }
 
   bool IsPass(const char* name) {
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 881f9ec..b82e37c 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1050,6 +1050,7 @@
   successors_.Reset();
   AddSuccessor(new_block);
 
+  GetGraph()->AddBlock(new_block);
   return new_block;
 }
 
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 95ea966..5c10245 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -729,9 +729,10 @@
   bool IsExceptionalSuccessor(size_t idx) const;
 
   // Split the block into two blocks just before `cursor`. Returns the newly
-  // created, latter block. Note that this method will create a Goto at the end
-  // of the former block and will create an edge between them. It will not,
-  // however, update the graph, reverse post order or loop information.
+  // created, latter block. Note that this method will add the block to the
+  // graph, create a Goto at the end of the former block and will create an edge
+  // between the blocks. It will not, however, update the reverse post order or
+  // loop information.
   HBasicBlock* SplitBefore(HInstruction* cursor);
 
   // Split the block into two blocks just after `cursor`. Returns the newly
@@ -1946,11 +1947,18 @@
 // higher indices in no particular order.
 class HTryBoundary : public HTemplateInstruction<0> {
  public:
-  HTryBoundary(bool is_entry, bool is_exit)
-      : HTemplateInstruction(SideEffects::None()), is_entry_(is_entry), is_exit_(is_exit) {}
+  enum BoundaryKind {
+    kEntry,
+    kExit,
+  };
+
+  explicit HTryBoundary(BoundaryKind kind)
+      : HTemplateInstruction(SideEffects::None()), kind_(kind) {}
 
   bool IsControlFlow() const OVERRIDE { return true; }
 
+  bool CanThrow() const OVERRIDE { return true; }
+
   // Returns the block's non-exceptional successor (index zero).
   HBasicBlock* GetNormalFlowSuccessor() const { return GetBlock()->GetSuccessors().Get(0); }
 
@@ -1977,21 +1985,12 @@
     }
   }
 
-  bool IsTryEntry() const { return is_entry_; }
-  bool IsTryExit() const { return is_exit_; }
+  bool IsEntry() const { return kind_ == BoundaryKind::kEntry; }
 
   DECLARE_INSTRUCTION(TryBoundary);
 
  private:
-  // Only for debugging purposes.
-  bool is_entry_;
-  bool is_exit_;
-
-  // Only set by HGraphBuilder.
-  void SetIsTryEntry() { is_entry_ = true; }
-  void SetIsTryExit() { is_exit_ = true; }
-
-  friend HGraphBuilder;
+  const BoundaryKind kind_;
 
   DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
 };
diff --git a/test/510-checker-try-catch/smali/Builder.smali b/test/510-checker-try-catch/smali/Builder.smali
index f300b21..95708a2 100644
--- a/test/510-checker-try-catch/smali/Builder.smali
+++ b/test/510-checker-try-catch/smali/Builder.smali
@@ -68,25 +68,25 @@
 ## CHECK:  predecessors     "B0"
 ## CHECK:  successors       "<<BTry1>>"
 ## CHECK:  xhandlers        "<<BCatch1>>" "<<BCatch3>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry1>>"
 ## CHECK:  predecessors     "<<BTry1>>"
 ## CHECK:  successors       "<<BAdd>>"
 ## CHECK:  xhandlers        "<<BCatch1>>" "<<BCatch3>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 ## CHECK:  name             "<<BEnterTry2>>"
 ## CHECK:  predecessors     "<<BAdd>>"
 ## CHECK:  successors       "<<BTry2>>"
 ## CHECK:  xhandlers        "<<BCatch2>>" "<<BCatch3>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry2>>"
 ## CHECK:  predecessors     "<<BTry2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch2>>" "<<BCatch3>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testMultipleTryCatch(III)I
     .registers 3
@@ -164,19 +164,19 @@
 ## CHECK:  predecessors     "<<BThen>>"
 ## CHECK:  successors       "<<BTry1>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BEnterTry2>>"
 ## CHECK:  predecessors     "<<BIf>>"
 ## CHECK:  successors       "<<BTry2>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry>>"
 ## CHECK:  predecessors     "<<BTry2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testMultipleEntries(IIII)I
     .registers 4
@@ -237,19 +237,19 @@
 ## CHECK:  predecessors     "B0"
 ## CHECK:  successors       "<<BTry>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry1>>"
 ## CHECK:  predecessors     "<<BTry>>"
 ## CHECK:  successors       "<<BThen>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 ## CHECK:  name             "<<BExitTry2>>"
 ## CHECK:  predecessors     "<<BTry>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testMultipleExits(II)I
     .registers 2
@@ -284,11 +284,11 @@
 
 ## CHECK:  name             "<<BTry1:B\d+>>"
 ## CHECK:  predecessors     "<<BEnter1>>"
-## CHECK:  successors       "<<BExit1Enter2:B\d+>>"
+## CHECK:  successors       "<<BExit1:B\d+>>"
 ## CHECK:  Div
 
 ## CHECK:  name             "<<BTry2:B\d+>>"
-## CHECK:  predecessors     "<<BExit1Enter2>>"
+## CHECK:  predecessors     "<<BEnter2:B\d+>>"
 ## CHECK:  successors       "<<BExit2:B\d+>>"
 ## CHECK:  Div
 
@@ -297,13 +297,13 @@
 ## CHECK:  Return
 
 ## CHECK:  name             "<<BCatch1>>"
-## CHECK:  predecessors     "<<BEnter1>>" "<<BExit1Enter2>>"
+## CHECK:  predecessors     "<<BEnter1>>" "<<BExit1>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  flags            "catch_block"
 ## CHECK:  StoreLocal       [v0,<<Minus1>>]
 
 ## CHECK:  name             "<<BCatch2>>"
-## CHECK:  predecessors     "<<BExit1Enter2>>" "<<BExit2>>"
+## CHECK:  predecessors     "<<BEnter2>>" "<<BExit2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  flags            "catch_block"
 ## CHECK:  StoreLocal       [v0,<<Minus2>>]
@@ -312,19 +312,25 @@
 ## CHECK:  predecessors     "B0"
 ## CHECK:  successors       "<<BTry1>>"
 ## CHECK:  xhandlers        "<<BCatch1>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
-## CHECK:  name             "<<BExit1Enter2>>"
+## CHECK:  name             "<<BExit1>>"
 ## CHECK:  predecessors     "<<BTry1>>"
+## CHECK:  successors       "<<BEnter2>>"
+## CHECK:  xhandlers        "<<BCatch1>>"
+## CHECK:  TryBoundary      kind:exit
+
+## CHECK:  name             "<<BEnter2>>"
+## CHECK:  predecessors     "<<BExit1>>"
 ## CHECK:  successors       "<<BTry2>>"
-## CHECK:  xhandlers        "<<BCatch1>>" "<<BCatch2>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:true
+## CHECK:  xhandlers        "<<BCatch2>>"
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExit2>>"
 ## CHECK:  predecessors     "<<BTry2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch2>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testSharedBoundary(III)I
     .registers 3
@@ -365,13 +371,13 @@
 ## CHECK:  Goto
 
 ## CHECK:  name             "<<BTry1:B\d+>>"
-## CHECK:  predecessors     "<<BExit2Enter1:B\d+>>"
+## CHECK:  predecessors     "<<BEnter1:B\d+>>"
 ## CHECK:  successors       "<<BExit1:B\d+>>"
 ## CHECK:  Div
 
 ## CHECK:  name             "<<BTry2:B\d+>>"
 ## CHECK:  predecessors     "<<BEnter2>>"
-## CHECK:  successors       "<<BExit2Enter1>>"
+## CHECK:  successors       "<<BExit2:B\d+>>"
 ## CHECK:  Div
 
 ## CHECK:  name             "<<BReturn:B\d+>>"
@@ -379,34 +385,40 @@
 ## CHECK:  Return
 
 ## CHECK:  name             "<<BCatch1>>"
-## CHECK:  predecessors     "<<BExit2Enter1>>" "<<BExit1>>"
+## CHECK:  predecessors     "<<BEnter1>>" "<<BExit1>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  flags            "catch_block"
 ## CHECK:  StoreLocal       [v0,<<Minus1>>]
 
 ## CHECK:  name             "<<BCatch2>>"
-## CHECK:  predecessors     "<<BEnter2>>" "<<BExit2Enter1>>"
+## CHECK:  predecessors     "<<BEnter2>>" "<<BExit2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  flags            "catch_block"
 ## CHECK:  StoreLocal       [v0,<<Minus2>>]
 
-## CHECK:  name             "<<BExit2Enter1>>"
-## CHECK:  predecessors     "<<BTry2>>"
+## CHECK:  name             "<<BEnter1>>"
+## CHECK:  predecessors     "<<BExit2>>"
 ## CHECK:  successors       "<<BTry1>>"
-## CHECK:  xhandlers        "<<BCatch1>>" "<<BCatch2>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:true
+## CHECK:  xhandlers        "<<BCatch1>>"
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExit1>>"
 ## CHECK:  predecessors     "<<BTry1>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch1>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 ## CHECK:  name             "<<BEnter2>>"
 ## CHECK:  predecessors     "<<BGoto>>"
 ## CHECK:  successors       "<<BTry2>>"
 ## CHECK:  xhandlers        "<<BCatch2>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
+
+## CHECK:  name             "<<BExit2>>"
+## CHECK:  predecessors     "<<BTry2>>"
+## CHECK:  successors       "<<BEnter1>>"
+## CHECK:  xhandlers        "<<BCatch2>>"
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testSharedBoundary_Reverse(III)I
     .registers 3
@@ -448,16 +460,16 @@
 
 ## CHECK:  name             "<<BTry1:B\d+>>"
 ## CHECK:  predecessors     "<<BEnter1:B\d+>>"
-## CHECK:  successors       "<<BExit1Enter2:B\d+>>"
+## CHECK:  successors       "<<BExit1:B\d+>>"
 ## CHECK:  Div
 
 ## CHECK:  name             "<<BTry2:B\d+>>"
-## CHECK:  predecessors     "<<BExit1Enter2>>"
-## CHECK:  successors       "<<BExit2Enter3:B\d+>>"
+## CHECK:  predecessors     "<<BEnter2:B\d+>>"
+## CHECK:  successors       "<<BExit2:B\d+>>"
 ## CHECK:  Div
 
 ## CHECK:  name             "<<BTry3:B\d+>>"
-## CHECK:  predecessors     "<<BExit2Enter3>>"
+## CHECK:  predecessors     "<<BEnter3:B\d+>>"
 ## CHECK:  successors       "<<BExit3:B\d+>>"
 ## CHECK:  Div
 
@@ -465,13 +477,13 @@
 ## CHECK:  predecessors     "<<BExit3>>" "<<BCatchArith:B\d+>>" "<<BCatchAll:B\d+>>"
 
 ## CHECK:  name             "<<BCatchArith>>"
-## CHECK:  predecessors     "<<BExit1Enter2>>" "<<BExit2Enter3>>"
+## CHECK:  predecessors     "<<BEnter2>>" "<<BExit2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  flags            "catch_block"
 ## CHECK:  StoreLocal       [v0,<<Minus1>>]
 
 ## CHECK:  name             "<<BCatchAll>>"
-## CHECK:  predecessors     "<<BEnter1>>" "<<BExit1Enter2>>" "<<BExit2Enter3>>" "<<BExit3>>"
+## CHECK:  predecessors     "<<BEnter1>>" "<<BExit1>>" "<<BEnter2>>" "<<BExit2>>" "<<BEnter3>>" "<<BExit3>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  flags            "catch_block"
 ## CHECK:  StoreLocal       [v0,<<Minus2>>]
@@ -480,25 +492,37 @@
 ## CHECK:  predecessors     "B0"
 ## CHECK:  successors       "<<BTry1>>"
 ## CHECK:  xhandlers        "<<BCatchAll>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
-## CHECK:  name             "<<BExit1Enter2>>"
+## CHECK:  name             "<<BExit1>>"
 ## CHECK:  predecessors     "<<BTry1>>"
-## CHECK:  successors       "<<BTry2>>"
-## CHECK:  xhandlers        "<<BCatchAll>>" "<<BCatchArith>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:true
+## CHECK:  successors       "<<BEnter2>>"
+## CHECK:  xhandlers        "<<BCatchAll>>"
+## CHECK:  TryBoundary      kind:exit
 
-## CHECK:  name             "<<BExit2Enter3>>"
-## CHECK:  predecessors     "<<BTry2>>"
-## CHECK:  successors       "<<BTry3>>"
+## CHECK:  name             "<<BEnter2>>"
+## CHECK:  predecessors     "<<BExit1>>"
+## CHECK:  successors       "<<BTry2>>"
 ## CHECK:  xhandlers        "<<BCatchArith>>" "<<BCatchAll>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:true
+## CHECK:  TryBoundary      kind:entry
+
+## CHECK:  name             "<<BExit2>>"
+## CHECK:  predecessors     "<<BTry2>>"
+## CHECK:  successors       "<<BEnter3>>"
+## CHECK:  xhandlers        "<<BCatchArith>>" "<<BCatchAll>>"
+## CHECK:  TryBoundary      kind:exit
+
+## CHECK:  name             "<<BEnter3>>"
+## CHECK:  predecessors     "<<BExit2>>"
+## CHECK:  successors       "<<BTry3>>"
+## CHECK:  xhandlers        "<<BCatchAll>>"
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExit3>>"
 ## CHECK:  predecessors     "<<BTry3>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatchAll>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testNestedTry(IIII)I
     .registers 4
@@ -562,25 +586,25 @@
 ## CHECK:  predecessors     "B0"
 ## CHECK:  successors       "<<BTry1>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry1>>"
 ## CHECK:  predecessors     "<<BTry1>>"
 ## CHECK:  successors       "<<BOutside>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 ## CHECK:  name             "<<BEnterTry2>>"
 ## CHECK:  predecessors     "<<BOutside>>"
 ## CHECK:  successors       "<<BTry2>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry2>>"
 ## CHECK:  predecessors     "<<BTry2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testIncontinuousTry(IIII)I
     .registers 4
@@ -630,13 +654,13 @@
 ## CHECK:  predecessors     "B0"
 ## CHECK:  successors       "<<BTry>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry>>"
 ## CHECK:  predecessors     "<<BTry>>"
 ## CHECK:  successors       "<<BExit>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 ## CHECK:  name             "<<BExit>>"
 ## CHECK:  predecessors     "<<BExitTry>>" "<<BCatch>>"
@@ -660,28 +684,32 @@
 ## CHECK-START: int Builder.testCatchLoop(int, int, int) builder (after)
 
 ## CHECK:  name             "B0"
-## CHECK:  successors       "<<BEnterTry:B\d+>>"
+## CHECK:  successors       "<<BCatch:B\d+>>"
 
-## CHECK:  name             "<<BTry:B\d+>>"
-## CHECK:  predecessors     "<<BEnterTry>>" "<<BEnterTry>>" "<<BExitTry:B\d+>>"
-## CHECK:  successors       "<<BExitTry>>"
+## CHECK:  name             "<<BCatch>>"
+## CHECK:  predecessors     "B0" "<<BEnterTry:B\d+>>" "<<BExitTry:B\d+>>"
+## CHECK:  successors       "<<BEnterTry>>"
 ## CHECK:  flags            "catch_block"
-## CHECK:  Div
 
 ## CHECK:  name             "<<BReturn:B\d+>>"
 ## CHECK:  predecessors     "<<BExitTry>>"
 
+## CHECK:  name             "<<BTry:B\d+>>"
+## CHECK:  predecessors     "<<BEnterTry>>"
+## CHECK:  successors       "<<BExitTry>>"
+## CHECK:  Div
+
 ## CHECK:  name             "<<BEnterTry>>"
-## CHECK:  predecessors     "B0"
+## CHECK:  predecessors     "<<BCatch>>"
 ## CHECK:  successors       "<<BTry>>"
-## CHECK:  xhandlers        "<<BTry>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  xhandlers        "<<BCatch>>"
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExitTry>>"
 ## CHECK:  predecessors     "<<BTry>>"
 ## CHECK:  successors       "<<BReturn>>"
-## CHECK:  xhandlers        "<<BTry>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  xhandlers        "<<BCatch>>"
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testCatchLoop(III)I
     .registers 4
@@ -702,33 +730,49 @@
 ## CHECK-START: int Builder.testHandlerEdge1(int, int, int) builder (after)
 
 ## CHECK:  name             "B0"
-## CHECK:  successors       "<<BEnterTry:B\d+>>"
+## CHECK:  successors       "<<BEnterTry1:B\d+>>"
 
-## CHECK:  name             "<<BTry:B\d+>>"
-## CHECK:  predecessors     "<<BEnterTry>>"
-## CHECK:  successors       "<<BCatch:B\d+>>"
+## CHECK:  name             "<<BTry1:B\d+>>"
+## CHECK:  predecessors     "<<BEnterTry1>>"
+## CHECK:  successors       "<<BExitTry1:B\d+>>"
 ## CHECK:  Div
 
-## CHECK:  name             "<<BCatch>>"
-## CHECK:  predecessors     "<<BTry>>" "<<BEnterTry>>" "<<BExitTry:B\d+>>"
-## CHECK:  successors       "<<BExitTry>>"
+## CHECK:  name             "<<BCatch:B\d+>>"
+## CHECK:  predecessors     "<<BExitTry1>>" "<<BEnterTry1>>" "<<BExitTry1>>" "<<BEnterTry2:B\d+>>" "<<BExitTry2:B\d+>>"
+## CHECK:  successors       "<<BEnterTry2>>"
 ## CHECK:  flags            "catch_block"
-## CHECK:  Div
 
 ## CHECK:  name             "<<BReturn:B\d+>>"
-## CHECK:  predecessors     "<<BExitTry>>"
+## CHECK:  predecessors     "<<BExitTry2>>"
 
-## CHECK:  name             "<<BEnterTry>>"
+## CHECK:  name             "<<BEnterTry1>>"
 ## CHECK:  predecessors     "B0"
-## CHECK:  successors       "<<BTry>>"
+## CHECK:  successors       "<<BTry1>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  TryBoundary      kind:entry
 
-## CHECK:  name             "<<BExitTry>>"
+## CHECK:  name             "<<BExitTry1>>"
+## CHECK:  predecessors     "<<BTry1>>"
+## CHECK:  successors       "<<BCatch>>"
+## CHECK:  xhandlers        "<<BCatch>>"
+## CHECK:  TryBoundary      kind:exit
+
+## CHECK:  name             "<<BTry2:B\d+>>"
+## CHECK:  predecessors     "<<BEnterTry2>>"
+## CHECK:  successors       "<<BExitTry2>>"
+## CHECK:  Div
+
+## CHECK:  name             "<<BEnterTry2>>"
 ## CHECK:  predecessors     "<<BCatch>>"
+## CHECK:  successors       "<<BTry2>>"
+## CHECK:  xhandlers        "<<BCatch>>"
+## CHECK:  TryBoundary      kind:entry
+
+## CHECK:  name             "<<BExitTry2>>"
+## CHECK:  predecessors     "<<BTry2>>"
 ## CHECK:  successors       "<<BReturn>>"
 ## CHECK:  xhandlers        "<<BCatch>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testHandlerEdge1(III)I
     .registers 4
@@ -750,41 +794,55 @@
 ## CHECK-START: int Builder.testHandlerEdge2(int, int, int) builder (after)
 
 ## CHECK:  name             "B0"
+## CHECK:  successors       "<<BCatch1:B\d+>>"
+
+## CHECK:  name             "<<BCatch1>>"
+## CHECK:  predecessors     "B0" "<<BEnter2:B\d+>>" "<<BExit2:B\d+>>"
 ## CHECK:  successors       "<<BEnter1:B\d+>>"
-
-## CHECK:  name             "<<BTry1:B\d+>>"
-## CHECK:  predecessors     "<<BEnter1>>" "<<BExit1Enter2:B\d+>>" "<<BExit2:B\d+>>"
-## CHECK:  successors       "<<BExit1Enter2>>"
 ## CHECK:  flags            "catch_block"
-## CHECK:  Div
 
-## CHECK:  name             "<<BTry2:B\d+>>"
-## CHECK:  predecessors     "<<BExit1Enter2>>" "<<BEnter1>>" "<<BExit1Enter2>>"
-## CHECK:  successors       "<<BExit2>>"
+## CHECK:  name             "<<BCatch2:B\d+>>"
+## CHECK:  predecessors     "<<BExit1:B\d+>>" "<<BEnter1>>" "<<BExit1>>"
+## CHECK:  successors       "<<BEnter2>>"
 ## CHECK:  flags            "catch_block"
-## CHECK:  Div
 
 ## CHECK:  name             "<<BReturn:B\d+>>"
 ## CHECK:  predecessors     "<<BExit2>>"
 ## CHECK:  Return
 
-## CHECK:  name             "<<BEnter1>>"
-## CHECK:  predecessors     "B0"
-## CHECK:  successors       "<<BTry1>>"
-## CHECK:  xhandlers        "<<BTry2>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:false
+## CHECK:  name             "<<BTry1:B\d+>>"
+## CHECK:  predecessors     "<<BEnter1>>"
+## CHECK:  successors       "<<BExit1>>"
+## CHECK:  Div
 
-## CHECK:  name             "<<BExit1Enter2>>"
+## CHECK:  name             "<<BEnter1>>"
+## CHECK:  predecessors     "<<BCatch1>>"
+## CHECK:  successors       "<<BTry1>>"
+## CHECK:  xhandlers        "<<BCatch2>>"
+## CHECK:  TryBoundary      kind:entry
+
+## CHECK:  name             "<<BExit1>>"
 ## CHECK:  predecessors     "<<BTry1>>"
+## CHECK:  successors       "<<BCatch2>>"
+## CHECK:  xhandlers        "<<BCatch2>>"
+## CHECK:  TryBoundary      kind:exit
+
+## CHECK:  name             "<<BTry2:B\d+>>"
+## CHECK:  predecessors     "<<BEnter2>>"
+## CHECK:  successors       "<<BExit2>>"
+## CHECK:  Div
+
+## CHECK:  name             "<<BEnter2>>"
+## CHECK:  predecessors     "<<BCatch2>>"
 ## CHECK:  successors       "<<BTry2>>"
-## CHECK:  xhandlers        "<<BTry2>>" "<<BTry1>>"
-## CHECK:  TryBoundary      is_entry:true is_exit:true
+## CHECK:  xhandlers        "<<BCatch1>>"
+## CHECK:  TryBoundary      kind:entry
 
 ## CHECK:  name             "<<BExit2>>"
 ## CHECK:  predecessors     "<<BTry2>>"
 ## CHECK:  successors       "<<BReturn>>"
-## CHECK:  xhandlers        "<<BTry1>>"
-## CHECK:  TryBoundary      is_entry:false is_exit:true
+## CHECK:  xhandlers        "<<BCatch1>>"
+## CHECK:  TryBoundary      kind:exit
 
 .method public static testHandlerEdge2(III)I
     .registers 4