diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 03c94a45e3e..d87762d4174 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -80,15 +80,6 @@ const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref return (it != verified_methods_.end()) ? it->second : nullptr; } -void VerificationResults::RemoveVerifiedMethod(MethodReference ref) { - WriterMutexLock mu(Thread::Current(), verified_methods_lock_); - auto it = verified_methods_.find(ref); - if (it != verified_methods_.end()) { - delete it->second; - verified_methods_.erase(it); - } -} - void VerificationResults::AddRejectedClass(ClassReference ref) { { WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index da80bf07dbd..1af11a8114f 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -48,7 +48,6 @@ class VerificationResults { const VerifiedMethod* GetVerifiedMethod(MethodReference ref) REQUIRES(!verified_methods_lock_); - void RemoveVerifiedMethod(MethodReference ref) REQUIRES(!verified_methods_lock_); void AddRejectedClass(ClassReference ref) REQUIRES(!rejected_classes_lock_); bool IsClassRejected(ClassReference ref) REQUIRES(!rejected_classes_lock_); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 4594e2cc42a..e366e071f57 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -26,6 +26,7 @@ #include "art_field-inl.h" #include "art_method-inl.h" +#include "base/bit_vector.h" #include "base/stl_util.h" #include "base/systrace.h" #include "base/time_utils.h" @@ -66,6 +67,7 @@ #include "thread_pool.h" #include "trampolines/trampoline_compiler.h" #include "transaction.h" +#include "utils/array_ref.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "utils/swap_space.h" #include "verifier/method_verifier.h" @@ -333,6 +335,24 @@ class CompilerDriver::AOTCompilationStats { DISALLOW_COPY_AND_ASSIGN(AOTCompilationStats); }; +class CompilerDriver::DexFileMethodSet { + public: + explicit DexFileMethodSet(const DexFile& dex_file) + : dex_file_(dex_file), + method_indexes_(dex_file.NumMethodIds(), false, Allocator::GetMallocAllocator()) { + } + DexFileMethodSet(DexFileMethodSet&& other) = default; + + const DexFile& GetDexFile() const { return dex_file_; } + + BitVector& GetMethodIndexes() { return method_indexes_; } + const BitVector& GetMethodIndexes() const { return method_indexes_; } + + private: + const DexFile& dex_file_; + BitVector method_indexes_; +}; + CompilerDriver::CompilerDriver( const CompilerOptions* compiler_options, VerificationResults* verification_results, @@ -379,7 +399,10 @@ CompilerDriver::CompilerDriver( dex_files_for_oat_file_(nullptr), compiled_method_storage_(swap_fd), profile_compilation_info_(profile_compilation_info), - max_arena_alloc_(0) { + max_arena_alloc_(0), + dex_to_dex_references_lock_("dex-to-dex references lock"), + dex_to_dex_references_(), + current_dex_to_dex_methods_(nullptr) { DCHECK(compiler_options_ != nullptr); DCHECK(method_inliner_map_ != nullptr); @@ -552,7 +575,29 @@ static void CompileMethod(Thread* self, uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0; MethodReference method_ref(&dex_file, method_idx); - if ((access_flags & kAccNative) != 0) { + if (driver->GetCurrentDexToDexMethods() != nullptr) { + // This is the second pass when we dex-to-dex compile previously marked methods. + // TODO: Refactor the compilation to avoid having to distinguish the two passes + // here. That should be done on a higher level. http://b/29089975 + if (driver->GetCurrentDexToDexMethods()->IsBitSet(method_idx)) { + const VerifiedMethod* verified_method = + driver->GetVerificationResults()->GetVerifiedMethod(method_ref); + // Do not optimize if a VerifiedMethod is missing. SafeCast elision, + // for example, relies on it. + compiled_method = optimizer::ArtCompileDEX( + driver, + code_item, + access_flags, + invoke_type, + class_def_idx, + method_idx, + class_loader, + dex_file, + (verified_method != nullptr) + ? dex_to_dex_compilation_level + : optimizer::DexToDexCompilationLevel::kRequired); + } + } else if ((access_flags & kAccNative) != 0) { // Are we extracting only and have support for generic JNI down calls? if (!driver->GetCompilerOptions().IsJniCompilationEnabled() && InstructionSetHasGenericJniStub(driver->GetInstructionSet())) { @@ -588,21 +633,9 @@ static void CompileMethod(Thread* self, } if (compiled_method == nullptr && dex_to_dex_compilation_level != optimizer::DexToDexCompilationLevel::kDontDexToDexCompile) { + DCHECK(!Runtime::Current()->UseJitCompilation()); // TODO: add a command-line option to disable DEX-to-DEX compilation ? - // Do not optimize if a VerifiedMethod is missing. SafeCast elision, for example, relies on - // it. - compiled_method = optimizer::ArtCompileDEX( - driver, - code_item, - access_flags, - invoke_type, - class_def_idx, - method_idx, - class_loader, - dex_file, - (verified_method != nullptr) - ? dex_to_dex_compilation_level - : optimizer::DexToDexCompilationLevel::kRequired); + driver->MarkForDexToDexCompilation(self, method_ref); } } if (kTimeCompileMethod) { @@ -628,12 +661,6 @@ static void CompileMethod(Thread* self, driver->AddCompiledMethod(method_ref, compiled_method, non_relative_linker_patch_count); } - // Done compiling, delete the verified method to reduce native memory usage. Do not delete in - // optimizing compiler, which may need the verified method again for inlining. - if (driver->GetCompilerKind() != Compiler::kOptimizing) { - driver->GetVerificationResults()->RemoveVerifiedMethod(method_ref); - } - if (self->IsExceptionPending()) { ScopedObjectAccess soa(self); LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n" @@ -680,6 +707,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t *dex_file, dex_file->GetClassDef(class_def_idx)); + DCHECK(current_dex_to_dex_methods_ == nullptr); CompileMethod(self, this, code_item, @@ -693,6 +721,34 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t true, dex_cache); + ArrayRef dex_to_dex_references; + { + // From this point on, we shall not modify dex_to_dex_references_, so + // just grab a reference to it that we use without holding the mutex. + MutexLock lock(Thread::Current(), dex_to_dex_references_lock_); + dex_to_dex_references = ArrayRef(dex_to_dex_references_); + } + if (!dex_to_dex_references.empty()) { + DCHECK_EQ(dex_to_dex_references.size(), 1u); + DCHECK(&dex_to_dex_references[0].GetDexFile() == dex_file); + current_dex_to_dex_methods_ = &dex_to_dex_references.front().GetMethodIndexes(); + DCHECK(current_dex_to_dex_methods_->IsBitSet(method_idx)); + DCHECK_EQ(current_dex_to_dex_methods_->NumSetBits(), 1u); + CompileMethod(self, + this, + code_item, + access_flags, + invoke_type, + class_def_idx, + method_idx, + jclass_loader, + *dex_file, + dex_to_dex_compilation_level, + true, + dex_cache); + current_dex_to_dex_methods_ = nullptr; + } + FreeThreadPools(); self->GetJniEnv()->DeleteGlobalRef(jclass_loader); @@ -1285,6 +1341,17 @@ bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) { return IsImageClass(descriptor); } +void CompilerDriver::MarkForDexToDexCompilation(Thread* self, const MethodReference& method_ref) { + MutexLock lock(self, dex_to_dex_references_lock_); + // Since we're compiling one dex file at a time, we need to look for the + // current dex file entry only at the end of dex_to_dex_references_. + if (dex_to_dex_references_.empty() || + &dex_to_dex_references_.back().GetDexFile() != method_ref.dex_file) { + dex_to_dex_references_.emplace_back(*method_ref.dex_file); + } + dex_to_dex_references_.back().GetMethodIndexes().SetBit(method_ref.dex_method_index); +} + bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle dex_cache, uint32_t type_idx) { bool result = false; @@ -2496,8 +2563,9 @@ void CompilerDriver::Compile(jobject class_loader, ? "null" : profile_compilation_info_->DumpInfo(&dex_files)); } - for (size_t i = 0; i != dex_files.size(); ++i) { - const DexFile* dex_file = dex_files[i]; + + DCHECK(current_dex_to_dex_methods_ == nullptr); + for (const DexFile* dex_file : dex_files) { CHECK(dex_file != nullptr); CompileDexFile(class_loader, *dex_file, @@ -2510,6 +2578,25 @@ void CompilerDriver::Compile(jobject class_loader, max_arena_alloc_ = std::max(arena_alloc, max_arena_alloc_); Runtime::Current()->ReclaimArenaPoolMemory(); } + + ArrayRef dex_to_dex_references; + { + // From this point on, we shall not modify dex_to_dex_references_, so + // just grab a reference to it that we use without holding the mutex. + MutexLock lock(Thread::Current(), dex_to_dex_references_lock_); + dex_to_dex_references = ArrayRef(dex_to_dex_references_); + } + for (const auto& method_set : dex_to_dex_references) { + current_dex_to_dex_methods_ = &method_set.GetMethodIndexes(); + CompileDexFile(class_loader, + method_set.GetDexFile(), + dex_files, + parallel_thread_pool_.get(), + parallel_thread_count_, + timings); + } + current_dex_to_dex_methods_ = nullptr; + VLOG(compiler) << "Compile: " << GetMemoryUsageString(false); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 19a1ecc494a..2dd46514e79 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -52,6 +52,7 @@ namespace verifier { class MethodVerifier; } // namespace verifier +class BitVector; class CompiledClass; class CompiledMethod; class CompilerOptions; @@ -120,12 +121,12 @@ class CompilerDriver { void CompileAll(jobject class_loader, const std::vector& dex_files, TimingLogger* timings) - REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_); + REQUIRES(!Locks::mutator_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); // Compile a single Method. void CompileOne(Thread* self, ArtMethod* method, TimingLogger* timings) SHARED_REQUIRES(Locks::mutator_lock_) - REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_); + REQUIRES(!compiled_methods_lock_, !compiled_classes_lock_, !dex_to_dex_references_lock_); VerificationResults* GetVerificationResults() const { DCHECK(Runtime::Current()->IsAotCompiler()); @@ -475,6 +476,13 @@ class CompilerDriver { return true; } + void MarkForDexToDexCompilation(Thread* self, const MethodReference& method_ref) + REQUIRES(!dex_to_dex_references_lock_); + + const BitVector* GetCurrentDexToDexMethods() const { + return current_dex_to_dex_methods_; + } + private: // Return whether the declaring class of `resolved_member` is // available to `referrer_class` for read or write access using two @@ -601,7 +609,7 @@ class CompilerDriver { void Compile(jobject class_loader, const std::vector& dex_files, - TimingLogger* timings); + TimingLogger* timings) REQUIRES(!dex_to_dex_references_lock_); void CompileDexFile(jobject class_loader, const DexFile& dex_file, const std::vector& dex_files, @@ -702,6 +710,16 @@ class CompilerDriver { const ProfileCompilationInfo* const profile_compilation_info_; size_t max_arena_alloc_; + + // Data for delaying dex-to-dex compilation. + Mutex dex_to_dex_references_lock_; + // In the first phase, dex_to_dex_references_ collects methods for dex-to-dex compilation. + class DexFileMethodSet; + std::vector dex_to_dex_references_ GUARDED_BY(dex_to_dex_references_lock_); + // In the second phase, current_dex_to_dex_methods_ points to the BitVector with method + // indexes for dex-to-dex compilation in the current dex file. + const BitVector* current_dex_to_dex_methods_; + friend class CompileClassVisitor; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); }; diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index 424ebb70f69..56090672ce8 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -111,6 +111,20 @@ class BitVector { const BitVector* const bit_vector_; }; + // MoveConstructible but not MoveAssignable, CopyConstructible or CopyAssignable. + + BitVector(const BitVector& other) = delete; + BitVector& operator=(const BitVector& other) = delete; + + BitVector(BitVector&& other) + : storage_(other.storage_), + storage_size_(other.storage_size_), + allocator_(other.allocator_), + expandable_(other.expandable_) { + other.storage_ = nullptr; + other.storage_size_ = 0u; + } + BitVector(uint32_t start_bits, bool expandable, Allocator* allocator);