From 56e1accf3966ae92e151567abf4561ef3f6466f4 Mon Sep 17 00:00:00 2001 From: David Brazdil Date: Tue, 30 Jun 2015 15:41:36 +0100 Subject: [PATCH] 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 --- compiler/optimizing/builder.cc | 127 ++++++------- compiler/optimizing/builder.h | 6 + compiler/optimizing/graph_visualizer.cc | 7 +- compiler/optimizing/nodes.cc | 1 + compiler/optimizing/nodes.h | 31 ++-- test/510-checker-try-catch/smali/Builder.smali | 238 +++++++++++++++---------- 6 files changed, 238 insertions(+), 172 deletions(-) diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index bde2c70fc..883c983fe 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -306,6 +306,22 @@ void HGraphBuilder::CreateBlocksForTryCatch(const DexFile::CodeItem& code_item) } } +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 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) 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 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // 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 58d85e9ef..9744a5568 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -97,9 +97,15 @@ class HGraphBuilder : public ValueObject { 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 c41574c93..5cb97724a 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -380,12 +380,7 @@ class HGraphVisualizerPrinter : public HGraphVisitor { } 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 881f9ec11..b82e37cb4 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1050,6 +1050,7 @@ HBasicBlock* HBasicBlock::SplitBefore(HInstruction* cursor) { 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 95ea9662e..5c102453f 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -729,9 +729,10 @@ class HBasicBlock : public ArenaObject { 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 @@ class HIf : public HTemplateInstruction<1> { // 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 @@ class HTryBoundary : public HTemplateInstruction<0> { } } - 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 f300b2108..95708a2c8 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 "<>" ## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" -## 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 "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## 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 "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## 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 "<>" ## CHECK: predecessors "<>" -## CHECK: successors "<>" +## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" -## CHECK: predecessors "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div @@ -297,13 +297,13 @@ ## CHECK: Return ## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" +## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<>] ## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" +## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<>] @@ -312,19 +312,25 @@ ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry -## CHECK: name "<>" +## CHECK: name "<>" ## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:true +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## 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 "<>" -## CHECK: predecessors "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" -## CHECK: successors "<>" +## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" @@ -379,34 +385,40 @@ ## CHECK: Return ## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" +## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<>] ## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" +## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<>] -## CHECK: name "<>" -## CHECK: predecessors "<>" +## CHECK: name "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:true +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry + +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:exit .method public static testSharedBoundary_Reverse(III)I .registers 3 @@ -448,16 +460,16 @@ ## CHECK: name "<>" ## CHECK: predecessors "<>" -## CHECK: successors "<>" +## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" -## CHECK: predecessors "<>" -## CHECK: successors "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" ## CHECK: Div ## CHECK: name "<>" -## CHECK: predecessors "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: Div @@ -465,13 +477,13 @@ ## CHECK: predecessors "<>" "<>" "<>" ## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" +## CHECK: predecessors "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<>] ## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" "<>" "<>" +## CHECK: predecessors "<>" "<>" "<>" "<>" "<>" "<>" ## CHECK: successors "<>" ## CHECK: flags "catch_block" ## CHECK: StoreLocal [v0,<>] @@ -480,25 +492,37 @@ ## CHECK: predecessors "B0" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry -## CHECK: name "<>" +## CHECK: name "<>" ## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:true +## CHECK: xhandlers "<>" "<>" +## CHECK: TryBoundary kind:entry -## CHECK: name "<>" +## CHECK: name "<>" ## CHECK: predecessors "<>" -## CHECK: successors "<>" +## CHECK: successors "<>" ## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:true +## CHECK: TryBoundary kind:exit + +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## 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 "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## 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 "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: TryBoundary kind:exit ## CHECK: name "<>" ## CHECK: predecessors "<>" "<>" @@ -660,28 +684,32 @@ ## CHECK-START: int Builder.testCatchLoop(int, int, int) builder (after) ## CHECK: name "B0" -## CHECK: successors "<>" +## CHECK: successors "<>" -## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" "<>" -## CHECK: successors "<>" +## CHECK: name "<>" +## CHECK: predecessors "B0" "<>" "<>" +## CHECK: successors "<>" ## CHECK: flags "catch_block" -## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: Div + ## CHECK: name "<>" -## CHECK: predecessors "B0" +## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: xhandlers "<>" +## 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 "<>" +## CHECK: successors "<>" -## CHECK: name "<>" -## CHECK: predecessors "<>" -## CHECK: successors "<>" +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" ## CHECK: Div -## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" "<>" -## CHECK: successors "<>" +## CHECK: name "<>" +## CHECK: predecessors "<>" "<>" "<>" "<>" "<>" +## CHECK: successors "<>" ## CHECK: flags "catch_block" -## CHECK: Div ## CHECK: name "<>" -## CHECK: predecessors "<>" +## CHECK: predecessors "<>" -## CHECK: name "<>" +## CHECK: name "<>" ## CHECK: predecessors "B0" -## CHECK: successors "<>" +## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: TryBoundary kind:entry -## CHECK: name "<>" +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: Div + +## CHECK: name "<>" ## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry + +## CHECK: name "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" ## CHECK: xhandlers "<>" -## 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 "<>" +## CHECK: successors "<>" -## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" "<>" -## CHECK: successors "<>" +## CHECK: name "<>" +## CHECK: predecessors "B0" "<>" "<>" +## CHECK: successors "<>" ## CHECK: flags "catch_block" -## CHECK: Div -## CHECK: name "<>" -## CHECK: predecessors "<>" "<>" "<>" -## CHECK: successors "<>" +## CHECK: name "<>" +## CHECK: predecessors "<>" "<>" "<>" +## CHECK: successors "<>" ## CHECK: flags "catch_block" -## CHECK: Div ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: Return +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: Div + ## CHECK: name "<>" -## CHECK: predecessors "B0" +## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:true is_exit:false +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry -## CHECK: name "<>" +## CHECK: name "<>" ## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:exit + +## CHECK: name "<>" +## CHECK: predecessors "<>" +## CHECK: successors "<>" +## CHECK: Div + +## CHECK: name "<>" +## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" "<>" -## CHECK: TryBoundary is_entry:true is_exit:true +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:entry ## CHECK: name "<>" ## CHECK: predecessors "<>" ## CHECK: successors "<>" -## CHECK: xhandlers "<>" -## CHECK: TryBoundary is_entry:false is_exit:true +## CHECK: xhandlers "<>" +## CHECK: TryBoundary kind:exit .method public static testHandlerEdge2(III)I .registers 4 -- 2.11.0