diff options
Diffstat (limited to 'compiler/optimizing/sharpening.cc')
| -rw-r--r-- | compiler/optimizing/sharpening.cc | 318 |
1 files changed, 179 insertions, 139 deletions
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index 08bd35f14a..eedaf6e67e 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -17,8 +17,10 @@ #include "sharpening.h" #include "base/casts.h" +#include "base/enums.h" #include "class_linker.h" #include "code_generator.h" +#include "driver/compiler_options.h" #include "driver/dex_compilation_unit.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "driver/compiler_driver.h" @@ -29,7 +31,7 @@ #include "mirror/string.h" #include "nodes.h" #include "runtime.h" -#include "scoped_thread_state_change.h" +#include "scoped_thread_state_change-inl.h" namespace art { @@ -39,130 +41,196 @@ void HSharpening::Run() { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsInvokeStaticOrDirect()) { - ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect()); + SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_); } else if (instruction->IsLoadString()) { ProcessLoadString(instruction->AsLoadString()); } // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data // because we know the type better when inlining. - // TODO: HLoadClass - select better load kind if available. } } } -void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { +static bool IsInBootImage(ArtMethod* method) { + const std::vector<gc::space::ImageSpace*>& image_spaces = + Runtime::Current()->GetHeap()->GetBootImageSpaces(); + for (gc::space::ImageSpace* image_space : image_spaces) { + const auto& method_section = image_space->GetImageHeader().GetMethodsSection(); + if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) { + return true; + } + } + return false; +} + +static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) { + return IsInBootImage(method) && !options.GetCompilePic(); +} + + +void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen) { if (invoke->IsStringInit()) { // Not using the dex cache arrays. But we could still try to use a better dispatch... // TODO: Use direct_method and direct_code for the appropriate StringFactory method. return; } - // TODO: Avoid CompilerDriver. - InvokeType original_invoke_type = invoke->GetOriginalInvokeType(); - InvokeType optimized_invoke_type = original_invoke_type; - MethodReference target_method(&graph_->GetDexFile(), invoke->GetDexMethodIndex()); - int vtable_idx; - uintptr_t direct_code, direct_method; - bool success = compiler_driver_->ComputeInvokeInfo( - &compilation_unit_, - invoke->GetDexPc(), - false /* update_stats: already updated in builder */, - true /* enable_devirtualization */, - &optimized_invoke_type, - &target_method, - &vtable_idx, - &direct_code, - &direct_method); - if (!success) { - // TODO: try using kDexCachePcRelative. It's always a valid method load - // kind as long as it's supported by the codegen - return; - } - invoke->SetOptimizedInvokeType(optimized_invoke_type); - invoke->SetTargetMethod(target_method); + ArtMethod* callee = invoke->GetResolvedMethod(); + DCHECK(callee != nullptr); HInvokeStaticOrDirect::MethodLoadKind method_load_kind; HInvokeStaticOrDirect::CodePtrLocation code_ptr_location; uint64_t method_load_data = 0u; - uint64_t direct_code_ptr = 0u; - HGraph* outer_graph = codegen_->GetGraph(); - if (target_method.dex_file == &outer_graph->GetDexFile() && - target_method.dex_method_index == outer_graph->GetMethodIdx()) { + // Note: we never call an ArtMethod through a known code pointer, as + // we do not want to keep on invoking it if it gets deoptimized. This + // applies to both AOT and JIT. + // This also avoids having to find out if the code pointer of an ArtMethod + // is the resolution trampoline (for ensuring the class is initialized), or + // the interpreter entrypoint. Such code pointers we do not want to call + // directly. + // Only in the case of a recursive call can we call directly, as we know the + // class is initialized already or being initialized, and the call will not + // be invoked once the method is deoptimized. + + // We don't optimize for debuggable as it would prevent us from obsoleting the method in some + // situations. + if (callee == codegen->GetGraph()->GetArtMethod() && !codegen->GetGraph()->IsDebuggable()) { + // Recursive call. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf; + } else if (Runtime::Current()->UseJitCompilation() || + AOTCanEmbedMethod(callee, codegen->GetCompilerOptions())) { + // JIT or on-device AOT compilation referencing a boot image method. + // Use the method address directly. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; + method_load_data = reinterpret_cast<uintptr_t>(callee); + code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } else { - bool use_pc_relative_instructions = - ((direct_method == 0u || direct_code == static_cast<uintptr_t>(-1))) && - ContainsElement(compiler_driver_->GetDexFilesForOatFile(), target_method.dex_file); - if (direct_method != 0u) { // Should we use a direct pointer to the method? - // Note: For JIT, kDirectAddressWithFixup doesn't make sense at all and while - // kDirectAddress would be fine for image methods, we don't support it at the moment. - DCHECK(!Runtime::Current()->UseJitCompilation()); - if (direct_method != static_cast<uintptr_t>(-1)) { // Is the method pointer known now? - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; - method_load_data = direct_method; - } else { // The direct pointer will be known at link time. - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddressWithFixup; - } - } else { // Use dex cache. - DCHECK_EQ(target_method.dex_file, &graph_->GetDexFile()); - if (use_pc_relative_instructions) { // Can we use PC-relative access to the dex cache arrays? - DCHECK(!Runtime::Current()->UseJitCompilation()); - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative; - DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()), - &graph_->GetDexFile()); - method_load_data = layout.MethodOffset(target_method.dex_method_index); - } else { // We must go through the ArtMethod's pointer to resolved methods. - method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCacheViaMethod; - } - } - if (direct_code != 0u) { // Should we use a direct pointer to the code? - // Note: For JIT, kCallPCRelative and kCallDirectWithFixup don't make sense at all and - // while kCallDirect would be fine for image methods, we don't support it at the moment. - DCHECK(!Runtime::Current()->UseJitCompilation()); - if (direct_code != static_cast<uintptr_t>(-1)) { // Is the code pointer known now? - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirect; - direct_code_ptr = direct_code; - } else if (use_pc_relative_instructions) { - // Use PC-relative calls for invokes within a multi-dex oat file. - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallPCRelative; - } else { // The direct pointer will be known at link time. - // NOTE: This is used for app->boot calls when compiling an app against - // a relocatable but not yet relocated image. - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallDirectWithFixup; - } - } else { // We must use the code pointer from the ArtMethod. - code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; - } + // Use PC-relative access to the dex cache arrays. + method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative; + // Note: we use the invoke's graph instead of the codegen graph, which are + // different when inlining (the codegen graph is the most outer graph). The + // invoke's dex method index is relative to the dex file where the invoke's graph + // was built from. + DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen->GetInstructionSet()), + &invoke->GetBlock()->GetGraph()->GetDexFile()); + method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex()); + code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } - if (graph_->IsDebuggable()) { + if (codegen->GetGraph()->IsDebuggable()) { // For debuggable apps always use the code pointer from ArtMethod // so that we don't circumvent instrumentation stubs if installed. code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = { - method_load_kind, code_ptr_location, method_load_data, direct_code_ptr + method_load_kind, code_ptr_location, method_load_data }; HInvokeStaticOrDirect::DispatchInfo dispatch_info = - codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, - invoke->GetTargetMethod()); + codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); invoke->SetDispatchInfo(dispatch_info); } +HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) { + Handle<mirror::Class> klass = load_class->GetClass(); + DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod || + load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) + << load_class->GetLoadKind(); + DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening."; + + HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); + + if (load_class->NeedsAccessCheck()) { + // We need to call the runtime anyway, so we simply get the class as that call's return value. + } else if (load_kind == HLoadClass::LoadKind::kReferrersClass) { + // Loading from the ArtMethod* is the most efficient retrieval in code size. + // TODO: This may not actually be true for all architectures and + // locations of target classes. The additional register pressure + // for using the ArtMethod* should be considered. + } else { + const DexFile& dex_file = load_class->GetDexFile(); + dex::TypeIndex type_index = load_class->GetTypeIndex(); + + bool is_in_boot_image = false; + HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid; + Runtime* runtime = Runtime::Current(); + if (codegen->GetCompilerOptions().IsBootImage()) { + // Compiling boot image. Check if the class is a boot image class. + DCHECK(!runtime->UseJitCompilation()); + if (!compiler_driver->GetSupportBootImageFixup()) { + // compiler_driver_test. Do not sharpen. + desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; + } else if ((klass != nullptr) && compiler_driver->IsImageClass( + dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) { + is_in_boot_image = true; + desired_load_kind = codegen->GetCompilerOptions().GetCompilePic() + ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative + : HLoadClass::LoadKind::kBootImageLinkTimeAddress; + } else { + // Not a boot image class. + DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); + desired_load_kind = HLoadClass::LoadKind::kBssEntry; + } + } else { + is_in_boot_image = (klass != nullptr) && + runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); + if (runtime->UseJitCompilation()) { + // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. + // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); + if (is_in_boot_image) { + // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 + desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; + } else if (klass != nullptr) { + desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; + } else { + // Class not loaded yet. This happens when the dex code requesting + // this `HLoadClass` hasn't been executed in the interpreter. + // Fallback to the dex cache. + // TODO(ngeoffray): Generate HDeoptimize instead. + desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod; + } + } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) { + // AOT app compilation. Check if the class is in the boot image. + desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; + } else { + // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. + desired_load_kind = HLoadClass::LoadKind::kBssEntry; + } + } + DCHECK_NE(desired_load_kind, HLoadClass::LoadKind::kInvalid); + + if (is_in_boot_image) { + load_class->MarkInBootImage(); + } + load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind); + } + + if (!IsSameDexFile(load_class->GetDexFile(), *dex_compilation_unit.GetDexFile())) { + if ((load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) || + (load_kind == HLoadClass::LoadKind::kBssEntry)) { + // We actually cannot reference this class, we're forced to bail. + // We cannot reference this class with Bss, as the entrypoint will lookup the class + // in the caller's dex file, but that dex file does not reference the class. + return HLoadClass::LoadKind::kInvalid; + } + } + return load_kind; +} + void HSharpening::ProcessLoadString(HLoadString* load_string) { DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kDexCacheViaMethod); - DCHECK(!load_string->IsInDexCache()); const DexFile& dex_file = load_string->GetDexFile(); - uint32_t string_index = load_string->GetStringIndex(); + dex::StringIndex string_index = load_string->GetStringIndex(); - bool is_in_dex_cache = false; - HLoadString::LoadKind desired_load_kind; - uint64_t address = 0u; // String or dex cache element address. + HLoadString::LoadKind desired_load_kind = static_cast<HLoadString::LoadKind>(-1); { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); @@ -171,83 +239,55 @@ void HSharpening::ProcessLoadString(HLoadString* load_string) { Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) ? compilation_unit_.GetDexCache() : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); + mirror::String* string = nullptr; - if (compiler_driver_->IsBootImage()) { - // Compiling boot image. Resolve the string and allocate it if needed. + if (codegen_->GetCompilerOptions().IsBootImage()) { + // Compiling boot image. Resolve the string and allocate it if needed, to ensure + // the string will be added to the boot image. DCHECK(!runtime->UseJitCompilation()); - mirror::String* string = class_linker->ResolveString(dex_file, string_index, dex_cache); + string = class_linker->ResolveString(dex_file, string_index, dex_cache); CHECK(string != nullptr); - if (!compiler_driver_->GetSupportBootImageFixup()) { - // MIPS/MIPS64 or compiler_driver_test. Do not sharpen. - desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; - } else { + if (compiler_driver_->GetSupportBootImageFixup()) { DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); - is_in_dex_cache = true; desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic() ? HLoadString::LoadKind::kBootImageLinkTimePcRelative : HLoadString::LoadKind::kBootImageLinkTimeAddress; + } else { + // compiler_driver_test. Do not sharpen. + desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; } } else if (runtime->UseJitCompilation()) { // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus. // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); - mirror::String* string = dex_cache->GetResolvedString(string_index); - is_in_dex_cache = (string != nullptr); - if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(string); + string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + if (string != nullptr) { + if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { + desired_load_kind = HLoadString::LoadKind::kBootImageAddress; + } else { + desired_load_kind = HLoadString::LoadKind::kJitTableAddress; + } } else { - // Note: If the string is not in the dex cache, the instruction needs environment - // and will not be inlined across dex files. Within a dex file, the slow-path helper - // loads the correct string and inlined frames are used correctly for OOM stack trace. - // TODO: Write a test for this. - desired_load_kind = HLoadString::LoadKind::kDexCacheAddress; - void* dex_cache_element_address = &dex_cache->GetStrings()[string_index]; - address = reinterpret_cast64<uint64_t>(dex_cache_element_address); + desired_load_kind = HLoadString::LoadKind::kDexCacheViaMethod; } } else { // AOT app compilation. Try to lookup the string without allocating if not found. - mirror::String* string = class_linker->LookupString(dex_file, string_index, dex_cache); - if (string != nullptr && runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { - if (codegen_->GetCompilerOptions().GetCompilePic()) { - // Use PC-relative load from the dex cache if the dex file belongs - // to the oat file that we're currently compiling. - desired_load_kind = ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file) - ? HLoadString::LoadKind::kDexCachePcRelative - : HLoadString::LoadKind::kDexCacheViaMethod; - } else { - desired_load_kind = HLoadString::LoadKind::kBootImageAddress; - address = reinterpret_cast64<uint64_t>(string); - } + string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); + if (string != nullptr && + runtime->GetHeap()->ObjectIsInBootImageSpace(string) && + !codegen_->GetCompilerOptions().GetCompilePic()) { + desired_load_kind = HLoadString::LoadKind::kBootImageAddress; } else { - // Not JIT and the string is not in boot image. - desired_load_kind = HLoadString::LoadKind::kDexCachePcRelative; + desired_load_kind = HLoadString::LoadKind::kBssEntry; } } + if (string != nullptr) { + load_string->SetString(handles_->NewHandle(string)); + } } - if (is_in_dex_cache) { - load_string->MarkInDexCache(); - } + DCHECK_NE(desired_load_kind, static_cast<HLoadString::LoadKind>(-1)); HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind); - switch (load_kind) { - case HLoadString::LoadKind::kBootImageLinkTimeAddress: - case HLoadString::LoadKind::kBootImageLinkTimePcRelative: - case HLoadString::LoadKind::kDexCacheViaMethod: - load_string->SetLoadKindWithStringReference(load_kind, dex_file, string_index); - break; - case HLoadString::LoadKind::kBootImageAddress: - case HLoadString::LoadKind::kDexCacheAddress: - DCHECK_NE(address, 0u); - load_string->SetLoadKindWithAddress(load_kind, address); - break; - case HLoadString::LoadKind::kDexCachePcRelative: { - size_t pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet()); - DexCacheArraysLayout layout(pointer_size, &dex_file); - size_t element_index = layout.StringOffset(string_index); - load_string->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index); - break; - } - } + load_string->SetLoadKind(load_kind); } } // namespace art |