summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--compiler/compiled_method.cc6
-rw-r--r--compiler/compiled_method.h6
-rw-r--r--compiler/dex/dex_to_dex_compiler.cc206
-rw-r--r--compiler/dex/dex_to_dex_compiler.h97
-rw-r--r--compiler/driver/compiler_driver.cc13
-rw-r--r--compiler/driver/compiler_driver.h1
-rw-r--r--compiler/utils/atomic_dex_ref_map-inl.h12
-rw-r--r--compiler/utils/atomic_dex_ref_map.h3
-rw-r--r--dexlayout/compact_dex_writer.cc5
-rw-r--r--dexlayout/compact_dex_writer.h5
-rw-r--r--dexlayout/dexlayout.h3
-rw-r--r--runtime/dex/dex_file_layout.h2
-rw-r--r--test/ManyMethods/ManyMethods.java18
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[]) {