diff options
| -rw-r--r-- | compiler/compiled_method.cc | 6 | ||||
| -rw-r--r-- | compiler/compiled_method.h | 6 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_compiler.cc | 206 | ||||
| -rw-r--r-- | compiler/dex/dex_to_dex_compiler.h | 97 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.cc | 13 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.h | 1 | ||||
| -rw-r--r-- | compiler/utils/atomic_dex_ref_map-inl.h | 12 | ||||
| -rw-r--r-- | compiler/utils/atomic_dex_ref_map.h | 3 | ||||
| -rw-r--r-- | dexlayout/compact_dex_writer.cc | 5 | ||||
| -rw-r--r-- | dexlayout/compact_dex_writer.h | 5 | ||||
| -rw-r--r-- | dexlayout/dexlayout.h | 3 | ||||
| -rw-r--r-- | runtime/dex/dex_file_layout.h | 2 | ||||
| -rw-r--r-- | test/ManyMethods/ManyMethods.java | 18 |
13 files changed, 231 insertions, 146 deletions
diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 0f69dbab94..e41371855d 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -159,10 +159,4 @@ CompiledMethod::~CompiledMethod() { storage->ReleaseMethodInfo(method_info_); } -void CompiledMethod::ReleaseVMapTable() { - CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); - storage->ReleaseVMapTable(vmap_table_); - vmap_table_ = nullptr; -} - } // namespace art diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index 4e8f3efe5a..acdce260e5 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -168,10 +168,6 @@ class CompiledMethod FINAL : public CompiledCode { ArrayRef<const linker::LinkerPatch> GetPatches() const; - // The compiler sometimes unquickens shared code items. In that case, we need to clear the vmap - // table to avoid writing the quicken info to the vdex file. - void ReleaseVMapTable(); - private: static constexpr size_t kIsIntrinsicLsb = kNumberOfCompiledCodePackedBits; static constexpr size_t kIsIntrinsicSize = 1u; @@ -190,7 +186,7 @@ class CompiledMethod FINAL : public CompiledCode { // For quick code, method specific information that is not very dedupe friendly (method indices). const LengthPrefixedArray<uint8_t>* const method_info_; // For quick code, holds code infos which contain stack maps, inline information, and etc. - const LengthPrefixedArray<uint8_t>* vmap_table_; + const LengthPrefixedArray<uint8_t>* const vmap_table_; // For quick code, a FDE entry for the debug_frame section. const LengthPrefixedArray<uint8_t>* const cfi_info_; // For quick code, linker patches needed by the method. diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 28c7fe2c34..9f0aaa4e10 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -45,6 +45,85 @@ const bool kEnableQuickening = true; // Control check-cast elision. const bool kEnableCheckCastEllision = true; +// Holds the state for compiling a single method. +struct DexToDexCompiler::CompilationState { + struct QuickenedInfo { + QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {} + + uint32_t dex_pc; + uint16_t dex_member_index; + }; + + CompilationState(DexToDexCompiler* compiler, + const DexCompilationUnit& unit, + const CompilationLevel compilation_level, + const std::vector<uint8_t>* quicken_data); + + const std::vector<QuickenedInfo>& GetQuickenedInfo() const { + return quickened_info_; + } + + // Returns the quickening info, or an empty array if it was not quickened. + // If already_quickened is true, then don't change anything but still return what the quicken + // data would have been. + std::vector<uint8_t> Compile(); + + const DexFile& GetDexFile() const; + + // 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); + + // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In + // this case, returns the second NOP instruction pointer. Otherwise, returns + // the given "inst". + Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc); + + // Compiles a field access into a quick field access. + // The field index is replaced by an offset within an Object where we can read + // from / write to this field. Therefore, this does not involve any resolution + // at runtime. + // Since the field index is encoded with 16 bits, we can replace it only if the + // field offset can be encoded with 16 bits too. + void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_put); + + // Compiles a virtual method invocation into a quick virtual method invocation. + // The method index is replaced by the vtable index where the corresponding + // executable can be found. Therefore, this does not involve any resolution + // at runtime. + // Since the method index is encoded with 16 bits, we can replace it only if the + // vtable index can be encoded with 16 bits too. + void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, + Instruction::Code new_opcode, bool is_range); + + // Return the next index. + uint16_t NextIndex(); + + // Returns the dequickened index if an instruction is quickened, otherwise return index. + uint16_t GetIndexForInstruction(const Instruction* inst, uint32_t index); + + DexToDexCompiler* const compiler_; + CompilerDriver& driver_; + const DexCompilationUnit& unit_; + const CompilationLevel compilation_level_; + + // Filled by the compiler when quickening, in order to encode that information + // in the .oat file. The runtime will use that information to get to the original + // opcodes. + std::vector<QuickenedInfo> quickened_info_; + + // True if we optimized a return void to a return void no barrier. + bool optimized_return_void_ = false; + + // If the code item was already quickened previously. + const bool already_quickened_; + const QuickenInfoTable existing_quicken_info_; + uint32_t quicken_index_ = 0u; + + DISALLOW_COPY_AND_ASSIGN(CompilationState); +}; + DexToDexCompiler::DexToDexCompiler(CompilerDriver* driver) : driver_(driver), lock_("Quicken lock", kDexToDexCompilerLock) { @@ -55,16 +134,13 @@ void DexToDexCompiler::ClearState() { MutexLock lock(Thread::Current(), lock_); active_dex_file_ = nullptr; active_bit_vector_ = nullptr; - seen_code_items_.clear(); should_quicken_.clear(); - shared_code_items_.clear(); - blacklisted_code_items_.clear(); shared_code_item_quicken_info_.clear(); } -size_t DexToDexCompiler::NumUniqueCodeItems(Thread* self) const { +size_t DexToDexCompiler::NumCodeItemsToQuicken(Thread* self) const { MutexLock lock(self, lock_); - return seen_code_items_.size(); + return num_code_items_; } BitVector* DexToDexCompiler::GetOrAddBitVectorForDex(const DexFile* dex_file) { @@ -80,17 +156,13 @@ BitVector* DexToDexCompiler::GetOrAddBitVectorForDex(const DexFile* dex_file) { } void DexToDexCompiler::MarkForCompilation(Thread* self, - const MethodReference& method_ref, - const DexFile::CodeItem* code_item) { + const MethodReference& method_ref) { MutexLock lock(self, lock_); BitVector* const bitmap = GetOrAddBitVectorForDex(method_ref.dex_file); DCHECK(bitmap != nullptr); DCHECK(!bitmap->IsBitSet(method_ref.index)); bitmap->SetBit(method_ref.index); - // Detect the shared code items. - if (!seen_code_items_.insert(code_item).second) { - shared_code_items_.insert(code_item); - } + ++num_code_items_; } DexToDexCompiler::CompilationState::CompilationState(DexToDexCompiler* compiler, @@ -316,6 +388,7 @@ void DexToDexCompiler::CompilationState::CompileReturnVoid(Instruction* inst, ui << " at dex pc " << StringPrintf("0x%x", dex_pc) << " in method " << GetDexFile().PrettyMethod(unit_.GetDexMethodIndex(), true); inst->SetOpcode(Instruction::RETURN_VOID_NO_BARRIER); + optimized_return_void_ = true; } Instruction* DexToDexCompiler::CompilationState::CompileCheckCast(Instruction* inst, @@ -464,51 +537,48 @@ CompiledMethod* DexToDexCompiler::CompileMethod( // If the code item is shared with multiple different method ids, make sure that we quicken only // once and verify that all the dequicken maps match. if (UNLIKELY(shared_code_items_.find(code_item) != shared_code_items_.end())) { - // For shared code items, use a lock to prevent races. - MutexLock mu(soa.Self(), lock_); - // Blacklisted means there was a quickening conflict previously, bail early. - if (blacklisted_code_items_.find(code_item) != blacklisted_code_items_.end()) { + // Avoid quickening the shared code items for now because the existing conflict detection logic + // does not currently handle cases where the code item is quickened in one place but + // compiled in another. + static constexpr bool kAvoidQuickeningSharedCodeItems = true; + if (kAvoidQuickeningSharedCodeItems) { return nullptr; } + // For shared code items, use a lock to prevent races. + MutexLock mu(soa.Self(), lock_); auto existing = shared_code_item_quicken_info_.find(code_item); - const bool already_quickened = existing != shared_code_item_quicken_info_.end(); + QuickenState* existing_data = nullptr; + std::vector<uint8_t>* existing_quicken_data = nullptr; + if (existing != shared_code_item_quicken_info_.end()) { + existing_data = &existing->second; + if (existing_data->conflict_) { + return nullptr; + } + existing_quicken_data = &existing_data->quicken_data_; + } + bool optimized_return_void; { - CompilationState state(this, - unit, - compilation_level, - already_quickened ? &existing->second.quicken_data_ : nullptr); + CompilationState state(this, unit, compilation_level, existing_quicken_data); quicken_data = state.Compile(); + optimized_return_void = state.optimized_return_void_; } // Already quickened, check that the data matches what was previously seen. MethodReference method_ref(&dex_file, method_idx); - if (already_quickened) { - QuickenState* const existing_data = &existing->second; - if (existing_data->quicken_data_ != quicken_data) { - VLOG(compiler) << "Quicken data mismatch, dequickening method " + if (existing_data != nullptr) { + if (*existing_quicken_data != quicken_data || + existing_data->optimized_return_void_ != optimized_return_void) { + VLOG(compiler) << "Quicken data mismatch, for method " << dex_file.PrettyMethod(method_idx); - // Unquicken using the existing quicken data. - optimizer::ArtDecompileDEX(dex_file, - *code_item, - ArrayRef<const uint8_t>(existing_data->quicken_data_), - /* decompile_return_instruction*/ false); - // Go clear the vmaps for all the methods that were already quickened to avoid writing them - // out during oat writing. - for (const MethodReference& ref : existing_data->methods_) { - CompiledMethod* method = driver_->GetCompiledMethod(ref); - DCHECK(method != nullptr); - method->ReleaseVMapTable(); - } - // Blacklist the method to never attempt to quicken it in the future. - blacklisted_code_items_.insert(code_item); - shared_code_item_quicken_info_.erase(existing); - return nullptr; + // Mark the method as a conflict to never attempt to quicken it in the future. + existing_data->conflict_ = true; } existing_data->methods_.push_back(method_ref); } else { QuickenState new_state; new_state.methods_.push_back(method_ref); new_state.quicken_data_ = quicken_data; + new_state.optimized_return_void_ = optimized_return_void; bool inserted = shared_code_item_quicken_info_.emplace(code_item, new_state).second; CHECK(inserted) << "Failed to insert " << dex_file.PrettyMethod(method_idx); } @@ -556,9 +626,65 @@ CompiledMethod* DexToDexCompiler::CompileMethod( ArrayRef<const uint8_t>(quicken_data), // vmap_table ArrayRef<const uint8_t>(), // cfi data ArrayRef<const linker::LinkerPatch>()); + DCHECK(ret != nullptr); return ret; } +void DexToDexCompiler::SetDexFiles(const std::vector<const DexFile*>& dex_files) { + // Record what code items are already seen to detect when multiple methods have the same code + // item. + std::unordered_set<const DexFile::CodeItem*> seen_code_items; + for (const DexFile* dex_file : dex_files) { + for (size_t i = 0; i < dex_file->NumClassDefs(); ++i) { + const DexFile::ClassDef& class_def = dex_file->GetClassDef(i); + const uint8_t* class_data = dex_file->GetClassData(class_def); + if (class_data == nullptr) { + continue; + } + ClassDataItemIterator it(*dex_file, class_data); + it.SkipAllFields(); + for (; it.HasNextMethod(); it.Next()) { + const DexFile::CodeItem* code_item = it.GetMethodCodeItem(); + // Detect the shared code items. + if (!seen_code_items.insert(code_item).second) { + shared_code_items_.insert(code_item); + } + } + } + } + VLOG(compiler) << "Shared code items " << shared_code_items_.size(); +} + +void DexToDexCompiler::UnquickenConflictingMethods() { + MutexLock mu(Thread::Current(), lock_); + size_t unquicken_count = 0; + for (const auto& pair : shared_code_item_quicken_info_) { + const DexFile::CodeItem* code_item = pair.first; + const QuickenState& state = pair.second; + CHECK_GE(state.methods_.size(), 1u); + if (state.conflict_) { + // Unquicken using the existing quicken data. + // TODO: Do we really need to pass a dex file in? + optimizer::ArtDecompileDEX(*state.methods_[0].dex_file, + *code_item, + ArrayRef<const uint8_t>(state.quicken_data_), + /* decompile_return_instruction*/ true); + ++unquicken_count; + // Go clear the vmaps for all the methods that were already quickened to avoid writing them + // out during oat writing. + for (const MethodReference& ref : state.methods_) { + CompiledMethod* method = driver_->RemoveCompiledMethod(ref); + if (method != nullptr) { + // There is up to one compiled method for each method ref. Releasing it leaves the + // deduped data intact, this means its safe to do even when other threads might be + // compiling. + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(driver_, method); + } + } + } + } +} + } // namespace optimizer } // namespace art diff --git a/compiler/dex/dex_to_dex_compiler.h b/compiler/dex/dex_to_dex_compiler.h index 2105a9ded4..7df09f140c 100644 --- a/compiler/dex/dex_to_dex_compiler.h +++ b/compiler/dex/dex_to_dex_compiler.h @@ -59,99 +59,35 @@ class DexToDexCompiler { const CompilationLevel compilation_level) WARN_UNUSED; void MarkForCompilation(Thread* self, - const MethodReference& method_ref, - const DexFile::CodeItem* code_item); + const MethodReference& method_ref); void ClearState(); + // Unquicken all methods that have conflicting quicken info. This is not done during the + // quickening process to avoid race conditions. + void UnquickenConflictingMethods(); + CompilerDriver* GetDriver() { return driver_; } bool ShouldCompileMethod(const MethodReference& ref); - size_t NumUniqueCodeItems(Thread* self) const; + // Return the number of code items to quicken. + size_t NumCodeItemsToQuicken(Thread* self) const; + + void SetDexFiles(const std::vector<const DexFile*>& dex_files); private: // Holds the state for compiling a single method. - struct CompilationState { - struct QuickenedInfo { - QuickenedInfo(uint32_t pc, uint16_t index) : dex_pc(pc), dex_member_index(index) {} - - uint32_t dex_pc; - uint16_t dex_member_index; - }; - - CompilationState(DexToDexCompiler* compiler, - const DexCompilationUnit& unit, - const CompilationLevel compilation_level, - const std::vector<uint8_t>* quicken_data); - - const std::vector<QuickenedInfo>& GetQuickenedInfo() const { - return quickened_info_; - } - - // Returns the quickening info, or an empty array if it was not quickened. - // If already_quickened is true, then don't change anything but still return what the quicken - // data would have been. - std::vector<uint8_t> Compile(); - - const DexFile& GetDexFile() const; - - // 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); - - // Compiles a CHECK-CAST into 2 NOP instructions if it is known to be safe. In - // this case, returns the second NOP instruction pointer. Otherwise, returns - // the given "inst". - Instruction* CompileCheckCast(Instruction* inst, uint32_t dex_pc); - - // Compiles a field access into a quick field access. - // The field index is replaced by an offset within an Object where we can read - // from / write to this field. Therefore, this does not involve any resolution - // at runtime. - // Since the field index is encoded with 16 bits, we can replace it only if the - // field offset can be encoded with 16 bits too. - void CompileInstanceFieldAccess(Instruction* inst, uint32_t dex_pc, - Instruction::Code new_opcode, bool is_put); - - // Compiles a virtual method invocation into a quick virtual method invocation. - // The method index is replaced by the vtable index where the corresponding - // executable can be found. Therefore, this does not involve any resolution - // at runtime. - // Since the method index is encoded with 16 bits, we can replace it only if the - // vtable index can be encoded with 16 bits too. - void CompileInvokeVirtual(Instruction* inst, uint32_t dex_pc, - Instruction::Code new_opcode, bool is_range); - - // Return the next index. - uint16_t NextIndex(); - - // Returns the dequickened index if an instruction is quickened, otherwise return index. - uint16_t GetIndexForInstruction(const Instruction* inst, uint32_t index); - - DexToDexCompiler* const compiler_; - CompilerDriver& driver_; - const DexCompilationUnit& unit_; - const CompilationLevel compilation_level_; - - // Filled by the compiler when quickening, in order to encode that information - // in the .oat file. The runtime will use that information to get to the original - // opcodes. - std::vector<QuickenedInfo> quickened_info_; - - // If the code item was already quickened previously. - const bool already_quickened_; - const QuickenInfoTable existing_quicken_info_; - uint32_t quicken_index_ = 0u; - - DISALLOW_COPY_AND_ASSIGN(CompilationState); - }; + struct CompilationState; + // Quicken state for a code item, may be referenced by multiple methods. struct QuickenState { std::vector<MethodReference> methods_; std::vector<uint8_t> quicken_data_; + bool optimized_return_void_ = false; + bool conflict_ = false; }; BitVector* GetOrAddBitVectorForDex(const DexFile* dex_file) REQUIRES(lock_); @@ -166,15 +102,14 @@ class DexToDexCompiler { mutable Mutex lock_; // Record what method references are going to get quickened. std::unordered_map<const DexFile*, BitVector> should_quicken_; - // Record what code items are already seen to detect when multiple methods have the same code - // item. - std::unordered_set<const DexFile::CodeItem*> seen_code_items_ GUARDED_BY(lock_); // Guarded by lock_ during writing, accessed without a lock during quickening. // This is safe because no thread is adding to the shared code items during the quickening phase. std::unordered_set<const DexFile::CodeItem*> shared_code_items_; - std::unordered_set<const DexFile::CodeItem*> blacklisted_code_items_ GUARDED_BY(lock_); + // Blacklisted code items are unquickened in UnquickenConflictingMethods. std::unordered_map<const DexFile::CodeItem*, QuickenState> shared_code_item_quicken_info_ GUARDED_BY(lock_); + // Number of added code items. + size_t num_code_items_ GUARDED_BY(lock_) = 0u; }; std::ostream& operator<<(std::ostream& os, const DexToDexCompiler::CompilationLevel& rhs); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index b3adf10aea..fb428b8d9a 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -656,7 +656,7 @@ static void CompileMethodQuick( optimizer::DexToDexCompiler::CompilationLevel::kDontDexToDexCompile) { DCHECK(!Runtime::Current()->UseJitCompilation()); // TODO: add a command-line option to disable DEX-to-DEX compilation ? - driver->GetDexToDexCompiler().MarkForCompilation(self, method_ref, code_item); + driver->GetDexToDexCompiler().MarkForCompilation(self, method_ref); } } return compiled_method; @@ -730,7 +730,7 @@ void CompilerDriver::CompileOne(Thread* self, ArtMethod* method, TimingLogger* t true, dex_cache); - const size_t num_methods = dex_to_dex_compiler_.NumUniqueCodeItems(self); + const size_t num_methods = dex_to_dex_compiler_.NumCodeItemsToQuicken(self); if (num_methods != 0) { DCHECK_EQ(num_methods, 1u); CompileMethodDex2Dex(self, @@ -2808,7 +2808,7 @@ void CompilerDriver::Compile(jobject class_loader, Runtime::Current()->ReclaimArenaPoolMemory(); } - if (dex_to_dex_compiler_.NumUniqueCodeItems(Thread::Current()) > 0u) { + if (dex_to_dex_compiler_.NumCodeItemsToQuicken(Thread::Current()) > 0u) { // TODO: Not visit all of the dex files, its probably rare that only one would have quickened // methods though. for (const DexFile* dex_file : dex_files) { @@ -2840,6 +2840,12 @@ void CompilerDriver::AddCompiledMethod(const MethodReference& method_ref, DCHECK(GetCompiledMethod(method_ref) != nullptr) << method_ref.PrettyMethod(); } +CompiledMethod* CompilerDriver::RemoveCompiledMethod(const MethodReference& method_ref) { + CompiledMethod* ret = nullptr; + CHECK(compiled_methods_.Remove(method_ref, &ret)); + return ret; +} + bool CompilerDriver::GetCompiledClass(const ClassReference& ref, ClassStatus* status) const { DCHECK(status != nullptr); // The table doesn't know if something wasn't inserted. For this case it will return @@ -3013,6 +3019,7 @@ void CompilerDriver::FreeThreadPools() { void CompilerDriver::SetDexFilesForOatFile(const std::vector<const DexFile*>& dex_files) { dex_files_for_oat_file_ = dex_files; compiled_classes_.AddDexFiles(dex_files); + dex_to_dex_compiler_.SetDexFiles(dex_files); } void CompilerDriver::SetClasspathDexFiles(const std::vector<const DexFile*>& dex_files) { diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index e32b5c4fd3..2b524a347d 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -165,6 +165,7 @@ class CompilerDriver { void AddCompiledMethod(const MethodReference& method_ref, CompiledMethod* const compiled_method, size_t non_relative_linker_patch_count); + CompiledMethod* RemoveCompiledMethod(const MethodReference& method_ref); void SetRequiresConstructorBarrier(Thread* self, const DexFile* dex_file, diff --git a/compiler/utils/atomic_dex_ref_map-inl.h b/compiler/utils/atomic_dex_ref_map-inl.h index 203e484fb7..7023b9a0e8 100644 --- a/compiler/utils/atomic_dex_ref_map-inl.h +++ b/compiler/utils/atomic_dex_ref_map-inl.h @@ -75,6 +75,18 @@ inline bool AtomicDexRefMap<DexFileReferenceType, Value>::Get(const DexFileRefer } template <typename DexFileReferenceType, typename Value> +inline bool AtomicDexRefMap<DexFileReferenceType, Value>::Remove(const DexFileReferenceType& ref, + Value* out) { + ElementArray* const array = GetArray(ref.dex_file); + if (array == nullptr) { + return false; + } + *out = (*array)[ref.index].LoadRelaxed(); + (*array)[ref.index].StoreSequentiallyConsistent(nullptr); + return true; +} + +template <typename DexFileReferenceType, typename Value> inline void AtomicDexRefMap<DexFileReferenceType, Value>::AddDexFile(const DexFile* dex_file) { arrays_.Put(dex_file, std::move(ElementArray(NumberOfDexIndices(dex_file)))); } diff --git a/compiler/utils/atomic_dex_ref_map.h b/compiler/utils/atomic_dex_ref_map.h index 9ff506d6a4..3474e16b8d 100644 --- a/compiler/utils/atomic_dex_ref_map.h +++ b/compiler/utils/atomic_dex_ref_map.h @@ -45,6 +45,9 @@ class AtomicDexRefMap { // Retreive an item, returns false if the dex file is not added. bool Get(const DexFileReferenceType& ref, Value* out) const; + // Remove an item and return the existing value. Returns false if the dex file is not added. + bool Remove(const DexFileReferenceType& ref, Value* out); + // Dex files must be added before method references belonging to them can be used as keys. Not // thread safe. void AddDexFile(const DexFile* dex_file); diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc index 08438c4f4a..9462fe563b 100644 --- a/dexlayout/compact_dex_writer.cc +++ b/dexlayout/compact_dex_writer.cc @@ -460,6 +460,11 @@ void CompactDexWriter::Write(DexContainer* output) { // Rewrite the header with the calculated checksum. WriteHeader(main_stream); } + + // Clear the dedupe to prevent interdex code item deduping. This does not currently work well with + // dex2oat's class unloading. The issue is that verification encounters quickened opcodes after + // the first dex gets unloaded. + code_item_dedupe_->Clear(); } std::unique_ptr<DexContainer> CompactDexWriter::CreateDexContainer() const { diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h index 24d0fbf61d..ea9f7d13db 100644 --- a/dexlayout/compact_dex_writer.h +++ b/dexlayout/compact_dex_writer.h @@ -44,6 +44,11 @@ class CompactDexWriter : public DexWriter { // Returns the offset of the deduplicated data or kDidNotDedupe did deduplication did not occur. uint32_t Dedupe(uint32_t data_start, uint32_t data_end, uint32_t item_offset); + // Clear dedupe state to prevent deduplication against existing items in the future. + void Clear() { + dedupe_map_.clear(); + } + private: class HashedMemoryRange { public: diff --git a/dexlayout/dexlayout.h b/dexlayout/dexlayout.h index d2f9cb9ce5..5635271dc1 100644 --- a/dexlayout/dexlayout.h +++ b/dexlayout/dexlayout.h @@ -66,8 +66,7 @@ class Options { bool visualize_pattern_ = false; bool update_checksum_ = false; CompactDexLevel compact_dex_level_ = CompactDexLevel::kCompactDexLevelNone; - // Disabled until dex2oat properly handles quickening of deduped code items. - bool dedupe_code_items_ = false; + bool dedupe_code_items_ = true; OutputFormat output_format_ = kOutputPlain; const char* output_dex_directory_ = nullptr; const char* output_file_name_ = nullptr; diff --git a/runtime/dex/dex_file_layout.h b/runtime/dex/dex_file_layout.h index a7b9051f24..793e3b5de7 100644 --- a/runtime/dex/dex_file_layout.h +++ b/runtime/dex/dex_file_layout.h @@ -83,7 +83,7 @@ class DexLayoutSection { } void CombineSection(uint32_t start_offset, uint32_t end_offset) { - DCHECK_LT(start_offset, end_offset); + DCHECK_LE(start_offset, end_offset); if (start_offset_ == end_offset_) { start_offset_ = start_offset; end_offset_ = end_offset; diff --git a/test/ManyMethods/ManyMethods.java b/test/ManyMethods/ManyMethods.java index b3a57f6b3b..98b9faffcd 100644 --- a/test/ManyMethods/ManyMethods.java +++ b/test/ManyMethods/ManyMethods.java @@ -26,6 +26,8 @@ class ManyMethods { public static String msg7 = "Hello World7"; public static String msg8 = "Hello World8"; public static String msg9 = "Hello World9"; + public static String msg10 = "Hello World10"; + public static String msg11 = "Hello World11"; } static class Printer { @@ -57,35 +59,35 @@ class ManyMethods { } public static void Print4() { - Printer.Print(Strings.msg2); + Printer.Print(Strings.msg4); } public static void Print5() { - Printer.Print(Strings.msg3); + Printer.Print(Strings.msg5); } public static void Print6() { - Printer2.Print(Strings.msg4); + Printer2.Print(Strings.msg6); } public static void Print7() { - Printer.Print(Strings.msg5); + Printer.Print(Strings.msg7); } public static void Print8() { - Printer.Print(Strings.msg6); + Printer.Print(Strings.msg8); } public static void Print9() { - Printer2.Print(Strings.msg7); + Printer2.Print(Strings.msg9); } public static void Print10() { - Printer2.Print(Strings.msg8); + Printer2.Print(Strings.msg10); } public static void Print11() { - Printer.Print(Strings.msg9); + Printer.Print(Strings.msg11); } public static void main(String args[]) { |