From 6f28d91aab952e3244fbb4e707fa38f85538f374 Mon Sep 17 00:00:00 2001 From: Anwar Ghuloum Date: Wed, 24 Jul 2013 15:02:53 -0700 Subject: Add systrace support to NewTimingLogger, migrate compiler timing logging to NewTimingLogger Rpleaced old TimingLogger by NewTimingLogger, renamed NewTimingLogger to TimingLogger, added systrace support to TimingLogger. Tests passing, phone booting, systrace working. Change-Id: I2aeffb8bcb7f0fd979d8a2a3a8bcfbaa02413679 --- compiler/driver/compiler_driver.cc | 41 +++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 20 deletions(-) (limited to 'compiler/driver/compiler_driver.cc') diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c2a1312354..2aa2a98efb 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -495,7 +495,7 @@ const std::vector* CompilerDriver::CreateInterpreterToQuickEntry() cons void CompilerDriver::CompileAll(jobject class_loader, const std::vector& dex_files, - TimingLogger& timings) { + base::TimingLogger& timings) { DCHECK(!Runtime::Current()->IsStarted()); UniquePtr thread_pool(new ThreadPool(thread_count_)); PreCompile(class_loader, dex_files, *thread_pool.get(), timings); @@ -528,7 +528,7 @@ static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader, return klass->IsVerified(); } -void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, TimingLogger& timings) { +void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, base::TimingLogger& timings) { DCHECK(!Runtime::Current()->IsStarted()); Thread* self = Thread::Current(); jobject jclass_loader; @@ -572,7 +572,7 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, TimingLogg } void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -581,7 +581,7 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { LoadImageClasses(timings); Resolve(class_loader, dex_files, thread_pool, timings); @@ -666,12 +666,13 @@ static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) } // Make a list of descriptors for classes to include in the image -void CompilerDriver::LoadImageClasses(TimingLogger& timings) +void CompilerDriver::LoadImageClasses(base::TimingLogger& timings) LOCKS_EXCLUDED(Locks::mutator_lock_) { if (image_classes_.get() == NULL) { return; } + timings.NewSplit("LoadImageClasses"); // Make a first class to load all classes explicitly listed in the file Thread* self = Thread::Current(); ScopedObjectAccess soa(self); @@ -726,7 +727,6 @@ void CompilerDriver::LoadImageClasses(TimingLogger& timings) class_linker->VisitClasses(RecordImageClassesVisitor, image_classes_.get()); CHECK_NE(image_classes_->size(), 0U); - timings.AddSplit("LoadImageClasses"); } static void MaybeAddToImageClasses(mirror::Class* klass, CompilerDriver::DescriptorSet* image_classes) @@ -758,11 +758,13 @@ void CompilerDriver::FindClinitImageClassesCallback(mirror::Object* object, void MaybeAddToImageClasses(object->GetClass(), compiler_driver->image_classes_.get()); } -void CompilerDriver::UpdateImageClasses(TimingLogger& timings) { +void CompilerDriver::UpdateImageClasses(base::TimingLogger& timings) { if (image_classes_.get() == NULL) { return; } + timings.NewSplit("UpdateImageClasses"); + // Update image_classes_ with classes for objects created by methods. Thread* self = Thread::Current(); const char* old_cause = self->StartAssertNoThreadSuspension("ImageWriter"); @@ -772,7 +774,6 @@ void CompilerDriver::UpdateImageClasses(TimingLogger& timings) { heap->FlushAllocStack(); heap->GetLiveBitmap()->Walk(FindClinitImageClassesCallback, this); self->EndAssertNoThreadSuspension(old_cause); - timings.AddSplit("UpdateImageClasses"); } void CompilerDriver::RecordClassStatus(ClassReference ref, CompiledClass* compiled_class) { @@ -1551,22 +1552,22 @@ static void ResolveType(const ParallelCompilationManager* manager, size_t type_i } void CompilerDriver::ResolveDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); // TODO: we could resolve strings here, although the string table is largely filled with class // and method names. + timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " Types").c_str())); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumTypeIds(), ResolveType, thread_count_); - timings.AddSplit("Resolve " + dex_file.GetLocation() + " Types"); + timings.NewSplit(strdup(("Resolve " + dex_file.GetLocation() + " MethodsAndFields").c_str())); context.ForAll(0, dex_file.NumClassDefs(), ResolveClassFieldsAndMethods, thread_count_); - timings.AddSplit("Resolve " + dex_file.GetLocation() + " MethodsAndFields"); } void CompilerDriver::Verify(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -1620,11 +1621,11 @@ static void VerifyClass(const ParallelCompilationManager* manager, size_t class_ } void CompilerDriver::VerifyDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { + timings.NewSplit(strdup(("Verify " + dex_file.GetLocation()).c_str())); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), VerifyClass, thread_count_); - timings.AddSplit("Verify " + dex_file.GetLocation()); } static const char* class_initializer_black_list[] = { @@ -2116,7 +2117,8 @@ static void InitializeClass(const ParallelCompilationManager* manager, size_t cl } void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { + timings.NewSplit(strdup(("InitializeNoClinit " + dex_file.GetLocation()).c_str())); #ifndef NDEBUG for (size_t i = 0; i < arraysize(class_initializer_black_list); ++i) { const char* descriptor = class_initializer_black_list[i]; @@ -2126,12 +2128,11 @@ void CompilerDriver::InitializeClasses(jobject jni_class_loader, const DexFile& ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); ParallelCompilationManager context(class_linker, jni_class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), InitializeClass, thread_count_); - timings.AddSplit("InitializeNoClinit " + dex_file.GetLocation()); } void CompilerDriver::InitializeClasses(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -2140,7 +2141,7 @@ void CompilerDriver::InitializeClasses(jobject class_loader, } void CompilerDriver::Compile(jobject class_loader, const std::vector& dex_files, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { for (size_t i = 0; i != dex_files.size(); ++i) { const DexFile* dex_file = dex_files[i]; CHECK(dex_file != NULL); @@ -2220,10 +2221,10 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz } void CompilerDriver::CompileDexFile(jobject class_loader, const DexFile& dex_file, - ThreadPool& thread_pool, TimingLogger& timings) { + ThreadPool& thread_pool, base::TimingLogger& timings) { + timings.NewSplit(strdup(("Compile " + dex_file.GetLocation()).c_str())); ParallelCompilationManager context(NULL, class_loader, this, &dex_file, thread_pool); context.ForAll(0, dex_file.NumClassDefs(), CompilerDriver::CompileClass, thread_count_); - timings.AddSplit("Compile " + dex_file.GetLocation()); } void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, -- cgit v1.2.3-59-g8ed1b From 4d4adb1dae07bb7421e863732ab789413a3b43f0 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Wed, 24 Jul 2013 16:14:19 +0200 Subject: Prevent verifier from creating unused compilation data. The verifier used to create data which may be unused like GC map. This is the case for non-compiled method (which are interpreted). This CL aims to optimize this. Here are the changes: - Move compilation selection to MethodVerifier::IsCandidateForCompilation. - Compiler and verifier use this method to know if a method must be compiled. - Only create compilation data while compiling using Runtime::IsCompiler. - Do not create internal structures concerning GC map, ... in Runtime::Init and Runtime::Shutdown when we are not compiling. - Checks we are compiling when accessing these structures. - Add missing destruction of MethodVerifier::safecast_map_lock_ and MethodVerifier::safecast_map_ in Runtime::Shutdown. - Call Runtime::Shutdown just before Runtime instance is destroyed to avoid a crash. - Add missing "GUARDED_BY" macro for MethodVerifier::rejected_classes_ field. - Add "has_check_casts" to avoid the safecast pass if there is no check-cast instruction. - Add "has_virtual_or_interface_invokes" to avoid the devirtualization pass if there is no invoke-virtual/range nor invoke-interface/range instructions. Bug: 9987437 Change-Id: I418ee99f63e4203409cf5b7d2c2295b22fcf24c1 --- compiler/driver/compiler_driver.cc | 14 +-- runtime/runtime.cc | 2 +- runtime/verifier/method_verifier.cc | 192 +++++++++++++++++++++++------------- runtime/verifier/method_verifier.h | 12 ++- 4 files changed, 139 insertions(+), 81 deletions(-) (limited to 'compiler/driver/compiler_driver.cc') diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 2aa2a98efb..8ce0aa9c94 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -2240,18 +2240,8 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t CHECK(compiled_method != NULL); } else if ((access_flags & kAccAbstract) != 0) { } else { - // In small mode we only compile image classes. - bool dont_compile = (Runtime::Current()->IsSmallMode() && - ((image_classes_.get() == NULL) || (image_classes_->size() == 0))); - - // Don't compile class initializers, ever. - if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { - dont_compile = true; - } else if (code_item->insns_size_in_code_units_ < Runtime::Current()->GetSmallModeMethodDexSizeLimit()) { - // Do compile small methods. - dont_compile = false; - } - if (!dont_compile) { + bool compile = verifier::MethodVerifier::IsCandidateForCompilation(code_item, access_flags); + if (compile) { CompilerFn compiler = compiler_; #ifdef ART_SEA_IR_MODE bool use_sea = Runtime::Current()->IsSeaIRMode(); diff --git a/runtime/runtime.cc b/runtime/runtime.cc index cf6e537df0..48ee127d21 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -134,10 +134,10 @@ Runtime::~Runtime() { delete java_vm_; Thread::Shutdown(); QuasiAtomic::Shutdown(); + verifier::MethodVerifier::Shutdown(); // TODO: acquire a static mutex on Runtime to avoid racing. CHECK(instance_ == NULL || instance_ == this); instance_ = NULL; - verifier::MethodVerifier::Shutdown(); } struct AbortState { diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index e182af7b82..3549945a4c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -282,7 +282,9 @@ MethodVerifier::MethodVerifier(const DexFile* dex_file, mirror::DexCache* dex_ca new_instance_count_(0), monitor_enter_count_(0), can_load_classes_(can_load_classes), - allow_soft_failures_(allow_soft_failures) { + allow_soft_failures_(allow_soft_failures), + has_check_casts_(false), + has_virtual_or_interface_invokes_(false) { } void MethodVerifier::FindLocksAtDexPc(mirror::AbstractMethod* m, uint32_t dex_pc, @@ -470,6 +472,13 @@ bool MethodVerifier::ComputeWidthsAndCountOps() { new_instance_count++; } else if (opcode == Instruction::MONITOR_ENTER) { monitor_enter_count++; + } else if (opcode == Instruction::CHECK_CAST) { + has_check_casts_ = true; + } else if ((inst->Opcode() == Instruction::INVOKE_VIRTUAL) || + (inst->Opcode() == Instruction::INVOKE_VIRTUAL_RANGE) || + (inst->Opcode() == Instruction::INVOKE_INTERFACE) || + (inst->Opcode() == Instruction::INVOKE_INTERFACE_RANGE)) { + has_virtual_or_interface_invokes_ = true; } size_t inst_size = inst->SizeInCodeUnits(); insn_flags_[dex_pc].SetLengthInCodeUnits(inst_size); @@ -1002,27 +1011,37 @@ bool MethodVerifier::VerifyCodeFlow() { return false; } - /* Generate a register map and add it to the method. */ - UniquePtr > map(GenerateGcMap()); - if (map.get() == NULL) { - DCHECK_NE(failures_.size(), 0U); - return false; // Not a real failure, but a failure to encode - } - if (kIsDebugBuild) { - VerifyGcMap(*map); - } - MethodReference ref(dex_file_, dex_method_idx_); - const std::vector* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get())); - verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map); + // Compute information for compiler. + if (Runtime::Current()->IsCompiler()) { + MethodReference ref(dex_file_, dex_method_idx_); + bool compile = IsCandidateForCompilation(code_item_, method_access_flags_); + if (compile) { + /* Generate a register map and add it to the method. */ + UniquePtr > map(GenerateGcMap()); + if (map.get() == NULL) { + DCHECK_NE(failures_.size(), 0U); + return false; // Not a real failure, but a failure to encode + } + if (kIsDebugBuild) { + VerifyGcMap(*map); + } + const std::vector* dex_gc_map = CreateLengthPrefixedDexGcMap(*(map.get())); + verifier::MethodVerifier::SetDexGcMap(ref, *dex_gc_map); + } - MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(); - if (method_to_safe_casts != NULL) { - SetSafeCastMap(ref, method_to_safe_casts); - } + if (has_check_casts_) { + MethodVerifier::MethodSafeCastSet* method_to_safe_casts = GenerateSafeCastSet(); + if(method_to_safe_casts != NULL ) { + SetSafeCastMap(ref, method_to_safe_casts); + } + } - MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(); - if (pc_to_concrete_method != NULL) { - SetDevirtMap(ref, pc_to_concrete_method); + if (has_virtual_or_interface_invokes_) { + MethodVerifier::PcToConcreteMethodMap* pc_to_concrete_method = GenerateDevirtMap(); + if(pc_to_concrete_method != NULL ) { + SetDevirtMap(ref, pc_to_concrete_method); + } + } } return true; } @@ -3948,6 +3967,7 @@ void MethodVerifier::VerifyGcMap(const std::vector& data) { } void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector& gc_map) { + DCHECK(Runtime::Current()->IsCompiler()); { WriterMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); DexGcMapTable::iterator it = dex_gc_maps_->find(ref); @@ -3962,6 +3982,7 @@ void MethodVerifier::SetDexGcMap(MethodReference ref, const std::vector void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSet* cast_set) { + DCHECK(Runtime::Current()->IsCompiler()); MutexLock mu(Thread::Current(), *safecast_map_lock_); SafeCastMap::iterator it = safecast_map_->find(ref); if (it != safecast_map_->end()) { @@ -3970,10 +3991,11 @@ void MethodVerifier::SetSafeCastMap(MethodReference ref, const MethodSafeCastSe } safecast_map_->Put(ref, cast_set); - CHECK(safecast_map_->find(ref) != safecast_map_->end()); + DCHECK(safecast_map_->find(ref) != safecast_map_->end()); } bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) { + DCHECK(Runtime::Current()->IsCompiler()); MutexLock mu(Thread::Current(), *safecast_map_lock_); SafeCastMap::const_iterator it = safecast_map_->find(ref); if (it == safecast_map_->end()) { @@ -3986,6 +4008,7 @@ bool MethodVerifier::IsSafeCast(MethodReference ref, uint32_t pc) { } const std::vector* MethodVerifier::GetDexGcMap(MethodReference ref) { + DCHECK(Runtime::Current()->IsCompiler()); ReaderMutexLock mu(Thread::Current(), *dex_gc_maps_lock_); DexGcMapTable::const_iterator it = dex_gc_maps_->find(ref); if (it == dex_gc_maps_->end()) { @@ -3998,6 +4021,7 @@ const std::vector* MethodVerifier::GetDexGcMap(MethodReference ref) { void MethodVerifier::SetDevirtMap(MethodReference ref, const PcToConcreteMethodMap* devirt_map) { + DCHECK(Runtime::Current()->IsCompiler()); WriterMutexLock mu(Thread::Current(), *devirt_maps_lock_); DevirtualizationMapTable::iterator it = devirt_maps_->find(ref); if (it != devirt_maps_->end()) { @@ -4006,11 +4030,12 @@ void MethodVerifier::SetDevirtMap(MethodReference ref, } devirt_maps_->Put(ref, devirt_map); - CHECK(devirt_maps_->find(ref) != devirt_maps_->end()); + DCHECK(devirt_maps_->find(ref) != devirt_maps_->end()); } const MethodReference* MethodVerifier::GetDevirtMap(const MethodReference& ref, uint32_t dex_pc) { + DCHECK(Runtime::Current()->IsCompiler()); ReaderMutexLock mu(Thread::Current(), *devirt_maps_lock_); DevirtualizationMapTable::const_iterator it = devirt_maps_->find(ref); if (it == devirt_maps_->end()) { @@ -4070,6 +4095,24 @@ std::vector MethodVerifier::DescribeVRegs(uint32_t dex_pc) { return result; } +bool MethodVerifier::IsCandidateForCompilation(const DexFile::CodeItem* code_item, + const uint32_t access_flags) { + // Don't compile class initializers, ever. + if (((access_flags & kAccConstructor) != 0) && ((access_flags & kAccStatic) != 0)) { + return false; + } + + const Runtime* runtime = Runtime::Current(); + if (runtime->IsSmallMode() && runtime->UseCompileTimeClassPath()) { + // In Small mode, we only compile small methods. + const uint32_t code_size = code_item->insns_size_in_code_units_; + return (code_size < runtime->GetSmallModeMethodDexSizeLimit()); + } else { + // In normal mode, we compile everything. + return true; + } +} + ReaderWriterMutex* MethodVerifier::dex_gc_maps_lock_ = NULL; MethodVerifier::DexGcMapTable* MethodVerifier::dex_gc_maps_ = NULL; @@ -4083,65 +4126,79 @@ Mutex* MethodVerifier::rejected_classes_lock_ = NULL; MethodVerifier::RejectedClassesTable* MethodVerifier::rejected_classes_ = NULL; void MethodVerifier::Init() { - dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock"); - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *dex_gc_maps_lock_); - dex_gc_maps_ = new MethodVerifier::DexGcMapTable; - } + if (Runtime::Current()->IsCompiler()) { + dex_gc_maps_lock_ = new ReaderWriterMutex("verifier GC maps lock"); + Thread* self = Thread::Current(); + { + WriterMutexLock mu(self, *dex_gc_maps_lock_); + dex_gc_maps_ = new MethodVerifier::DexGcMapTable; + } - safecast_map_lock_ = new Mutex("verifier Cast Elision lock"); - { - MutexLock mu(self, *safecast_map_lock_); - safecast_map_ = new MethodVerifier::SafeCastMap(); - } + safecast_map_lock_ = new Mutex("verifier Cast Elision lock"); + { + MutexLock mu(self, *safecast_map_lock_); + safecast_map_ = new MethodVerifier::SafeCastMap(); + } - devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock"); + devirt_maps_lock_ = new ReaderWriterMutex("verifier Devirtualization lock"); - { - WriterMutexLock mu(self, *devirt_maps_lock_); - devirt_maps_ = new MethodVerifier::DevirtualizationMapTable(); - } + { + WriterMutexLock mu(self, *devirt_maps_lock_); + devirt_maps_ = new MethodVerifier::DevirtualizationMapTable(); + } - rejected_classes_lock_ = new Mutex("verifier rejected classes lock"); - { - MutexLock mu(self, *rejected_classes_lock_); - rejected_classes_ = new MethodVerifier::RejectedClassesTable; + rejected_classes_lock_ = new Mutex("verifier rejected classes lock"); + { + MutexLock mu(self, *rejected_classes_lock_); + rejected_classes_ = new MethodVerifier::RejectedClassesTable; + } } art::verifier::RegTypeCache::Init(); } void MethodVerifier::Shutdown() { - Thread* self = Thread::Current(); - { - WriterMutexLock mu(self, *dex_gc_maps_lock_); - STLDeleteValues(dex_gc_maps_); - delete dex_gc_maps_; - dex_gc_maps_ = NULL; - } - delete dex_gc_maps_lock_; - dex_gc_maps_lock_ = NULL; - - { - WriterMutexLock mu(self, *devirt_maps_lock_); - STLDeleteValues(devirt_maps_); - delete devirt_maps_; - devirt_maps_ = NULL; - } - delete devirt_maps_lock_; - devirt_maps_lock_ = NULL; - - { - MutexLock mu(self, *rejected_classes_lock_); - delete rejected_classes_; - rejected_classes_ = NULL; + if (Runtime::Current()->IsCompiler()) { + Thread* self = Thread::Current(); + { + WriterMutexLock mu(self, *dex_gc_maps_lock_); + STLDeleteValues(dex_gc_maps_); + delete dex_gc_maps_; + dex_gc_maps_ = NULL; + } + delete dex_gc_maps_lock_; + dex_gc_maps_lock_ = NULL; + + { + MutexLock mu(self, *safecast_map_lock_); + STLDeleteValues(safecast_map_); + delete safecast_map_; + safecast_map_ = NULL; + } + delete safecast_map_lock_; + safecast_map_lock_ = NULL; + + { + WriterMutexLock mu(self, *devirt_maps_lock_); + STLDeleteValues(devirt_maps_); + delete devirt_maps_; + devirt_maps_ = NULL; + } + delete devirt_maps_lock_; + devirt_maps_lock_ = NULL; + + { + MutexLock mu(self, *rejected_classes_lock_); + delete rejected_classes_; + rejected_classes_ = NULL; + } + delete rejected_classes_lock_; + rejected_classes_lock_ = NULL; } - delete rejected_classes_lock_; - rejected_classes_lock_ = NULL; verifier::RegTypeCache::ShutDown(); } void MethodVerifier::AddRejectedClass(ClassReference ref) { + DCHECK(Runtime::Current()->IsCompiler()); { MutexLock mu(Thread::Current(), *rejected_classes_lock_); rejected_classes_->insert(ref); @@ -4150,6 +4207,7 @@ void MethodVerifier::AddRejectedClass(ClassReference ref) { } bool MethodVerifier::IsClassRejected(ClassReference ref) { + DCHECK(Runtime::Current()->IsCompiler()); MutexLock mu(Thread::Current(), *rejected_classes_lock_); return (rejected_classes_->find(ref) != rejected_classes_->end()); } diff --git a/runtime/verifier/method_verifier.h b/runtime/verifier/method_verifier.h index 57d630de5a..fdd90cc767 100644 --- a/runtime/verifier/method_verifier.h +++ b/runtime/verifier/method_verifier.h @@ -237,6 +237,9 @@ class MethodVerifier { // Describe VRegs at the given dex pc. std::vector DescribeVRegs(uint32_t dex_pc); + static bool IsCandidateForCompilation(const DexFile::CodeItem* code_item, + const uint32_t access_flags); + private: // Adds the given string to the beginning of the last failure message. void PrependToLastFailMessage(std::string); @@ -654,7 +657,7 @@ class MethodVerifier { LOCKS_EXCLUDED(devirt_maps_lock_); typedef std::set RejectedClassesTable; static Mutex* rejected_classes_lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - static RejectedClassesTable* rejected_classes_; + static RejectedClassesTable* rejected_classes_ GUARDED_BY(rejected_classes_lock_); static void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); @@ -717,6 +720,13 @@ class MethodVerifier { // Converts soft failures to hard failures when false. Only false when the compiler isn't // running and the verifier is called from the class linker. const bool allow_soft_failures_; + + // Indicates if the method being verified contains at least one check-cast instruction. + bool has_check_casts_; + + // Indicates if the method being verified contains at least one invoke-virtual/range + // or invoke-interface/range. + bool has_virtual_or_interface_invokes_; }; std::ostream& operator<<(std::ostream& os, const MethodVerifier::FailureKind& rhs); -- cgit v1.2.3-59-g8ed1b From 75021222d9c03a80fa5c136db0d5fb8d82d04031 Mon Sep 17 00:00:00 2001 From: Sebastien Hertz Date: Tue, 16 Jul 2013 18:34:50 +0200 Subject: Adds a DEX-to-DEX compilation level. This CL adds a DEX-to-DEX compilation level which allows the DEX-to-DEX compiler to ensure correctness on classes with soft-failed verification. Bug: 9307738 Change-Id: If051336bf81370bca55872c8c75ccd573d8ca391 --- compiler/dex/dex_to_dex_compiler.cc | 35 ++++++++++++-------- compiler/driver/compiler_driver.cc | 64 +++++++++++++++++++++---------------- compiler/driver/compiler_driver.h | 17 ++++++++-- 3 files changed, 74 insertions(+), 42 deletions(-) (limited to 'compiler/driver/compiler_driver.cc') diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 3c491ce20f..1ee29cbae1 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -36,9 +36,11 @@ const bool kEnableCheckCastEllision = true; class DexCompiler { public: DexCompiler(art::CompilerDriver& compiler, - const DexCompilationUnit& unit) + const DexCompilationUnit& unit, + DexToDexCompilationLevel dex_to_dex_compilation_level) : driver_(compiler), - unit_(unit) {} + unit_(unit), + dex_to_dex_compilation_level_(dex_to_dex_compilation_level) {} ~DexCompiler() {} @@ -55,6 +57,10 @@ class DexCompiler { return *const_cast(unit_.GetDexFile()); } + bool PerformOptimizations() const { + return dex_to_dex_compilation_level_ >= kOptimize; + } + // Compiles a RETURN-VOID into a RETURN-VOID-BARRIER within a constructor where // a barrier is required. void CompileReturnVoid(Instruction* inst, uint32_t dex_pc); @@ -84,6 +90,7 @@ class DexCompiler { CompilerDriver& driver_; const DexCompilationUnit& unit_; + const DexToDexCompilationLevel dex_to_dex_compilation_level_; DISALLOW_COPY_AND_ASSIGN(DexCompiler); }; @@ -138,6 +145,7 @@ class ScopedDexWriteAccess { }; void DexCompiler::Compile() { + DCHECK_GE(dex_to_dex_compilation_level_, kRequired); const DexFile::CodeItem* code_item = unit_.GetCodeItem(); const uint16_t* insns = code_item->insns_; const uint32_t insns_size = code_item->insns_size_in_code_units_; @@ -220,7 +228,7 @@ void DexCompiler::CompileReturnVoid(Instruction* inst, uint32_t dex_pc) { } Instruction* DexCompiler::CompileCheckCast(Instruction* inst, uint32_t dex_pc) { - if (!kEnableCheckCastEllision) { + if (!kEnableCheckCastEllision || !PerformOptimizations()) { return inst; } MethodReference referrer(&GetDexFile(), unit_.GetDexMethodIndex()); @@ -253,7 +261,7 @@ void DexCompiler::CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, Instruction::Code new_opcode, bool is_put) { - if (!kEnableQuickening) { + if (!kEnableQuickening || !PerformOptimizations()) { return; } uint32_t field_idx = inst->VRegC_22c(); @@ -280,7 +288,7 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, Instruction::Code new_opcode, bool is_range) { - if (!kEnableQuickening) { + if (!kEnableQuickening || !PerformOptimizations()) { return; } uint32_t method_idx = is_range ? inst->VRegB_3rc() : inst->VRegB_35c(); @@ -320,14 +328,15 @@ void DexCompiler::CompileInvokeVirtual(Instruction* inst, } // namespace optimizer } // namespace art -extern "C" art::CompiledMethod* - ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, +extern "C" void ArtCompileDEX(art::CompilerDriver& compiler, const art::DexFile::CodeItem* code_item, uint32_t access_flags, art::InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, - const art::DexFile& dex_file) { - art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), - dex_file, code_item, class_def_idx, method_idx, access_flags); - art::optimizer::DexCompiler dex_compiler(compiler, unit); - dex_compiler.Compile(); - return NULL; + const art::DexFile& dex_file, + art::DexToDexCompilationLevel dex_to_dex_compilation_level) { + if (dex_to_dex_compilation_level != art::kDontDexToDexCompile) { + art::DexCompilationUnit unit(NULL, class_loader, art::Runtime::Current()->GetClassLinker(), + dex_file, code_item, class_def_idx, method_idx, access_flags); + art::optimizer::DexCompiler dex_compiler(compiler, unit, dex_to_dex_compilation_level); + dex_compiler.Compile(); + } } diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 49aba4d6a9..38d00a0804 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -369,7 +369,7 @@ CompilerDriver::CompilerDriver(CompilerBackend compiler_backend, InstructionSet compiler_ = reinterpret_cast(ArtQuickCompileMethod); } - dex_to_dex_compiler_ = reinterpret_cast(ArtCompileDEX); + dex_to_dex_compiler_ = reinterpret_cast(ArtCompileDEX); #ifdef ART_SEA_IR_MODE sea_ir_compiler_ = NULL; @@ -505,16 +505,10 @@ void CompilerDriver::CompileAll(jobject class_loader, } } -static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader, - const DexFile& dex_file, - const DexFile::ClassDef& class_def) +static DexToDexCompilationLevel GetDexToDexCompilationlevel(mirror::ClassLoader* class_loader, + const DexFile& dex_file, + const DexFile::ClassDef& class_def) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - // Do not allow DEX-to-DEX compilation of image classes. This is to prevent the - // verifier from passing on "quick" instruction at compilation time. It must - // only pass on quick instructions at runtime. - if (class_loader == NULL) { - return false; - } const char* descriptor = dex_file.GetClassDescriptor(class_def); ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); mirror::Class* klass = class_linker->FindClass(descriptor, class_loader); @@ -522,10 +516,27 @@ static bool IsDexToDexCompilationAllowed(mirror::ClassLoader* class_loader, Thread* self = Thread::Current(); CHECK(self->IsExceptionPending()); self->ClearException(); - return false; + return kDontDexToDexCompile; + } + // The verifier can only run on "quick" instructions at runtime (see usage of + // FindAccessedFieldAtDexPc and FindInvokedMethodAtDexPc in ThrowNullPointerExceptionFromDexPC + // function). Since image classes can be verified again while compiling an application, + // we must prevent the DEX-to-DEX compiler from introducing them. + // TODO: find a way to enable "quick" instructions for image classes and remove this check. + bool compiling_image_classes = (class_loader == NULL); + if (compiling_image_classes) { + return kRequired; + } else if (klass->IsVerified()) { + // Class is verified so we can enable DEX-to-DEX compilation for performance. + return kOptimize; + } else if (klass->IsCompileTimeVerified()) { + // Class verification has soft-failed. Anyway, ensure at least correctness. + DCHECK_EQ(klass->GetStatus(), mirror::Class::kStatusRetryVerificationAtRuntime); + return kRequired; + } else { + // Class verification has failed: do not run DEX-to-DEX compilation. + return kDontDexToDexCompile; } - // DEX-to-DEX compilation is only allowed on preverified classes. - return klass->IsVerified(); } void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, base::TimingLogger& timings) { @@ -556,15 +567,15 @@ void CompilerDriver::CompileOne(const mirror::AbstractMethod* method, base::Timi uint32_t method_idx = method->GetDexMethodIndex(); const DexFile::CodeItem* code_item = dex_file->GetCodeItem(method->GetCodeItemOffset()); // Can we run DEX-to-DEX compiler on this class ? - bool allow_dex_compilation; + DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile; { ScopedObjectAccess soa(Thread::Current()); const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_idx); mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); - allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, *dex_file, class_def); + dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, *dex_file, class_def); } CompileMethod(code_item, method->GetAccessFlags(), method->GetInvokeType(), - class_def_idx, method_idx, jclass_loader, *dex_file, allow_dex_compilation); + class_def_idx, method_idx, jclass_loader, *dex_file, dex_to_dex_compilation_level); self->GetJniEnv()->DeleteGlobalRef(jclass_loader); @@ -2171,11 +2182,11 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz return; } // Can we run DEX-to-DEX compiler on this class ? - bool allow_dex_compilation; + DexToDexCompilationLevel dex_to_dex_compilation_level = kDontDexToDexCompile; { ScopedObjectAccess soa(Thread::Current()); mirror::ClassLoader* class_loader = soa.Decode(jclass_loader); - allow_dex_compilation = IsDexToDexCompilationAllowed(class_loader, dex_file, class_def); + dex_to_dex_compilation_level = GetDexToDexCompilationlevel(class_loader, dex_file, class_def); } ClassDataItemIterator it(dex_file, class_data); // Skip fields @@ -2198,7 +2209,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_direct_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, jclass_loader, dex_file, allow_dex_compilation); + method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level); it.Next(); } // Compile virtual methods @@ -2214,7 +2225,7 @@ void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, siz previous_virtual_method_idx = method_idx; manager->GetCompiler()->CompileMethod(it.GetMethodCodeItem(), it.GetMemberAccessFlags(), it.GetMethodInvokeType(class_def), class_def_index, - method_idx, jclass_loader, dex_file, allow_dex_compilation); + method_idx, jclass_loader, dex_file, dex_to_dex_compilation_level); it.Next(); } DCHECK(!it.HasNext()); @@ -2231,7 +2242,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, - bool allow_dex_to_dex_compilation) { + DexToDexCompilationLevel dex_to_dex_compilation_level) { CompiledMethod* compiled_method = NULL; uint64_t start_ns = NanoTime(); @@ -2253,13 +2264,12 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t compiled_method = (*compiler)(*this, code_item, access_flags, invoke_type, class_def_idx, method_idx, class_loader, dex_file); CHECK(compiled_method != NULL) << PrettyMethod(method_idx, dex_file); - } else if (allow_dex_to_dex_compilation) { + } else if (dex_to_dex_compilation_level != kDontDexToDexCompile) { // TODO: add a mode to disable DEX-to-DEX compilation ? - compiled_method = (*dex_to_dex_compiler_)(*this, code_item, access_flags, - invoke_type, class_def_idx, - method_idx, class_loader, dex_file); - // No native code is generated. - CHECK(compiled_method == NULL) << PrettyMethod(method_idx, dex_file); + (*dex_to_dex_compiler_)(*this, code_item, access_flags, + invoke_type, class_def_idx, + method_idx, class_loader, dex_file, + dex_to_dex_compilation_level); } } uint64_t duration_ns = NanoTime() - start_ns; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index a7a47ed876..18f852dc6f 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -48,6 +48,12 @@ enum CompilerBackend { kNoBackend }; +enum DexToDexCompilationLevel { + kDontDexToDexCompile, // Only meaning wrt image time interpretation. + kRequired, // Dex-to-dex compilation required for correctness. + kOptimize // Perform required transformation and peep-hole optimizations. +}; + // Thread-local storage compiler worker threads class CompilerTls { public: @@ -324,7 +330,7 @@ class CompilerDriver { void CompileMethod(const DexFile::CodeItem* code_item, uint32_t access_flags, InvokeType invoke_type, uint32_t class_def_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file, - bool allow_dex_to_dex_compilation) + DexToDexCompilationLevel dex_to_dex_compilation_level) LOCKS_EXCLUDED(compiled_methods_lock_); static void CompileClass(const ParallelCompilationManager* context, size_t class_def_index) @@ -375,12 +381,19 @@ class CompilerDriver { uint32_t access_flags, InvokeType invoke_type, uint32_t class_dex_idx, uint32_t method_idx, jobject class_loader, const DexFile& dex_file); + + typedef void (*DexToDexCompilerFn)(CompilerDriver& driver, + const DexFile::CodeItem* code_item, + uint32_t access_flags, InvokeType invoke_type, + uint32_t class_dex_idx, uint32_t method_idx, + jobject class_loader, const DexFile& dex_file, + DexToDexCompilationLevel dex_to_dex_compilation_level); CompilerFn compiler_; #ifdef ART_SEA_IR_MODE CompilerFn sea_ir_compiler_; #endif - CompilerFn dex_to_dex_compiler_; + DexToDexCompilerFn dex_to_dex_compiler_; void* compiler_context_; -- cgit v1.2.3-59-g8ed1b