diff options
author | 2016-08-02 11:02:54 -0700 | |
---|---|---|
committer | 2016-12-01 11:15:47 -0800 | |
commit | 063fc772b5b8aed7d769cd7cccb6ddc7619326ee (patch) | |
tree | bc165781989087a998721991504e589a7d5b0926 /compiler/optimizing/inliner.cc | |
parent | 48d08a4233ee4450b0d5073d41445f9dd1f17191 (diff) |
Class Hierarchy Analysis (CHA)
The class linker now tracks whether a method has a single implementation
and if so, the JIT compiler will try to devirtualize a virtual call for
the method into a direct call. If the single-implementation assumption
is violated due to additional class linking, compiled code that makes the
assumption is invalidated. Deoptimization is triggered for compiled code
live on stack. Instead of patching return pc's on stack, a CHA guard is
added which checks a hidden should_deoptimize flag for deoptimization.
This approach limits the number of deoptimization points.
This CL does not devirtualize abstract/interface method invocation.
Slides on CHA:
https://docs.google.com/a/google.com/presentation/d/1Ax6cabP1vM44aLOaJU3B26n5fTE9w5YU-1CRevIDsBc/edit?usp=sharing
Change-Id: I18bf716a601b6413b46312e925a6ad9e4008efa4
Test: ART_TEST_JIT=true m test-art-host/target-run-test test-art-host-gtest
Diffstat (limited to 'compiler/optimizing/inliner.cc')
-rw-r--r-- | compiler/optimizing/inliner.cc | 73 |
1 files changed, 69 insertions, 4 deletions
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 01e89bb304..8d93867230 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -292,6 +292,21 @@ static bool IsPolymorphic(Handle<mirror::ObjectArray<mirror::Class>> classes) classes->Get(InlineCache::kIndividualCacheSize - 1) == nullptr; } +ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { + if (!resolved_method->HasSingleImplementation()) { + return nullptr; + } + if (Runtime::Current()->IsAotCompiler()) { + // No CHA-based devirtulization for AOT compiler (yet). + return nullptr; + } + if (outermost_graph_->IsCompilingOsr()) { + // We do not support HDeoptimize in OSR methods. + return nullptr; + } + return resolved_method->GetSingleImplementation(); +} + bool HInliner::TryInline(HInvoke* invoke_instruction) { if (invoke_instruction->IsInvokeUnresolved()) { return false; // Don't bother to move further if we know the method is unresolved. @@ -317,10 +332,29 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { actual_method = FindVirtualOrInterfaceTarget(invoke_instruction, resolved_method); } + bool cha_devirtualize = false; + if (actual_method == nullptr) { + ArtMethod* method = TryCHADevirtualization(resolved_method); + if (method != nullptr) { + cha_devirtualize = true; + actual_method = method; + } + } + if (actual_method != nullptr) { - bool result = TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true); + bool result = TryInlineAndReplace(invoke_instruction, + actual_method, + /* do_rtp */ true, + cha_devirtualize); if (result && !invoke_instruction->IsInvokeStaticOrDirect()) { - MaybeRecordStat(kInlinedInvokeVirtualOrInterface); + if (cha_devirtualize) { + // Add dependency due to devirtulization. We've assumed resolved_method + // has single implementation. + outermost_graph_->AddCHASingleImplementationDependency(resolved_method); + MaybeRecordStat(kCHAInline); + } else { + MaybeRecordStat(kInlinedInvokeVirtualOrInterface); + } } return result; } @@ -438,7 +472,10 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, HInstruction* cursor = invoke_instruction->GetPrevious(); HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); - if (!TryInlineAndReplace(invoke_instruction, resolved_method, /* do_rtp */ false)) { + if (!TryInlineAndReplace(invoke_instruction, + resolved_method, + /* do_rtp */ false, + /* cha_devirtualize */ false)) { return false; } @@ -465,6 +502,25 @@ bool HInliner::TryInlineMonomorphicCall(HInvoke* invoke_instruction, return true; } +void HInliner::AddCHAGuard(HInstruction* invoke_instruction, + uint32_t dex_pc, + HInstruction* cursor, + HBasicBlock* bb_cursor) { + HInstruction* deopt_flag = new (graph_->GetArena()) HShouldDeoptimizeFlag(dex_pc); + HInstruction* should_deopt = new (graph_->GetArena()) HNotEqual( + deopt_flag, graph_->GetIntConstant(0, dex_pc)); + HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(should_deopt, dex_pc); + + if (cursor != nullptr) { + bb_cursor->InsertInstructionAfter(deopt_flag, cursor); + } else { + bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction()); + } + bb_cursor->InsertInstructionAfter(should_deopt, deopt_flag); + bb_cursor->InsertInstructionAfter(deopt, should_deopt); + deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment()); +} + HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, HInstruction* cursor, HBasicBlock* bb_cursor, @@ -787,8 +843,14 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget( return true; } -bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* method, bool do_rtp) { +bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, + ArtMethod* method, + bool do_rtp, + bool cha_devirtualize) { HInstruction* return_replacement = nullptr; + uint32_t dex_pc = invoke_instruction->GetDexPc(); + HInstruction* cursor = invoke_instruction->GetPrevious(); + HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); if (!TryBuildAndInline(invoke_instruction, method, &return_replacement)) { if (invoke_instruction->IsInvokeInterface()) { // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always @@ -826,6 +888,9 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, ArtMethod* metho return false; } } + if (cha_devirtualize) { + AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor); + } if (return_replacement != nullptr) { invoke_instruction->ReplaceWith(return_replacement); } |