diff options
-rw-r--r-- | compiler/optimizing/inliner.cc | 10 | ||||
-rw-r--r-- | compiler/optimizing/nodes.cc | 40 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler_stats.h | 1 | ||||
-rw-r--r-- | test/2250-inline-throw-into-try/expected-stderr.txt | 0 | ||||
-rw-r--r-- | test/2250-inline-throw-into-try/expected-stdout.txt | 0 | ||||
-rw-r--r-- | test/2250-inline-throw-into-try/info.txt | 1 | ||||
-rw-r--r-- | test/2250-inline-throw-into-try/src/Main.java | 64 |
7 files changed, 97 insertions, 19 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3e3b2d46ca..1f7ef2cf4c 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -1883,7 +1883,6 @@ bool HInliner::CanInlineBody(const HGraph* callee_graph, HInvoke* invoke, size_t* out_number_of_instructions, bool is_speculative) const { - const HBasicBlock* target_block = invoke->GetBlock(); ArtMethod* const resolved_method = callee_graph->GetArtMethod(); HBasicBlock* exit_block = callee_graph->GetExitBlock(); @@ -1905,14 +1904,7 @@ bool HInliner::CanInlineBody(const HGraph* callee_graph, } if (last_instruction->IsThrow()) { - if (target_block->IsTryBlock()) { - // TODO(ngeoffray): Support adding HTryBoundary in Hgraph::InlineInto. - LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedTryCatchCaller) - << "Method " << resolved_method->PrettyMethod() - << " could not be inlined because one branch always throws and" - << " caller is in a try/catch block"; - return false; - } else if (graph_->GetExitBlock() == nullptr) { + if (graph_->GetExitBlock() == nullptr) { // TODO(ngeoffray): Support adding HExit in the caller graph. LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedInfiniteLoop) << "Method " << resolved_method->PrettyMethod() diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 270bb4fb86..85d0fe7245 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2919,23 +2919,45 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { DCHECK_IMPLIES(saw_goto, last->IsReturnVoid() || last->IsReturn()); if (last->IsThrow()) { - DCHECK(!at->IsTryBlock()); // 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); - // We either have `Throw->TryBoundary` or `Throw`. We want to point the whole chain to the - // exit, so we recompute `predecessor` - predecessor = to->GetPredecessors()[pred]; - predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock()); + 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. + HBasicBlock* new_block = outer_graph->SplitEdge(predecessor, to); + new_block->AddInstruction( + new (allocator) HTryBoundary(HTryBoundary::BoundaryKind::kExit, last->GetDexPc())); + new_block->ReplaceSuccessor(to, outer_graph->GetExitBlock()); + + // Copy information from the predecessor. + new_block->SetLoopInformation(predecessor->GetLoopInformation()); + TryCatchInformation* try_catch_info = predecessor->GetTryCatchInformation(); + new_block->SetTryCatchInformation(try_catch_info); + for (HBasicBlock* xhandler : + try_catch_info->GetTryEntry().GetBlock()->GetExceptionalSuccessors()) { + new_block->AddSuccessor(xhandler); + } + DCHECK(try_catch_info->GetTryEntry().HasSameExceptionHandlersAs( + *new_block->GetLastInstruction()->AsTryBoundary())); + } else { + // We either have `Throw->TryBoundary` or `Throw`. We want to point the whole chain to the + // exit, so we recompute `predecessor` + predecessor = to->GetPredecessors()[pred]; + predecessor->ReplaceSuccessor(to, outer_graph->GetExitBlock()); + } + --pred; // We need to re-run dominance information, as the exit block now has - // a new dominator. + // a new predecessor and potential new dominator. + // TODO(solanes): See if it's worth it to hand-modify the domination chain instead of + // rerunning the dominance for the whole graph. rerun_dominance = true; if (predecessor->GetLoopInformation() != nullptr) { - // The exit block and blocks post dominated by the exit block do not belong - // to any loop. Because we do not compute the post dominators, we need to re-run - // loop analysis to get the loop information correct. + // The loop information might have changed e.g. `predecessor` might not be in a loop + // anymore. We only do this if `predecessor` has loop information as it is impossible for + // predecessor to end up in a loop if it wasn't in one before. rerun_loop_analysis = true; } } else { diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 02f1fe9d0d..c741b02751 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -95,7 +95,6 @@ enum class MethodCompilationStat { kNotInlinedIrreducibleLoop, kNotInlinedAlwaysThrows, kNotInlinedInfiniteLoop, - kNotInlinedTryCatchCaller, kNotInlinedTryCatchCallee, kNotInlinedTryCatchDisabled, kNotInlinedRegisterAllocator, diff --git a/test/2250-inline-throw-into-try/expected-stderr.txt b/test/2250-inline-throw-into-try/expected-stderr.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/2250-inline-throw-into-try/expected-stderr.txt diff --git a/test/2250-inline-throw-into-try/expected-stdout.txt b/test/2250-inline-throw-into-try/expected-stdout.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/2250-inline-throw-into-try/expected-stdout.txt diff --git a/test/2250-inline-throw-into-try/info.txt b/test/2250-inline-throw-into-try/info.txt new file mode 100644 index 0000000000..4d4cab56bd --- /dev/null +++ b/test/2250-inline-throw-into-try/info.txt @@ -0,0 +1 @@ +Tests that we inline methods that end with a throw, inside of try blocks. diff --git a/test/2250-inline-throw-into-try/src/Main.java b/test/2250-inline-throw-into-try/src/Main.java new file mode 100644 index 0000000000..d562279fc9 --- /dev/null +++ b/test/2250-inline-throw-into-try/src/Main.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +public class Main { + public static void main(String[] args) throws Exception { + // Inline methods that sometimes throw. + $noinline$assertEquals(-1000, $noinline$testThrowsWithZero(0)); + $noinline$assertEquals(1, $noinline$testThrowsWithZero(1)); + + // Tests that we can correctly inline even when the throw is not caught. + try { + $noinline$testThrowNotCaught(0); + unreachable(); + } catch (Error expected) { + } + } + + public static void $noinline$assertEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + private static int $noinline$testThrowsWithZero(int value) { + try { + return $inline$throwsWithZeroOrReturns(value); + } catch (Error e) { + return -1000; + } + } + + private static int $inline$throwsWithZeroOrReturns(int value) { + if (value == 0) { + throw new Error("Zero!"); + } else { + return value; + } + } + + private static int $noinline$testThrowNotCaught(int value) { + try { + return $inline$throwsWithZeroOrReturns(value); + } catch (Exception e) { + return -1000; + } + } + + private static void unreachable() throws Exception{ + throw new Exception("Unreachable"); + } +} |