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