1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include "sharpening.h" 18 19 #include "art_method-inl.h" 20 #include "base/casts.h" 21 #include "base/enums.h" 22 #include "class_linker.h" 23 #include "code_generator.h" 24 #include "driver/compiler_options.h" 25 #include "driver/dex_compilation_unit.h" 26 #include "utils/dex_cache_arrays_layout-inl.h" 27 #include "driver/compiler_driver.h" 28 #include "gc/heap.h" 29 #include "gc/space/image_space.h" 30 #include "handle_scope-inl.h" 31 #include "mirror/dex_cache.h" 32 #include "mirror/string.h" 33 #include "nodes.h" 34 #include "runtime.h" 35 #include "scoped_thread_state_change-inl.h" 36 37 namespace art { 38 39 void HSharpening::Run() { 40 // We don't care about the order of the blocks here. 41 for (HBasicBlock* block : graph_->GetReversePostOrder()) { 42 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { 43 HInstruction* instruction = it.Current(); 44 if (instruction->IsInvokeStaticOrDirect()) { 45 SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), 46 codegen_, 47 compiler_driver_); 48 } else if (instruction->IsLoadString()) { 49 ProcessLoadString(instruction->AsLoadString()); 50 } 51 // TODO: Move the sharpening of invoke-virtual/-interface/-super from HGraphBuilder 52 // here. Rewrite it to avoid the CompilerDriver's reliance on verifier data 53 // because we know the type better when inlining. 54 } 55 } 56 } 57 58 static bool IsInBootImage(ArtMethod* method) { 59 const std::vector<gc::space::ImageSpace*>& image_spaces = 60 Runtime::Current()->GetHeap()->GetBootImageSpaces(); 61 for (gc::space::ImageSpace* image_space : image_spaces) { 62 const ImageSection& method_section = image_space->GetImageHeader().GetMethodsSection(); 63 if (method_section.Contains(reinterpret_cast<uint8_t*>(method) - image_space->Begin())) { 64 return true; 65 } 66 } 67 return false; 68 } 69 70 static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) { 71 return IsInBootImage(method) && !options.GetCompilePic(); 72 } 73 74 static bool BootImageAOTCanEmbedMethod(ArtMethod* method, CompilerDriver* compiler_driver) { 75 DCHECK(compiler_driver->GetCompilerOptions().IsBootImage()); 76 if (!compiler_driver->GetSupportBootImageFixup()) { 77 return false; 78 } 79 ScopedObjectAccess soa(Thread::Current()); 80 ObjPtr<mirror::Class> klass = method->GetDeclaringClass(); 81 DCHECK(klass != nullptr); 82 const DexFile& dex_file = klass->GetDexFile(); 83 return compiler_driver->IsImageClass(dex_file.StringByTypeIdx(klass->GetDexTypeIndex())); 84 } 85 86 void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, 87 CodeGenerator* codegen, 88 CompilerDriver* compiler_driver) { 89 if (invoke->IsStringInit()) { 90 // Not using the dex cache arrays. But we could still try to use a better dispatch... 91 // TODO: Use direct_method and direct_code for the appropriate StringFactory method. 92 return; 93 } 94 95 ArtMethod* callee = invoke->GetResolvedMethod(); 96 DCHECK(callee != nullptr); 97 98 HInvokeStaticOrDirect::MethodLoadKind method_load_kind; 99 HInvokeStaticOrDirect::CodePtrLocation code_ptr_location; 100 uint64_t method_load_data = 0u; 101 102 // Note: we never call an ArtMethod through a known code pointer, as 103 // we do not want to keep on invoking it if it gets deoptimized. This 104 // applies to both AOT and JIT. 105 // This also avoids having to find out if the code pointer of an ArtMethod 106 // is the resolution trampoline (for ensuring the class is initialized), or 107 // the interpreter entrypoint. Such code pointers we do not want to call 108 // directly. 109 // Only in the case of a recursive call can we call directly, as we know the 110 // class is initialized already or being initialized, and the call will not 111 // be invoked once the method is deoptimized. 112 113 // We don't optimize for debuggable as it would prevent us from obsoleting the method in some 114 // situations. 115 if (callee == codegen->GetGraph()->GetArtMethod() && !codegen->GetGraph()->IsDebuggable()) { 116 // Recursive call. 117 method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive; 118 code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf; 119 } else if (Runtime::Current()->UseJitCompilation() || 120 AOTCanEmbedMethod(callee, codegen->GetCompilerOptions())) { 121 // JIT or on-device AOT compilation referencing a boot image method. 122 // Use the method address directly. 123 method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; 124 method_load_data = reinterpret_cast<uintptr_t>(callee); 125 code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 126 } else if (codegen->GetCompilerOptions().IsBootImage() && 127 BootImageAOTCanEmbedMethod(callee, compiler_driver)) { 128 method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBootImageLinkTimePcRelative; 129 code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 130 } else { 131 // Use PC-relative access to the .bss methods arrays. 132 method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kBssEntry; 133 code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 134 } 135 136 if (codegen->GetGraph()->IsDebuggable()) { 137 // For debuggable apps always use the code pointer from ArtMethod 138 // so that we don't circumvent instrumentation stubs if installed. 139 code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; 140 } 141 142 HInvokeStaticOrDirect::DispatchInfo desired_dispatch_info = { 143 method_load_kind, code_ptr_location, method_load_data 144 }; 145 HInvokeStaticOrDirect::DispatchInfo dispatch_info = 146 codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); 147 invoke->SetDispatchInfo(dispatch_info); 148 } 149 150 HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, 151 CodeGenerator* codegen, 152 CompilerDriver* compiler_driver, 153 const DexCompilationUnit& dex_compilation_unit) { 154 Handle<mirror::Class> klass = load_class->GetClass(); 155 DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kRuntimeCall || 156 load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) 157 << load_class->GetLoadKind(); 158 DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening."; 159 160 HLoadClass::LoadKind load_kind = load_class->GetLoadKind(); 161 162 if (load_class->NeedsAccessCheck()) { 163 // We need to call the runtime anyway, so we simply get the class as that call's return value. 164 } else if (load_kind == HLoadClass::LoadKind::kReferrersClass) { 165 // Loading from the ArtMethod* is the most efficient retrieval in code size. 166 // TODO: This may not actually be true for all architectures and 167 // locations of target classes. The additional register pressure 168 // for using the ArtMethod* should be considered. 169 } else { 170 const DexFile& dex_file = load_class->GetDexFile(); 171 dex::TypeIndex type_index = load_class->GetTypeIndex(); 172 173 bool is_in_boot_image = false; 174 HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid; 175 Runtime* runtime = Runtime::Current(); 176 if (codegen->GetCompilerOptions().IsBootImage()) { 177 // Compiling boot image. Check if the class is a boot image class. 178 DCHECK(!runtime->UseJitCompilation()); 179 if (!compiler_driver->GetSupportBootImageFixup()) { 180 // compiler_driver_test. Do not sharpen. 181 desired_load_kind = HLoadClass::LoadKind::kRuntimeCall; 182 } else if ((klass != nullptr) && 183 compiler_driver->IsImageClass(dex_file.StringByTypeIdx(type_index))) { 184 is_in_boot_image = true; 185 desired_load_kind = HLoadClass::LoadKind::kBootImageLinkTimePcRelative; 186 } else { 187 // Not a boot image class. 188 DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file)); 189 desired_load_kind = HLoadClass::LoadKind::kBssEntry; 190 } 191 } else { 192 is_in_boot_image = (klass != nullptr) && 193 runtime->GetHeap()->ObjectIsInBootImageSpace(klass.Get()); 194 if (runtime->UseJitCompilation()) { 195 DCHECK(!codegen->GetCompilerOptions().GetCompilePic()); 196 if (is_in_boot_image) { 197 // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787 198 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; 199 } else if (klass != nullptr) { 200 desired_load_kind = HLoadClass::LoadKind::kJitTableAddress; 201 } else { 202 // Class not loaded yet. This happens when the dex code requesting 203 // this `HLoadClass` hasn't been executed in the interpreter. 204 // Fallback to the dex cache. 205 // TODO(ngeoffray): Generate HDeoptimize instead. 206 desired_load_kind = HLoadClass::LoadKind::kRuntimeCall; 207 } 208 } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) { 209 // AOT app compilation. Check if the class is in the boot image. 210 desired_load_kind = HLoadClass::LoadKind::kBootImageAddress; 211 } else { 212 // Not JIT and either the klass is not in boot image or we are compiling in PIC mode. 213 desired_load_kind = HLoadClass::LoadKind::kBssEntry; 214 } 215 } 216 DCHECK_NE(desired_load_kind, HLoadClass::LoadKind::kInvalid); 217 218 if (is_in_boot_image) { 219 load_class->MarkInBootImage(); 220 } 221 load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind); 222 } 223 224 if (!IsSameDexFile(load_class->GetDexFile(), *dex_compilation_unit.GetDexFile())) { 225 if ((load_kind == HLoadClass::LoadKind::kRuntimeCall) || 226 (load_kind == HLoadClass::LoadKind::kBssEntry)) { 227 // We actually cannot reference this class, we're forced to bail. 228 // We cannot reference this class with Bss, as the entrypoint will lookup the class 229 // in the caller's dex file, but that dex file does not reference the class. 230 return HLoadClass::LoadKind::kInvalid; 231 } 232 } 233 return load_kind; 234 } 235 236 void HSharpening::ProcessLoadString(HLoadString* load_string) { 237 DCHECK_EQ(load_string->GetLoadKind(), HLoadString::LoadKind::kRuntimeCall); 238 239 const DexFile& dex_file = load_string->GetDexFile(); 240 dex::StringIndex string_index = load_string->GetStringIndex(); 241 242 HLoadString::LoadKind desired_load_kind = static_cast<HLoadString::LoadKind>(-1); 243 { 244 Runtime* runtime = Runtime::Current(); 245 ClassLinker* class_linker = runtime->GetClassLinker(); 246 ScopedObjectAccess soa(Thread::Current()); 247 StackHandleScope<1> hs(soa.Self()); 248 Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile()) 249 ? compilation_unit_.GetDexCache() 250 : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file)); 251 mirror::String* string = nullptr; 252 253 if (codegen_->GetCompilerOptions().IsBootImage()) { 254 // Compiling boot image. Resolve the string and allocate it if needed, to ensure 255 // the string will be added to the boot image. 256 DCHECK(!runtime->UseJitCompilation()); 257 string = class_linker->ResolveString(dex_file, string_index, dex_cache); 258 CHECK(string != nullptr); 259 if (compiler_driver_->GetSupportBootImageFixup()) { 260 DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file)); 261 desired_load_kind = HLoadString::LoadKind::kBootImageLinkTimePcRelative; 262 } else { 263 // compiler_driver_test. Do not sharpen. 264 desired_load_kind = HLoadString::LoadKind::kRuntimeCall; 265 } 266 } else if (runtime->UseJitCompilation()) { 267 DCHECK(!codegen_->GetCompilerOptions().GetCompilePic()); 268 string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); 269 if (string != nullptr) { 270 if (runtime->GetHeap()->ObjectIsInBootImageSpace(string)) { 271 desired_load_kind = HLoadString::LoadKind::kBootImageAddress; 272 } else { 273 desired_load_kind = HLoadString::LoadKind::kJitTableAddress; 274 } 275 } else { 276 desired_load_kind = HLoadString::LoadKind::kRuntimeCall; 277 } 278 } else { 279 // AOT app compilation. Try to lookup the string without allocating if not found. 280 string = class_linker->LookupString(dex_file, string_index, dex_cache.Get()); 281 if (string != nullptr && 282 runtime->GetHeap()->ObjectIsInBootImageSpace(string) && 283 !codegen_->GetCompilerOptions().GetCompilePic()) { 284 desired_load_kind = HLoadString::LoadKind::kBootImageAddress; 285 } else { 286 desired_load_kind = HLoadString::LoadKind::kBssEntry; 287 } 288 } 289 if (string != nullptr) { 290 load_string->SetString(handles_->NewHandle(string)); 291 } 292 } 293 DCHECK_NE(desired_load_kind, static_cast<HLoadString::LoadKind>(-1)); 294 295 HLoadString::LoadKind load_kind = codegen_->GetSupportedLoadStringKind(desired_load_kind); 296 load_string->SetLoadKind(load_kind); 297 } 298 299 } // namespace art 300