Revert "Refactor code in inliner."
This reverts commit 921525030301fd4b8f6bb83aa6b20160d802e689.
Reason for revert: Potential cause of b/186637642, b/186637642
Change-Id: Ia6f5cb742a068f148e290071cb0675648b22ec86
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index a86fffd..312b8ed 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -342,7 +342,7 @@
return classes.GetReference(0)->AsClass();
}
-ArtMethod* HInliner::FindMethodFromCHA(ArtMethod* resolved_method) {
+ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) {
if (!resolved_method->HasSingleImplementation()) {
return nullptr;
}
@@ -432,6 +432,27 @@
return throw_seen;
}
+ArtMethod* HInliner::FindActualCallTarget(HInvoke* invoke_instruction, bool* cha_devirtualize) {
+ ArtMethod* actual_method = nullptr;
+ if (invoke_instruction->IsInvokeStaticOrDirect()) {
+ actual_method = invoke_instruction->GetResolvedMethod();
+ } else {
+ // Check if we can statically find the method.
+ actual_method = FindVirtualOrInterfaceTarget(invoke_instruction);
+ }
+
+ if (actual_method == nullptr) {
+ ArtMethod* method = TryCHADevirtualization(invoke_instruction->GetResolvedMethod());
+ if (method != nullptr) {
+ *cha_devirtualize = true;
+ actual_method = method;
+ LOG_NOTE() << "Try CHA-based inlining of " << actual_method->PrettyMethod();
+ }
+ }
+
+ return actual_method;
+}
+
bool HInliner::TryInline(HInvoke* invoke_instruction) {
MaybeRecordStat(stats_, MethodCompilationStat::kTryInline);
@@ -459,66 +480,40 @@
return false;
}
- ArtMethod* actual_method = invoke_instruction->IsInvokeStaticOrDirect()
- ? invoke_instruction->GetResolvedMethod()
- : FindVirtualOrInterfaceTarget(invoke_instruction);
+ bool cha_devirtualize = false;
+ ArtMethod* actual_method = FindActualCallTarget(invoke_instruction, &cha_devirtualize);
- if (actual_method != nullptr) {
- // Single target.
- bool result = TryInlineAndReplace(invoke_instruction,
- actual_method,
- ReferenceTypeInfo::CreateInvalid(),
- /* do_rtp= */ true);
- if (result) {
- MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
- } else {
- HInvoke* invoke_to_analyze = nullptr;
- if (TryDevirtualize(invoke_instruction, actual_method, &invoke_to_analyze)) {
- // Consider devirtualization as inlining.
- result = true;
- MaybeRecordStat(stats_, MethodCompilationStat::kDevirtualized);
+ // If we didn't find a method, see if we can inline from the inline caches.
+ if (actual_method == nullptr) {
+ DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
+ return TryInlineFromInlineCache(invoke_instruction);
+ }
+
+ // Single target.
+ bool result = TryInlineAndReplace(invoke_instruction,
+ actual_method,
+ ReferenceTypeInfo::CreateInvalid(),
+ /* do_rtp= */ true,
+ cha_devirtualize);
+ if (result) {
+ // Successfully inlined.
+ if (!invoke_instruction->IsInvokeStaticOrDirect()) {
+ if (cha_devirtualize) {
+ // Add dependency due to devirtualization. We've assumed resolved_method
+ // has single implementation.
+ outermost_graph_->AddCHASingleImplementationDependency(resolved_method);
+ MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline);
} else {
- invoke_to_analyze = invoke_instruction;
- }
- // Set always throws property for non-inlined method call with single
- // target.
- if (AlwaysThrows(codegen_->GetCompilerOptions(), actual_method)) {
- invoke_to_analyze->SetAlwaysThrows(true);
+ MaybeRecordStat(stats_, MethodCompilationStat::kInlinedInvokeVirtualOrInterface);
}
}
- return result;
+ } else if (!cha_devirtualize && AlwaysThrows(codegen_->GetCompilerOptions(), actual_method)) {
+ // Set always throws property for non-inlined method call with single target
+ // (unless it was obtained through CHA, because that would imply we have
+ // to add the CHA dependency, which seems not worth it).
+ invoke_instruction->SetAlwaysThrows(true);
}
-
- DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
-
- if (TryInlineFromCHA(invoke_instruction)) {
- return true;
- }
- return TryInlineFromInlineCache(invoke_instruction);
-}
-
-bool HInliner::TryInlineFromCHA(HInvoke* invoke_instruction) {
- ArtMethod* method = FindMethodFromCHA(invoke_instruction->GetResolvedMethod());
- if (method == nullptr) {
- return false;
- }
- LOG_NOTE() << "Try CHA-based inlining of " << method->PrettyMethod();
-
- uint32_t dex_pc = invoke_instruction->GetDexPc();
- HInstruction* cursor = invoke_instruction->GetPrevious();
- HBasicBlock* bb_cursor = invoke_instruction->GetBlock();
- if (!TryInlineAndReplace(invoke_instruction,
- method,
- ReferenceTypeInfo::CreateInvalid(),
- /* do_rtp= */ true)) {
- return false;
- }
- AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor);
- // Add dependency due to devirtualization. We've assumed resolved_method
- // has single implementation.
- outermost_graph_->AddCHASingleImplementationDependency(method);
- MaybeRecordStat(stats_, MethodCompilationStat::kCHAInline);
- return true;
+ return result;
}
bool HInliner::UseOnlyPolymorphicInliningWithNoDeopt() {
@@ -808,7 +803,8 @@
if (!TryInlineAndReplace(invoke_instruction,
resolved_method,
ReferenceTypeInfo::Create(monomorphic_type, /* is_exact= */ true),
- /* do_rtp= */ false)) {
+ /* do_rtp= */ false,
+ /* cha_devirtualize= */ false)) {
return false;
}
@@ -1225,9 +1221,85 @@
return true;
}
-void HInliner::MaybeRunReferenceTypePropagation(HInstruction* replacement,
- HInvoke* invoke_instruction) {
- if (ReturnTypeMoreSpecific(replacement, invoke_instruction)) {
+bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
+ ArtMethod* method,
+ ReferenceTypeInfo receiver_type,
+ bool do_rtp,
+ bool cha_devirtualize) {
+ DCHECK(!invoke_instruction->IsIntrinsic());
+ 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, receiver_type, &return_replacement)) {
+ if (invoke_instruction->IsInvokeInterface()) {
+ DCHECK(!method->IsProxyMethod());
+ // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always
+ // better than an invoke-interface because:
+ // 1) In the best case, the interface call has one more indirection (to fetch the IMT).
+ // 2) We will not go to the conflict trampoline with an invoke-virtual.
+ // TODO: Consider sharpening once it is not dependent on the compiler driver.
+
+ if (method->IsDefault() && !method->IsCopied()) {
+ // Changing to invoke-virtual cannot be done on an original default method
+ // since it's not in any vtable. Devirtualization by exact type/inline-cache
+ // always uses a method in the iftable which is never an original default
+ // method.
+ // On the other hand, inlining an original default method by CHA is fine.
+ DCHECK(cha_devirtualize);
+ return false;
+ }
+
+ if (kIsDebugBuild && method->IsDefaultConflicting()) {
+ CHECK(!cha_devirtualize) << "CHA cannot have a default conflict method as target";
+ // Devirtualization by exact type/inline-cache always uses a method in the vtable,
+ // so it's OK to change this invoke into a HInvokeVirtual.
+ ObjPtr<mirror::Class> receiver_class = receiver_type.GetTypeHandle().Get();
+ CHECK(!receiver_class->IsInterface());
+ PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
+ CHECK(method == receiver_class->GetVTableEntry(method->GetMethodIndex(), pointer_size));
+ }
+
+ uint32_t dex_method_index = FindMethodIndexIn(
+ method,
+ *invoke_instruction->GetMethodReference().dex_file,
+ invoke_instruction->GetMethodReference().index);
+ if (dex_method_index == dex::kDexNoIndex) {
+ return false;
+ }
+ HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual(
+ graph_->GetAllocator(),
+ invoke_instruction->GetNumberOfArguments(),
+ invoke_instruction->GetType(),
+ invoke_instruction->GetDexPc(),
+ MethodReference(invoke_instruction->GetMethodReference().dex_file, dex_method_index),
+ method,
+ MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
+ method->GetMethodIndex());
+ HInputsRef inputs = invoke_instruction->GetInputs();
+ for (size_t index = 0; index != inputs.size(); ++index) {
+ new_invoke->SetArgumentAt(index, inputs[index]);
+ }
+ invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction);
+ new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ if (invoke_instruction->GetType() == DataType::Type::kReference) {
+ new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo());
+ }
+ return_replacement = new_invoke;
+ } else {
+ // TODO: Consider sharpening an invoke virtual once it is not dependent on the
+ // compiler driver.
+ return false;
+ }
+ }
+
+ if (cha_devirtualize) {
+ AddCHAGuard(invoke_instruction, dex_pc, cursor, bb_cursor);
+ }
+ MaybeReplaceAndRemove(return_replacement, invoke_instruction);
+ FixUpReturnReferenceType(method, return_replacement);
+ if (do_rtp && ReturnTypeMoreSpecific(invoke_instruction, return_replacement)) {
// Actual return value has a more specific type than the method's declared
// return type. Run RTP again on the outer graph to propagate it.
ReferenceTypePropagation(graph_,
@@ -1235,88 +1307,6 @@
outer_compilation_unit_.GetDexCache(),
/* is_first_run= */ false).Run();
}
-}
-
-bool HInliner::TryDevirtualize(HInvoke* invoke_instruction,
- ArtMethod* method,
- HInvoke** replacement) {
- DCHECK(!method->IsProxyMethod());
- DCHECK(invoke_instruction != *replacement);
- if (!invoke_instruction->IsInvokeInterface()) {
- // TODO: Consider sharpening an invoke virtual once it is not dependent on the
- // compiler driver.
- return false;
- }
- // Devirtualization by exact type uses a method in the vtable, so we should
- // not see a default non-copied method.
- DCHECK(!method->IsDefault() || method->IsCopied());
- // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always
- // better than an invoke-interface because:
- // 1) In the best case, the interface call has one more indirection (to fetch the IMT).
- // 2) We will not go to the conflict trampoline with an invoke-virtual.
- // TODO: Consider sharpening once it is not dependent on the compiler driver.
-
- if (kIsDebugBuild && method->IsDefaultConflicting()) {
- ReferenceTypeInfo receiver_type = invoke_instruction->InputAt(0)->GetReferenceTypeInfo();
- // Devirtualization by exact type uses a method in the vtable,
- // so it's OK to change this invoke into a HInvokeVirtual.
- ObjPtr<mirror::Class> receiver_class = receiver_type.GetTypeHandle().Get();
- CHECK(!receiver_class->IsInterface());
- PointerSize pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- CHECK(method == receiver_class->GetVTableEntry(method->GetMethodIndex(), pointer_size));
- }
-
- uint32_t dex_method_index = FindMethodIndexIn(
- method,
- *invoke_instruction->GetMethodReference().dex_file,
- invoke_instruction->GetMethodReference().index);
- if (dex_method_index == dex::kDexNoIndex) {
- return false;
- }
- HInvokeVirtual* new_invoke = new (graph_->GetAllocator()) HInvokeVirtual(
- graph_->GetAllocator(),
- invoke_instruction->GetNumberOfArguments(),
- invoke_instruction->GetType(),
- invoke_instruction->GetDexPc(),
- MethodReference(invoke_instruction->GetMethodReference().dex_file, dex_method_index),
- method,
- MethodReference(method->GetDexFile(), method->GetDexMethodIndex()),
- method->GetMethodIndex());
- HInputsRef inputs = invoke_instruction->GetInputs();
- for (size_t index = 0; index != inputs.size(); ++index) {
- new_invoke->SetArgumentAt(index, inputs[index]);
- }
- invoke_instruction->GetBlock()->InsertInstructionBefore(new_invoke, invoke_instruction);
- new_invoke->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
- if (invoke_instruction->GetType() == DataType::Type::kReference) {
- new_invoke->SetReferenceTypeInfo(invoke_instruction->GetReferenceTypeInfo());
- }
- *replacement = new_invoke;
-
- MaybeReplaceAndRemove(*replacement, invoke_instruction);
- // No need to call MaybeRunReferenceTypePropagation, as we know the return type
- // cannot be more specific.
- DCHECK(!ReturnTypeMoreSpecific(*replacement, invoke_instruction));
- return true;
-}
-
-
-bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction,
- ArtMethod* method,
- ReferenceTypeInfo receiver_type,
- bool do_rtp) {
- DCHECK(!invoke_instruction->IsIntrinsic());
- HInstruction* return_replacement = nullptr;
-
- if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) {
- return false;
- }
-
- MaybeReplaceAndRemove(return_replacement, invoke_instruction);
- FixUpReturnReferenceType(method, return_replacement);
- if (do_rtp) {
- MaybeRunReferenceTypePropagation(return_replacement, invoke_instruction);
- }
return true;
}
@@ -2141,8 +2131,8 @@
return false;
}
-bool HInliner::ReturnTypeMoreSpecific(HInstruction* return_replacement,
- HInvoke* invoke_instruction) {
+bool HInliner::ReturnTypeMoreSpecific(HInvoke* invoke_instruction,
+ HInstruction* return_replacement) {
// Check the integrity of reference types and run another type propagation if needed.
if (return_replacement != nullptr) {
if (return_replacement->GetType() == DataType::Type::kReference) {
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index e98a66b..241c63b 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -72,13 +72,24 @@
bool TryInline(HInvoke* invoke_instruction);
+ // Attempt to resolve the target of the invoke instruction to an acutal call
+ // target.
+ //
+ // Returns the target directly in the case of static or direct invokes.
+ // Otherwise, uses CHA devirtualization or other methods to try to find the
+ // call target.
+ ArtMethod* FindActualCallTarget(HInvoke* invoke_instruction, bool* cha_devirtualize)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
// Try to inline `resolved_method` in place of `invoke_instruction`. `do_rtp` is whether
// reference type propagation can run after the inlining. If the inlining is successful, this
- // method will replace and remove the `invoke_instruction`.
+ // method will replace and remove the `invoke_instruction`. If `cha_devirtualize` is true,
+ // a CHA guard needs to be added for the inlining.
bool TryInlineAndReplace(HInvoke* invoke_instruction,
ArtMethod* resolved_method,
ReferenceTypeInfo receiver_type,
- bool do_rtp)
+ bool do_rtp,
+ bool cha_devirtualize)
REQUIRES_SHARED(Locks::mutator_lock_);
bool TryBuildAndInline(HInvoke* invoke_instruction,
@@ -159,17 +170,6 @@
bool TryInlineFromInlineCache(HInvoke* invoke_instruction)
REQUIRES_SHARED(Locks::mutator_lock_);
- // Try inlining the invoke instruction using CHA.
- bool TryInlineFromCHA(HInvoke* invoke_instruction)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
- // When we fail inlining `invoke_instruction`, we will try to devirtualize the
- // call.
- bool TryDevirtualize(HInvoke* invoke_instruction,
- ArtMethod* method,
- HInvoke** replacement)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
// Try getting the inline cache from JIT code cache.
// Return true if the inline cache was successfully allocated and the
// invoke info was found in the profile info.
@@ -215,7 +215,7 @@
// Try CHA-based devirtualization to change virtual method calls into
// direct calls.
// Returns the actual method that resolved_method can be devirtualized to.
- ArtMethod* FindMethodFromCHA(ArtMethod* resolved_method)
+ ArtMethod* TryCHADevirtualization(ArtMethod* resolved_method)
REQUIRES_SHARED(Locks::mutator_lock_);
// Add a CHA guard for a CHA-based devirtualized call. A CHA guard checks a
@@ -230,17 +230,13 @@
uint32_t dex_pc) const
REQUIRES_SHARED(Locks::mutator_lock_);
- void MaybeRunReferenceTypePropagation(HInstruction* replacement,
- HInvoke* invoke_instruction)
- REQUIRES_SHARED(Locks::mutator_lock_);
-
void FixUpReturnReferenceType(ArtMethod* resolved_method, HInstruction* return_replacement)
REQUIRES_SHARED(Locks::mutator_lock_);
bool ArgumentTypesMoreSpecific(HInvoke* invoke_instruction, ArtMethod* resolved_method)
REQUIRES_SHARED(Locks::mutator_lock_);
- bool ReturnTypeMoreSpecific(HInstruction* return_replacement, HInvoke* invoke_instruction)
+ bool ReturnTypeMoreSpecific(HInvoke* invoke_instruction, HInstruction* return_replacement)
REQUIRES_SHARED(Locks::mutator_lock_);
// Add a type guard on the given `receiver`. This will add to the graph:
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 3d0815f..a2f71cf 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -116,7 +116,6 @@
kPartialAllocationMoved,
kPredicatedLoadAdded,
kPredicatedStoreAdded,
- kDevirtualized,
kLastStat
};
std::ostream& operator<<(std::ostream& os, MethodCompilationStat rhs);