Mark graphs as always throwing alongside methods
This CL removes the need of using an extra boolean
(`did_set_always_throws`) as we already encode the information in
HasAlwaysThrowingInvokes after aosp/2153582.
Also, if we know that a method always throws in the particular
instance that it is called, we can mark it as such. This is an
improvement versus using the dex instructions as methods like:
int foo(int a) {
if (a == 0) { throw new Error("a is 0!"); }
return a / a;
}
will be marked as always throws if called as foo(0).
Test: art/test/testrunner/testrunner.py --host --64 --optimizing -b
Change-Id: I5368878d0023775c53028a5cccd4a1111b50f60e
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index d8ace5e..3a2f7f2 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -141,7 +141,11 @@
}
bool did_inline = false;
- bool did_set_always_throws = false;
+ // The inliner is the only phase that sets invokes as `always throwing`, and since we only run the
+ // inliner once per graph this value should always be false at the beginning of the inlining
+ // phase. This is important since we use `HasAlwaysThrowingInvokes` to know whether the inliner
+ // phase performed a relevant change in the graph.
+ DCHECK(!graph_->HasAlwaysThrowingInvokes());
// Initialize the number of instructions for the method being compiled. Recursive calls
// to HInliner::Run have already updated the instruction count.
@@ -182,7 +186,7 @@
call->GetMethodReference().PrettyMethod(/* with_signature= */ false);
// Tests prevent inlining by having $noinline$ in their method names.
if (callee_name.find("$noinline$") == std::string::npos) {
- if (TryInline(call, &did_set_always_throws)) {
+ if (TryInline(call)) {
did_inline = true;
} else if (honor_inline_directives) {
bool should_have_inlined = (callee_name.find("$inline$") != std::string::npos);
@@ -192,7 +196,7 @@
} else {
DCHECK(!honor_inline_directives);
// Normal case: try to inline.
- if (TryInline(call, &did_set_always_throws)) {
+ if (TryInline(call)) {
did_inline = true;
}
}
@@ -201,11 +205,9 @@
}
}
- if (did_set_always_throws) {
- graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
- }
-
- return did_inline || did_set_always_throws;
+ // We return true if we either inlined at least one method, or we marked one of our methods as
+ // always throwing.
+ return did_inline || graph_->HasAlwaysThrowingInvokes();
}
static bool IsMethodOrDeclaringClassFinal(ArtMethod* method)
@@ -440,7 +442,7 @@
return throw_seen;
}
-bool HInliner::TryInline(HInvoke* invoke_instruction, /*inout*/ bool* did_set_always_throws) {
+bool HInliner::TryInline(HInvoke* invoke_instruction) {
MaybeRecordStat(stats_, MethodCompilationStat::kTryInline);
// Don't bother to move further if we know the method is unresolved or the invocation is
@@ -491,11 +493,10 @@
} else {
invoke_to_analyze = invoke_instruction;
}
- // Set always throws property for non-inlined method call with single
- // target.
+ // Set always throws property for non-inlined method call with single target.
if (AlwaysThrows(actual_method)) {
- invoke_to_analyze->SetAlwaysThrows(true);
- *did_set_always_throws = true;
+ invoke_to_analyze->SetAlwaysThrows(/* always_throws= */ true);
+ graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
}
}
return result;
@@ -1819,8 +1820,9 @@
// If this function returns true, it will also set out_number_of_instructions to
// the number of instructions in the inlined body.
bool HInliner::CanInlineBody(const HGraph* callee_graph,
- const HBasicBlock* target_block,
+ HInvoke* invoke,
size_t* out_number_of_instructions) const {
+ const HBasicBlock* target_block = invoke->GetBlock();
ArtMethod* const resolved_method = callee_graph->GetArtMethod();
HBasicBlock* exit_block = callee_graph->GetExitBlock();
@@ -1862,6 +1864,11 @@
}
if (!has_one_return) {
+ // If we know that the method always throws with the particular parameters, set it as such. This
+ // is better than using the dex instructions as we have more information about this particular
+ // call.
+ invoke->SetAlwaysThrows(/* always_throws= */ true);
+ graph_->SetHasAlwaysThrowingInvokes(/* value= */ true);
LOG_FAIL(stats_, MethodCompilationStat::kNotInlinedAlwaysThrows)
<< "Method " << resolved_method->PrettyMethod()
<< " could not be inlined because it always throws";
@@ -2058,7 +2065,7 @@
RunOptimizations(callee_graph, code_item, dex_compilation_unit);
size_t number_of_instructions = 0;
- if (!CanInlineBody(callee_graph, invoke_instruction->GetBlock(), &number_of_instructions)) {
+ if (!CanInlineBody(callee_graph, invoke_instruction, &number_of_instructions)) {
return false;
}