// Copyright 2011 Google Inc. All Rights Reserved. #include "compiler.h" #include #include "assembler.h" #include "class_linker.h" #include "class_loader.h" #include "dex_cache.h" #include "dex_verifier.h" #include "jni_compiler.h" #include "jni_internal.h" #include "oat_file.h" #include "object_utils.h" #include "runtime.h" #include "stl_util.h" namespace art { CompiledMethod* oatCompileMethod(const Compiler& compiler, const DexFile::CodeItem* code_item, uint32_t access_flags, uint32_t method_idx, const ClassLoader* class_loader, const DexFile& dex_file, InstructionSet); namespace arm { ByteArray* CreateAbstractMethodErrorStub(); CompiledInvokeStub* ArmCreateInvokeStub(bool is_static, const char* shorty); ByteArray* ArmCreateResolutionTrampoline(Runtime::TrampolineType type); ByteArray* CreateJniDlsymLookupStub(); } namespace x86 { ByteArray* CreateAbstractMethodErrorStub(); CompiledInvokeStub* X86CreateInvokeStub(bool is_static, const char* shorty); ByteArray* X86CreateResolutionTrampoline(Runtime::TrampolineType type); ByteArray* CreateJniDlsymLookupStub(); } Compiler::Compiler(InstructionSet instruction_set, bool image, const std::set* image_classes) : instruction_set_(instruction_set), jni_compiler_(instruction_set), image_(image), dex_file_count_(0), class_count_(0), abstract_method_count_(0), native_method_count_(0), regular_method_count_(0), instruction_count_(0), start_ns_(NanoTime()), image_classes_(image_classes) { CHECK(!Runtime::Current()->IsStarted()); if (!image_) { CHECK(image_classes_ == NULL); } } Compiler::~Compiler() { STLDeleteValues(&compiled_classes_); STLDeleteValues(&compiled_methods_); STLDeleteValues(&compiled_invoke_stubs_); if (dex_file_count_ > 0) { uint64_t duration_ns = NanoTime() - start_ns_; std::string stats(StringPrintf("Compiled files:%zd" " classes:%zd" " methods:(abstract:%zd" " native:%zd" " regular:%zd)" " instructions:%zd", dex_file_count_, class_count_, abstract_method_count_, native_method_count_, regular_method_count_, instruction_count_)); stats += " (took ", stats += PrettyDuration(duration_ns); if (instruction_count_ != 0) { stats += ", "; stats += PrettyDuration(duration_ns / instruction_count_); stats += "/instruction"; } stats += ")"; LOG(INFO) << stats; } } ByteArray* Compiler::CreateResolutionStub(InstructionSet instruction_set, Runtime::TrampolineType type) { if (instruction_set == kX86) { return x86::X86CreateResolutionTrampoline(type); } else { CHECK(instruction_set == kArm || instruction_set == kThumb2); // Generates resolution stub using ARM instruction set return arm::ArmCreateResolutionTrampoline(type); } } ByteArray* Compiler::CreateJniDlsymLookupStub(InstructionSet instruction_set) { switch (instruction_set) { case kArm: case kThumb2: return arm::CreateJniDlsymLookupStub(); case kX86: return x86::CreateJniDlsymLookupStub(); default: LOG(FATAL) << "Unknown InstructionSet: " << static_cast(instruction_set); return NULL; } } ByteArray* Compiler::CreateAbstractMethodErrorStub(InstructionSet instruction_set) { if (instruction_set == kX86) { return x86::CreateAbstractMethodErrorStub(); } else { CHECK(instruction_set == kArm || instruction_set == kThumb2); // Generates resolution stub using ARM instruction set return arm::CreateAbstractMethodErrorStub(); } } void Compiler::CompileAll(const ClassLoader* class_loader, const std::vector& dex_files) { DCHECK(!Runtime::Current()->IsStarted()); PreCompile(class_loader, dex_files); Compile(class_loader, dex_files); PostCompile(class_loader, dex_files); } void Compiler::CompileOne(const Method* method) { DCHECK(!Runtime::Current()->IsStarted()); const ClassLoader* class_loader = method->GetDeclaringClass()->GetClassLoader(); // Find the dex_file const DexCache* dex_cache = method->GetDeclaringClass()->GetDexCache(); const DexFile& dex_file = Runtime::Current()->GetClassLinker()->FindDexFile(dex_cache); std::vector dex_files; dex_files.push_back(&dex_file); PreCompile(class_loader, dex_files); uint32_t method_idx = method->GetDexMethodIndex(); const DexFile::CodeItem* code_item = dex_file.GetCodeItem(method->GetCodeItemOffset()); CompileMethod(code_item, method->GetAccessFlags(), method_idx, class_loader, dex_file); PostCompile(class_loader, dex_files); } void Compiler::Resolve(const ClassLoader* class_loader, const std::vector& dex_files) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); ResolveDexFile(class_loader, *dex_file); } } void Compiler::PreCompile(const ClassLoader* class_loader, const std::vector& dex_files) { Resolve(class_loader, dex_files); Verify(class_loader, dex_files); InitializeClassesWithoutClinit(class_loader, dex_files); } void Compiler::PostCompile(const ClassLoader* class_loader, const std::vector& dex_files) { SetGcMaps(class_loader, dex_files); SetCodeAndDirectMethods(dex_files); } bool Compiler::IsImageClass(const std::string& descriptor) const { if (image_classes_ == NULL) { return true; } return image_classes_->find(descriptor) != image_classes_->end(); } bool Compiler::CanAssumeTypeIsPresentInDexCache(const DexCache* dex_cache, uint32_t type_idx) const { if (!IsImage()) { return false; } Class* resolved_class = dex_cache->GetResolvedTypes()->Get(type_idx); if (resolved_class == NULL) { return false; } return IsImageClass(ClassHelper(resolved_class).GetDescriptor()); } // Return true if the class should be skipped during compilation. We // never skip classes in the boot class loader. However, if we have a // non-boot class loader and we can resolve the class in the boot // class loader, we do skip the class. This happens if an app bundles // classes found in the boot classpath. Since at runtime we will // select the class from the boot classpath, do not attempt to resolve // or compile it now. static bool SkipClass(const ClassLoader* class_loader, const DexFile& dex_file, const DexFile::ClassDef& class_def) { if (class_loader == NULL) { return false; } const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); Class* klass = class_linker->FindClass(descriptor, NULL); if (klass == NULL) { Thread* self = Thread::Current(); CHECK(self->IsExceptionPending()); self->ClearException(); return false; } return true; } void Compiler::ResolveDexFile(const ClassLoader* class_loader, const DexFile& dex_file) { Thread* self = Thread::Current(); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); DexCache* dex_cache = class_linker->FindDexCache(dex_file); // Strings are easy in that they always are simply resolved to literals in the same file if (image_ && image_classes_ == NULL) { // TODO: Add support for loading strings referenced by image_classes_ // See also Compiler::CanAssumeTypeIsPresentInDexCache. for (size_t string_idx = 0; string_idx < dex_cache->NumStrings(); string_idx++) { class_linker->ResolveString(dex_file, string_idx, dex_cache); } } // Class derived values are more complicated, they require the linker and loader. for (size_t type_idx = 0; type_idx < dex_cache->NumResolvedTypes(); type_idx++) { Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); if (klass == NULL) { CHECK(self->IsExceptionPending()); Thread::Current()->ClearException(); } } // Method and Field are the worst. We can't resolve without either // context from the code use (to disambiguate virtual vs direct // method and instance vs static field) or from class // definitions. While the compiler will resolve what it can as it // needs it, here we try to resolve fields and methods used in class // definitions, since many of them many never be referenced by // generated code. for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); if (SkipClass(class_loader, dex_file, class_def)) { continue; } // Note the class_data pointer advances through the headers, // static fields, instance fields, direct methods, and virtual // methods. const byte* class_data = dex_file.GetClassData(class_def); if (class_data == NULL) { // empty class such as a marker interface continue; } ClassDataItemIterator it(dex_file, class_data); while (it.HasNextStaticField()) { Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true); if (field == NULL) { CHECK(self->IsExceptionPending()); self->ClearException(); } it.Next(); } while (it.HasNextInstanceField()) { Field* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), dex_cache, class_loader, false); if (field == NULL) { CHECK(self->IsExceptionPending()); self->ClearException(); } it.Next(); } while (it.HasNextDirectMethod()) { Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true); if (method == NULL) { CHECK(self->IsExceptionPending()); self->ClearException(); } it.Next(); } while (it.HasNextVirtualMethod()) { Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache, class_loader, false); if (method == NULL) { CHECK(self->IsExceptionPending()); self->ClearException(); } it.Next(); } DCHECK(!it.HasNext()); } } void Compiler::Verify(const ClassLoader* class_loader, const std::vector& dex_files) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); VerifyDexFile(class_loader, *dex_file); } } void Compiler::VerifyDexFile(const ClassLoader* class_loader, const DexFile& dex_file) { dex_file.ChangePermissions(PROT_READ | PROT_WRITE); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); const char* descriptor = dex_file.GetClassDescriptor(class_def); Class* klass = class_linker->FindClass(descriptor, class_loader); if (klass == NULL) { Thread* self = Thread::Current(); CHECK(self->IsExceptionPending()); self->ClearException(); continue; } CHECK(klass->IsResolved()) << PrettyClass(klass); class_linker->VerifyClass(klass); if (klass->IsErroneous()) { // ClassLinker::VerifyClass throws, which isn't useful in the compiler. CHECK(Thread::Current()->IsExceptionPending()); Thread::Current()->ClearException(); // We want to try verification again at run-time, so move back into the resolved state. klass->SetStatus(Class::kStatusResolved); } CHECK(klass->IsVerified() || klass->IsResolved()) << PrettyClass(klass); CHECK(!Thread::Current()->IsExceptionPending()) << PrettyTypeOf(Thread::Current()->GetException()); } dex_file.ChangePermissions(PROT_READ); } void Compiler::InitializeClassesWithoutClinit(const ClassLoader* class_loader, const std::vector& dex_files) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); InitializeClassesWithoutClinit(class_loader, *dex_file); } } void Compiler::InitializeClassesWithoutClinit(const ClassLoader* class_loader, const DexFile& dex_file) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); const char* descriptor = dex_file.GetClassDescriptor(class_def); Class* klass = class_linker->FindClass(descriptor, class_loader); if (klass != NULL) { class_linker->EnsureInitialized(klass, false); // record the final class status if necessary Class::Status status = klass->GetStatus(); ClassReference ref(&dex_file, class_def_index); CompiledClass* compiled_class = GetCompiledClass(ref); if (compiled_class == NULL) { compiled_class = new CompiledClass(status); compiled_classes_[ref] = compiled_class; } else { DCHECK_EQ(status, compiled_class->GetStatus()); } } // clear any class not found or verification exceptions Thread::Current()->ClearException(); } DexCache* dex_cache = class_linker->FindDexCache(dex_file); for (size_t type_idx = 0; type_idx < dex_cache->NumResolvedTypes(); type_idx++) { Class* klass = class_linker->ResolveType(dex_file, type_idx, dex_cache, class_loader); if (klass == NULL) { Thread::Current()->ClearException(); } else if (klass->IsInitialized()) { dex_cache->GetInitializedStaticStorage()->Set(type_idx, klass); } } } void Compiler::Compile(const ClassLoader* class_loader, const std::vector& dex_files) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); CompileDexFile(class_loader, *dex_file); } } void Compiler::CompileDexFile(const ClassLoader* class_loader, const DexFile& dex_file) { ++dex_file_count_; for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); CompileClass(class_def, class_loader, dex_file); } } void Compiler::CompileClass(const DexFile::ClassDef& class_def, const ClassLoader* class_loader, const DexFile& dex_file) { if (SkipClass(class_loader, dex_file, class_def)) { return; } ++class_count_; const byte* class_data = dex_file.GetClassData(class_def); if (class_data == NULL) { // empty class, probably a marker interface return; } ClassDataItemIterator it(dex_file, class_data); // Skip fields while (it.HasNextStaticField()) { it.Next(); } while (it.HasNextInstanceField()) { it.Next(); } // Compile direct methods while (it.HasNextDirectMethod()) { CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMemberIndex(), class_loader, dex_file); it.Next(); } // Compile virtual methods while (it.HasNextVirtualMethod()) { CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMemberIndex(), class_loader, dex_file); it.Next(); } DCHECK(!it.HasNext()); } void Compiler::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, uint32_t method_idx, const ClassLoader* class_loader, const DexFile& dex_file) { CompiledMethod* compiled_method = NULL; uint64_t start_ns = NanoTime(); if ((access_flags & kAccNative) != 0) { ++native_method_count_; compiled_method = jni_compiler_.Compile(access_flags, method_idx, class_loader, dex_file); CHECK(compiled_method != NULL); } else if ((access_flags & kAccAbstract) != 0) { ++abstract_method_count_; } else { ++regular_method_count_; instruction_count_ += code_item->insns_size_in_code_units_; compiled_method = oatCompileMethod(*this, code_item, access_flags, method_idx, class_loader, dex_file, kThumb2); CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); } uint64_t duration_ns = NanoTime() - start_ns; if (duration_ns > MsToNs(10)) { LOG(WARNING) << "Compilation of " << PrettyMethod(method_idx, dex_file) << " took " << PrettyDuration(duration_ns); } if (compiled_method != NULL) { MethodReference ref(&dex_file, method_idx); CHECK(GetCompiledMethod(ref) == NULL) << PrettyMethod(method_idx, dex_file); compiled_methods_[ref] = compiled_method; DCHECK(GetCompiledMethod(ref) != NULL) << PrettyMethod(method_idx, dex_file); } const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx)); bool is_static = (access_flags & kAccStatic) != 0; const CompiledInvokeStub* compiled_invoke_stub = FindInvokeStub(is_static, shorty); if (compiled_invoke_stub == NULL) { if (instruction_set_ == kX86) { compiled_invoke_stub = ::art::x86::X86CreateInvokeStub(is_static, shorty); } else { CHECK(instruction_set_ == kArm || instruction_set_ == kThumb2); // Generates invocation stub using ARM instruction set compiled_invoke_stub = ::art::arm::ArmCreateInvokeStub(is_static, shorty); } CHECK(compiled_invoke_stub != NULL); InsertInvokeStub(is_static, shorty, compiled_invoke_stub); } CHECK(!Thread::Current()->IsExceptionPending()) << PrettyMethod(method_idx, dex_file); } static std::string MakeInvokeStubKey(bool is_static, const char* shorty) { std::string key(shorty); if (is_static) { key += "$"; // Must not be a shorty type character. } return key; } const CompiledInvokeStub* Compiler::FindInvokeStub(bool is_static, const char* shorty) const { const std::string key(MakeInvokeStubKey(is_static, shorty)); InvokeStubTable::const_iterator it = compiled_invoke_stubs_.find(key); if (it == compiled_invoke_stubs_.end()) { return NULL; } else { DCHECK(it->second != NULL); return it->second; } } void Compiler::InsertInvokeStub(bool is_static, const char* shorty, const CompiledInvokeStub* compiled_invoke_stub) { std::string key(MakeInvokeStubKey(is_static, shorty)); compiled_invoke_stubs_[key] = compiled_invoke_stub; } CompiledClass* Compiler::GetCompiledClass(ClassReference ref) const { ClassTable::const_iterator it = compiled_classes_.find(ref); if (it == compiled_classes_.end()) { return NULL; } CHECK(it->second != NULL); return it->second; } CompiledMethod* Compiler::GetCompiledMethod(MethodReference ref) const { MethodTable::const_iterator it = compiled_methods_.find(ref); if (it == compiled_methods_.end()) { return NULL; } CHECK(it->second != NULL); return it->second; } void Compiler::SetGcMaps(const ClassLoader* class_loader, const std::vector& dex_files) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); SetGcMapsDexFile(class_loader, *dex_file); } } void Compiler::SetGcMapsDexFile(const ClassLoader* class_loader, const DexFile& dex_file) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); DexCache* dex_cache = class_linker->FindDexCache(dex_file); for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs(); class_def_index++) { const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); const char* descriptor = dex_file.GetClassDescriptor(class_def); Class* klass = class_linker->FindClass(descriptor, class_loader); if (klass == NULL || !klass->IsVerified()) { Thread::Current()->ClearException(); continue; } const byte* class_data = dex_file.GetClassData(class_def); if (class_data == NULL) { // empty class such as a marker interface continue; } ClassDataItemIterator it(dex_file, class_data); while (it.HasNextStaticField()) { it.Next(); } while (it.HasNextInstanceField()) { it.Next(); } while (it.HasNextDirectMethod()) { Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache, class_loader, true); SetGcMapsMethod(dex_file, method); it.Next(); } while (it.HasNextVirtualMethod()) { Method* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), dex_cache, class_loader, false); SetGcMapsMethod(dex_file, method); it.Next(); } } } void Compiler::SetGcMapsMethod(const DexFile& dex_file, Method* method) { if (method == NULL) { Thread::Current()->ClearException(); return; } uint16_t method_idx = method->GetDexMethodIndex(); MethodReference ref(&dex_file, method_idx); CompiledMethod* compiled_method = GetCompiledMethod(ref); if (compiled_method == NULL) { return; } const std::vector* gc_map = verifier::DexVerifier::GetGcMap(ref); if (gc_map == NULL) { return; } compiled_method->SetGcMap(*gc_map); } void Compiler::SetCodeAndDirectMethods(const std::vector& dex_files) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); SetCodeAndDirectMethodsDexFile(*dex_file); } } void Compiler::SetCodeAndDirectMethodsDexFile(const DexFile& dex_file) { Runtime* runtime = Runtime::Current(); ClassLinker* class_linker = runtime->GetClassLinker(); DexCache* dex_cache = class_linker->FindDexCache(dex_file); CodeAndDirectMethods* code_and_direct_methods = dex_cache->GetCodeAndDirectMethods(); for (size_t i = 0; i < dex_cache->NumResolvedMethods(); i++) { Method* method = dex_cache->GetResolvedMethod(i); if (method == NULL || method->IsDirect()) { Runtime::TrampolineType type = Runtime::GetTrampolineType(method); ByteArray* res_trampoline = runtime->GetResolutionStubArray(type); code_and_direct_methods->SetResolvedDirectMethodTrampoline(i, res_trampoline); } else { // TODO: we currently leave the entry blank for resolved // non-direct methods. we could put in an error stub. } } } } // namespace art