diff options
author | 2015-09-11 11:59:18 +0100 | |
---|---|---|
committer | 2015-10-27 15:13:37 +0000 | |
commit | 35831e8bfa1c0944d4c978d99c4c5b9577945170 (patch) | |
tree | a347ecaf4265a1713c86d528392e162d5edfaebc | |
parent | 171b12e8060f63cf900ffc43d6db35125f6b8c83 (diff) |
Reduce memory used by CompiledMethods.
Use LengthPrefixedArray<>s instead of SwapVector<>s to store
CompiledMethod data and get rid of the unnecessary members
of CompiledMethod to reduce dex2oat memory usage. Refactor
the deduplication from CompilerDriver to a new class.
Use HashSet<> instead of std::set<> for the DedupeSet<> to
further decrease the memory usage and improve performance.
This reduces the dex2oat memory usage when compiling boot
image on Nexus 5 (with Optimizing, -j1) by ~6.75MiB (5%).
This also reduces the compile time by ~2.2% (~1.6% dex2oat
time; with Optimizing, without -j).
Change-Id: I974f1f5e58350de2bf487a2bca3907fa05fb80ea
40 files changed, 1225 insertions, 570 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 6295e1527b..fdfd94c5c0 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -230,6 +230,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ runtime/jni_internal_test.cc \ runtime/proxy_test.cc \ runtime/reflection_test.cc \ + compiler/compiled_method_test.cc \ compiler/dex/gvn_dead_code_elimination_test.cc \ compiler/dex/global_value_numbering_test.cc \ compiler/dex/local_value_numbering_test.cc \ @@ -237,6 +238,7 @@ COMPILER_GTEST_COMMON_SRC_FILES := \ compiler/dex/mir_optimization_test.cc \ compiler/dex/type_inference_test.cc \ compiler/dwarf/dwarf_test.cc \ + compiler/driver/compiled_method_storage_test.cc \ compiler/driver/compiler_driver_test.cc \ compiler/elf_writer_test.cc \ compiler/image_test.cc \ diff --git a/compiler/Android.mk b/compiler/Android.mk index 17f9d122a7..4ae1403303 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -54,6 +54,7 @@ LIBART_COMPILER_SRC_FILES := \ dex/verification_results.cc \ dex/vreg_analysis.cc \ dex/quick_compiler_callbacks.cc \ + driver/compiled_method_storage.cc \ driver/compiler_driver.cc \ driver/compiler_options.cc \ driver/dex_compilation_unit.cc \ diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index 5e345dbf61..6fd457599f 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -51,7 +51,7 @@ class CFITest : public dwarf::DwarfTest { dwarf::WriteDebugFrameCIE(is64bit, dwarf::DW_EH_PE_absptr, dwarf::Reg(8), initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; - dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi, + dwarf::WriteDebugFrameFDE(is64bit, 0, 0, actual_asm.size(), ArrayRef<const uint8_t>(actual_cfi), kCFIFormat, &debug_frame_data_, &debug_frame_patches); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 58a2f96cd9..151437b4cb 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -54,22 +54,22 @@ void CommonCompilerTest::MakeExecutable(ArtMethod* method) { method->GetDexMethodIndex())); } if (compiled_method != nullptr) { - const SwapVector<uint8_t>* code = compiled_method->GetQuickCode(); - uint32_t code_size = code->size(); + ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); + uint32_t code_size = code.size(); CHECK_NE(0u, code_size); - const SwapVector<uint8_t>* vmap_table = compiled_method->GetVmapTable(); - uint32_t vmap_table_offset = vmap_table->empty() ? 0u - : sizeof(OatQuickMethodHeader) + vmap_table->size(); - const SwapVector<uint8_t>* mapping_table = compiled_method->GetMappingTable(); - bool mapping_table_used = mapping_table != nullptr && !mapping_table->empty(); - size_t mapping_table_size = mapping_table_used ? mapping_table->size() : 0U; + ArrayRef<const uint8_t> vmap_table = compiled_method->GetVmapTable(); + uint32_t vmap_table_offset = vmap_table.empty() ? 0u + : sizeof(OatQuickMethodHeader) + vmap_table.size(); + ArrayRef<const uint8_t> mapping_table = compiled_method->GetMappingTable(); + bool mapping_table_used = !mapping_table.empty(); + size_t mapping_table_size = mapping_table.size(); uint32_t mapping_table_offset = !mapping_table_used ? 0u - : sizeof(OatQuickMethodHeader) + vmap_table->size() + mapping_table_size; - const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap(); - bool gc_map_used = gc_map != nullptr && !gc_map->empty(); - size_t gc_map_size = gc_map_used ? gc_map->size() : 0U; + : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size; + ArrayRef<const uint8_t> gc_map = compiled_method->GetGcMap(); + bool gc_map_used = !gc_map.empty(); + size_t gc_map_size = gc_map.size(); uint32_t gc_map_offset = !gc_map_used ? 0u - : sizeof(OatQuickMethodHeader) + vmap_table->size() + mapping_table_size + gc_map_size; + : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table_size + gc_map_size; OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset, compiled_method->GetFrameSizeInBytes(), compiled_method->GetCoreSpillMask(), @@ -77,25 +77,25 @@ void CommonCompilerTest::MakeExecutable(ArtMethod* method) { header_code_and_maps_chunks_.push_back(std::vector<uint8_t>()); std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back(); - size_t size = sizeof(method_header) + code_size + vmap_table->size() + mapping_table_size + + size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table_size + gc_map_size; size_t code_offset = compiled_method->AlignCode(size - code_size); size_t padding = code_offset - (size - code_size); chunk->reserve(padding + size); chunk->resize(sizeof(method_header)); memcpy(&(*chunk)[0], &method_header, sizeof(method_header)); - chunk->insert(chunk->begin(), vmap_table->begin(), vmap_table->end()); + chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end()); if (mapping_table_used) { - chunk->insert(chunk->begin(), mapping_table->begin(), mapping_table->end()); + chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end()); } if (gc_map_used) { - chunk->insert(chunk->begin(), gc_map->begin(), gc_map->end()); + chunk->insert(chunk->begin(), gc_map.begin(), gc_map.end()); } chunk->insert(chunk->begin(), padding, 0); - chunk->insert(chunk->end(), code->begin(), code->end()); + chunk->insert(chunk->end(), code.begin(), code.end()); CHECK_EQ(padding + size, chunk->size()); const void* code_ptr = &(*chunk)[code_offset]; - MakeExecutable(code_ptr, code->size()); + MakeExecutable(code_ptr, code.size()); const void* method_code = CompiledMethod::CodePointer(code_ptr, compiled_method->GetInstructionSet()); LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code; diff --git a/compiler/compiled_method.cc b/compiler/compiled_method.cc index 74ef35e740..9551d2298b 100644 --- a/compiler/compiled_method.cc +++ b/compiler/compiled_method.cc @@ -15,27 +15,22 @@ */ #include "compiled_method.h" + +#include "driver/compiled_method_storage.h" #include "driver/compiler_driver.h" +#include "utils/swap_space.h" namespace art { CompiledCode::CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, - const ArrayRef<const uint8_t>& quick_code, bool owns_code_array) - : compiler_driver_(compiler_driver), instruction_set_(instruction_set), - owns_code_array_(owns_code_array), quick_code_(nullptr) { - if (owns_code_array_) { - // If we are supposed to own the code, don't deduplicate it. - quick_code_ = new SwapVector<uint8_t>(quick_code.begin(), quick_code.end(), - compiler_driver_->GetSwapSpaceAllocator()); - } else { - quick_code_ = compiler_driver_->DeduplicateCode(quick_code); - } + const ArrayRef<const uint8_t>& quick_code) + : compiler_driver_(compiler_driver), + instruction_set_(instruction_set), + quick_code_(compiler_driver_->GetCompiledMethodStorage()->DeduplicateCode(quick_code)) { } CompiledCode::~CompiledCode() { - if (owns_code_array_) { - delete quick_code_; - } + compiler_driver_->GetCompiledMethodStorage()->ReleaseCode(quick_code_); } bool CompiledCode::operator==(const CompiledCode& rhs) const { @@ -104,59 +99,28 @@ const void* CompiledCode::CodePointer(const void* code_pointer, } } -const std::vector<uint32_t>& CompiledCode::GetOatdataOffsetsToCompliledCodeOffset() const { - CHECK_NE(0U, oatdata_offsets_to_compiled_code_offset_.size()); - return oatdata_offsets_to_compiled_code_offset_; -} - -void CompiledCode::AddOatdataOffsetToCompliledCodeOffset(uint32_t offset) { - oatdata_offsets_to_compiled_code_offset_.push_back(offset); -} - CompiledMethod::CompiledMethod(CompilerDriver* driver, InstructionSet instruction_set, const ArrayRef<const uint8_t>& quick_code, const size_t frame_size_in_bytes, const uint32_t core_spill_mask, const uint32_t fp_spill_mask, - DefaultSrcMap* src_mapping_table, + const ArrayRef<const SrcMapElem>& src_mapping_table, const ArrayRef<const uint8_t>& mapping_table, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& native_gc_map, const ArrayRef<const uint8_t>& cfi_info, const ArrayRef<const LinkerPatch>& patches) - : CompiledCode(driver, instruction_set, quick_code, !driver->DedupeEnabled()), - owns_arrays_(!driver->DedupeEnabled()), + : CompiledCode(driver, instruction_set, quick_code), frame_size_in_bytes_(frame_size_in_bytes), core_spill_mask_(core_spill_mask), fp_spill_mask_(fp_spill_mask), - patches_(patches.begin(), patches.end(), driver->GetSwapSpaceAllocator()) { - if (owns_arrays_) { - if (src_mapping_table == nullptr) { - src_mapping_table_ = new SwapSrcMap(driver->GetSwapSpaceAllocator()); - } else { - src_mapping_table_ = new SwapSrcMap(src_mapping_table->begin(), src_mapping_table->end(), - driver->GetSwapSpaceAllocator()); - } - mapping_table_ = mapping_table.empty() ? - nullptr : new SwapVector<uint8_t>(mapping_table.begin(), mapping_table.end(), - driver->GetSwapSpaceAllocator()); - vmap_table_ = new SwapVector<uint8_t>(vmap_table.begin(), vmap_table.end(), - driver->GetSwapSpaceAllocator()); - gc_map_ = native_gc_map.empty() ? nullptr : - new SwapVector<uint8_t>(native_gc_map.begin(), native_gc_map.end(), - driver->GetSwapSpaceAllocator()); - cfi_info_ = cfi_info.empty() ? nullptr : - new SwapVector<uint8_t>(cfi_info.begin(), cfi_info.end(), driver->GetSwapSpaceAllocator()); - } else { - src_mapping_table_ = src_mapping_table == nullptr ? - driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>()) : - driver->DeduplicateSrcMappingTable(ArrayRef<SrcMapElem>(*src_mapping_table)); - mapping_table_ = mapping_table.empty() ? - nullptr : driver->DeduplicateMappingTable(mapping_table); - vmap_table_ = driver->DeduplicateVMapTable(vmap_table); - gc_map_ = native_gc_map.empty() ? nullptr : driver->DeduplicateGCMap(native_gc_map); - cfi_info_ = cfi_info.empty() ? nullptr : driver->DeduplicateCFIInfo(cfi_info); - } + src_mapping_table_( + driver->GetCompiledMethodStorage()->DeduplicateSrcMappingTable(src_mapping_table)), + mapping_table_(driver->GetCompiledMethodStorage()->DeduplicateMappingTable(mapping_table)), + vmap_table_(driver->GetCompiledMethodStorage()->DeduplicateVMapTable(vmap_table)), + gc_map_(driver->GetCompiledMethodStorage()->DeduplicateGCMap(native_gc_map)), + cfi_info_(driver->GetCompiledMethodStorage()->DeduplicateCFIInfo(cfi_info)), + patches_(driver->GetCompiledMethodStorage()->DeduplicateLinkerPatches(patches)) { } CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( @@ -166,13 +130,13 @@ CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( const size_t frame_size_in_bytes, const uint32_t core_spill_mask, const uint32_t fp_spill_mask, - DefaultSrcMap* src_mapping_table, + const ArrayRef<const SrcMapElem>& src_mapping_table, const ArrayRef<const uint8_t>& mapping_table, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& native_gc_map, const ArrayRef<const uint8_t>& cfi_info, const ArrayRef<const LinkerPatch>& patches) { - SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator()); + SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator()); CompiledMethod* ret = alloc.allocate(1); alloc.construct(ret, driver, instruction_set, quick_code, frame_size_in_bytes, core_spill_mask, fp_spill_mask, src_mapping_table, mapping_table, vmap_table, native_gc_map, @@ -180,22 +144,20 @@ CompiledMethod* CompiledMethod::SwapAllocCompiledMethod( return ret; } - - void CompiledMethod::ReleaseSwapAllocatedCompiledMethod(CompilerDriver* driver, CompiledMethod* m) { - SwapAllocator<CompiledMethod> alloc(driver->GetSwapSpaceAllocator()); + SwapAllocator<CompiledMethod> alloc(driver->GetCompiledMethodStorage()->GetSwapSpaceAllocator()); alloc.destroy(m); alloc.deallocate(m, 1); } CompiledMethod::~CompiledMethod() { - if (owns_arrays_) { - delete src_mapping_table_; - delete mapping_table_; - delete vmap_table_; - delete gc_map_; - delete cfi_info_; - } + CompiledMethodStorage* storage = GetCompilerDriver()->GetCompiledMethodStorage(); + storage->ReleaseLinkerPatches(patches_); + storage->ReleaseCFIInfo(cfi_info_); + storage->ReleaseGCMap(gc_map_); + storage->ReleaseVMapTable(vmap_table_); + storage->ReleaseMappingTable(mapping_table_); + storage->ReleaseSrcMappingTable(src_mapping_table_); } } // namespace art diff --git a/compiler/compiled_method.h b/compiler/compiled_method.h index a4d2387030..15a4ba0f6f 100644 --- a/compiler/compiled_method.h +++ b/compiler/compiled_method.h @@ -23,19 +23,20 @@ #include "arch/instruction_set.h" #include "base/bit_utils.h" +#include "length_prefixed_array.h" #include "method_reference.h" #include "utils/array_ref.h" -#include "utils/swap_space.h" namespace art { class CompilerDriver; +class CompiledMethodStorage; class CompiledCode { public: // For Quick to supply an code blob CompiledCode(CompilerDriver* compiler_driver, InstructionSet instruction_set, - const ArrayRef<const uint8_t>& quick_code, bool owns_code_array); + const ArrayRef<const uint8_t>& quick_code); virtual ~CompiledCode(); @@ -43,8 +44,8 @@ class CompiledCode { return instruction_set_; } - const SwapVector<uint8_t>* GetQuickCode() const { - return quick_code_; + ArrayRef<const uint8_t> GetQuickCode() const { + return GetArray(quick_code_); } bool operator==(const CompiledCode& rhs) const; @@ -66,40 +67,45 @@ class CompiledCode { static const void* CodePointer(const void* code_pointer, InstructionSet instruction_set); - const std::vector<uint32_t>& GetOatdataOffsetsToCompliledCodeOffset() const; - void AddOatdataOffsetToCompliledCodeOffset(uint32_t offset); + protected: + template <typename T> + static ArrayRef<const T> GetArray(const LengthPrefixedArray<T>* array) { + if (array == nullptr) { + return ArrayRef<const T>(); + } + DCHECK_NE(array->size(), 0u); + return ArrayRef<const T>(&array->At(0), array->size()); + } + + CompilerDriver* GetCompilerDriver() { + return compiler_driver_; + } private: CompilerDriver* const compiler_driver_; const InstructionSet instruction_set_; - // If we own the code array (means that we free in destructor). - const bool owns_code_array_; - // Used to store the PIC code for Quick. - SwapVector<uint8_t>* quick_code_; - - // There are offsets from the oatdata symbol to where the offset to - // the compiled method will be found. These are computed by the - // OatWriter and then used by the ElfWriter to add relocations so - // that MCLinker can update the values to the location in the linked .so. - std::vector<uint32_t> oatdata_offsets_to_compiled_code_offset_; + const LengthPrefixedArray<uint8_t>* const quick_code_; }; class SrcMapElem { public: uint32_t from_; int32_t to_; +}; - // Lexicographical compare. - bool operator<(const SrcMapElem& other) const { - if (from_ != other.from_) { - return from_ < other.from_; - } - return to_ < other.to_; +inline bool operator<(const SrcMapElem& lhs, const SrcMapElem& rhs) { + if (lhs.from_ != rhs.from_) { + return lhs.from_ < rhs.from_; } -}; + return lhs.to_ < rhs.to_; +} + +inline bool operator==(const SrcMapElem& lhs, const SrcMapElem& rhs) { + return lhs.from_ == rhs.from_ && lhs.to_ == rhs.to_; +} template <class Allocator> class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> { @@ -151,7 +157,6 @@ class SrcMap FINAL : public std::vector<SrcMapElem, Allocator> { }; using DefaultSrcMap = SrcMap<std::allocator<SrcMapElem>>; -using SwapSrcMap = SrcMap<SwapAllocator<SrcMapElem>>; enum LinkerPatchType { @@ -273,6 +278,9 @@ class LinkerPatch { uint32_t method_idx_; // Method index for Call/Method patches. uint32_t type_idx_; // Type index for Type patches. uint32_t element_offset_; // Element offset in the dex cache arrays. + static_assert(sizeof(method_idx_) == sizeof(cmp1_), "needed by relational operators"); + static_assert(sizeof(type_idx_) == sizeof(cmp1_), "needed by relational operators"); + static_assert(sizeof(element_offset_) == sizeof(cmp1_), "needed by relational operators"); }; union { uint32_t cmp2_; // Used for relational operators. @@ -313,7 +321,7 @@ class CompiledMethod FINAL : public CompiledCode { const size_t frame_size_in_bytes, const uint32_t core_spill_mask, const uint32_t fp_spill_mask, - DefaultSrcMap* src_mapping_table, + const ArrayRef<const SrcMapElem>& src_mapping_table, const ArrayRef<const uint8_t>& mapping_table, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& native_gc_map, @@ -329,7 +337,7 @@ class CompiledMethod FINAL : public CompiledCode { const size_t frame_size_in_bytes, const uint32_t core_spill_mask, const uint32_t fp_spill_mask, - DefaultSrcMap* src_mapping_table, + const ArrayRef<const SrcMapElem>& src_mapping_table, const ArrayRef<const uint8_t>& mapping_table, const ArrayRef<const uint8_t>& vmap_table, const ArrayRef<const uint8_t>& native_gc_map, @@ -350,35 +358,31 @@ class CompiledMethod FINAL : public CompiledCode { return fp_spill_mask_; } - const SwapSrcMap& GetSrcMappingTable() const { - DCHECK(src_mapping_table_ != nullptr); - return *src_mapping_table_; + ArrayRef<const SrcMapElem> GetSrcMappingTable() const { + return GetArray(src_mapping_table_); } - SwapVector<uint8_t> const* GetMappingTable() const { - return mapping_table_; + ArrayRef<const uint8_t> GetMappingTable() const { + return GetArray(mapping_table_); } - const SwapVector<uint8_t>* GetVmapTable() const { - DCHECK(vmap_table_ != nullptr); - return vmap_table_; + ArrayRef<const uint8_t> GetVmapTable() const { + return GetArray(vmap_table_); } - SwapVector<uint8_t> const* GetGcMap() const { - return gc_map_; + ArrayRef<const uint8_t> GetGcMap() const { + return GetArray(gc_map_); } - const SwapVector<uint8_t>* GetCFIInfo() const { - return cfi_info_; + ArrayRef<const uint8_t> GetCFIInfo() const { + return GetArray(cfi_info_); } ArrayRef<const LinkerPatch> GetPatches() const { - return ArrayRef<const LinkerPatch>(patches_); + return GetArray(patches_); } private: - // Whether or not the arrays are owned by the compiled method or dedupe sets. - const bool owns_arrays_; // For quick code, the size of the activation used by the code. const size_t frame_size_in_bytes_; // For quick code, a bit mask describing spilled GPR callee-save registers. @@ -386,19 +390,19 @@ class CompiledMethod FINAL : public CompiledCode { // For quick code, a bit mask describing spilled FPR callee-save registers. const uint32_t fp_spill_mask_; // For quick code, a set of pairs (PC, DEX) mapping from native PC offset to DEX offset. - SwapSrcMap* src_mapping_table_; + const LengthPrefixedArray<SrcMapElem>* const src_mapping_table_; // For quick code, a uleb128 encoded map from native PC offset to dex PC aswell as dex PC to // native PC offset. Size prefixed. - SwapVector<uint8_t>* mapping_table_; + const LengthPrefixedArray<uint8_t>* const mapping_table_; // For quick code, a uleb128 encoded map from GPR/FPR register to dex register. Size prefixed. - SwapVector<uint8_t>* vmap_table_; + const LengthPrefixedArray<uint8_t>* const vmap_table_; // For quick code, a map keyed by native PC indices to bitmaps describing what dalvik registers // are live. - SwapVector<uint8_t>* gc_map_; + const LengthPrefixedArray<uint8_t>* const gc_map_; // For quick code, a FDE entry for the debug_frame section. - SwapVector<uint8_t>* cfi_info_; + const LengthPrefixedArray<uint8_t>* const cfi_info_; // For quick code, linker patches needed by the method. - const SwapVector<LinkerPatch> patches_; + const LengthPrefixedArray<LinkerPatch>* const patches_; }; } // namespace art diff --git a/compiler/compiled_method_test.cc b/compiler/compiled_method_test.cc new file mode 100644 index 0000000000..99ee875da2 --- /dev/null +++ b/compiler/compiled_method_test.cc @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "compiled_method.h" + +namespace art { + +TEST(CompiledMethod, SrcMapElemOperators) { + SrcMapElem elems[] = { + { 1u, -1 }, + { 1u, 0 }, + { 1u, 1 }, + { 2u, -1 }, + { 2u, 0 }, // Index 4. + { 2u, 1 }, + { 2u, 0u }, // Index 6: Arbitrarily add identical SrcMapElem with index 4. + }; + + for (size_t i = 0; i != arraysize(elems); ++i) { + for (size_t j = 0; j != arraysize(elems); ++j) { + bool expected = (i != 6u ? i : 4u) == (j != 6u ? j : 4u); + EXPECT_EQ(expected, elems[i] == elems[j]) << i << " " << j; + } + } + + for (size_t i = 0; i != arraysize(elems); ++i) { + for (size_t j = 0; j != arraysize(elems); ++j) { + bool expected = (i != 6u ? i : 4u) < (j != 6u ? j : 4u); + EXPECT_EQ(expected, elems[i] < elems[j]) << i << " " << j; + } + } +} + +TEST(CompiledMethod, LinkerPatchOperators) { + const DexFile* dex_file1 = reinterpret_cast<const DexFile*>(1); + const DexFile* dex_file2 = reinterpret_cast<const DexFile*>(2); + LinkerPatch patches[] = { + LinkerPatch::MethodPatch(16u, dex_file1, 1000u), + LinkerPatch::MethodPatch(16u, dex_file1, 1001u), + LinkerPatch::MethodPatch(16u, dex_file2, 1000u), + LinkerPatch::MethodPatch(16u, dex_file2, 1001u), // Index 3. + LinkerPatch::CodePatch(16u, dex_file1, 1000u), + LinkerPatch::CodePatch(16u, dex_file1, 1001u), + LinkerPatch::CodePatch(16u, dex_file2, 1000u), + LinkerPatch::CodePatch(16u, dex_file2, 1001u), + LinkerPatch::RelativeCodePatch(16u, dex_file1, 1000u), + LinkerPatch::RelativeCodePatch(16u, dex_file1, 1001u), + LinkerPatch::RelativeCodePatch(16u, dex_file2, 1000u), + LinkerPatch::RelativeCodePatch(16u, dex_file2, 1001u), + LinkerPatch::TypePatch(16u, dex_file1, 1000u), + LinkerPatch::TypePatch(16u, dex_file1, 1001u), + LinkerPatch::TypePatch(16u, dex_file2, 1000u), + LinkerPatch::TypePatch(16u, dex_file2, 1001u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3000u, 2000u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3001u, 2000u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3000u, 2001u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file1, 3001u, 2001u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3000u, 2000u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3001u, 2000u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3000u, 2001u), + LinkerPatch::DexCacheArrayPatch(16u, dex_file2, 3001u, 2001u), + LinkerPatch::MethodPatch(32u, dex_file1, 1000u), + LinkerPatch::MethodPatch(32u, dex_file1, 1001u), + LinkerPatch::MethodPatch(32u, dex_file2, 1000u), + LinkerPatch::MethodPatch(32u, dex_file2, 1001u), + LinkerPatch::CodePatch(32u, dex_file1, 1000u), + LinkerPatch::CodePatch(32u, dex_file1, 1001u), + LinkerPatch::CodePatch(32u, dex_file2, 1000u), + LinkerPatch::CodePatch(32u, dex_file2, 1001u), + LinkerPatch::RelativeCodePatch(32u, dex_file1, 1000u), + LinkerPatch::RelativeCodePatch(32u, dex_file1, 1001u), + LinkerPatch::RelativeCodePatch(32u, dex_file2, 1000u), + LinkerPatch::RelativeCodePatch(32u, dex_file2, 1001u), + LinkerPatch::TypePatch(32u, dex_file1, 1000u), + LinkerPatch::TypePatch(32u, dex_file1, 1001u), + LinkerPatch::TypePatch(32u, dex_file2, 1000u), + LinkerPatch::TypePatch(32u, dex_file2, 1001u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3000u, 2000u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3001u, 2000u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3000u, 2001u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file1, 3001u, 2001u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3000u, 2000u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3001u, 2000u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3000u, 2001u), + LinkerPatch::DexCacheArrayPatch(32u, dex_file2, 3001u, 2001u), + LinkerPatch::MethodPatch(16u, dex_file2, 1001u), // identical with patch as index 3. + }; + constexpr size_t last_index = arraysize(patches) - 1u; + + for (size_t i = 0; i != arraysize(patches); ++i) { + for (size_t j = 0; j != arraysize(patches); ++j) { + bool expected = (i != last_index ? i : 3u) == (j != last_index ? j : 3u); + EXPECT_EQ(expected, patches[i] == patches[j]) << i << " " << j; + } + } + + for (size_t i = 0; i != arraysize(patches); ++i) { + for (size_t j = 0; j != arraysize(patches); ++j) { + bool expected = (i != last_index ? i : 3u) < (j != last_index ? j : 3u); + EXPECT_EQ(expected, patches[i] < patches[j]) << i << " " << j; + } + } +} + +} // namespace art diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index ff7ddc18c6..4836041ce0 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -356,7 +356,7 @@ CompiledMethod* ArtCompileDEX( 0, 0, 0, - nullptr, // src_mapping_table + ArrayRef<const SrcMapElem>(), // src_mapping_table ArrayRef<const uint8_t>(), // mapping_table ArrayRef<const uint8_t>(builder.GetData()), // vmap_table ArrayRef<const uint8_t>(), // gc_map diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index cde99b3fae..d68835a9cf 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -22,6 +22,7 @@ #endif #include "base/bit_vector-inl.h" +#include "base/stringprintf.h" #include "dex/mir_graph.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -1165,7 +1166,7 @@ CompiledMethod* Mir2Lir::GetCompiledMethod() { cu_->compiler_driver, cu_->instruction_set, ArrayRef<const uint8_t>(code_buffer_), frame_size_, core_spill_mask_, fp_spill_mask_, - &src_mapping_table_, + ArrayRef<const SrcMapElem>(src_mapping_table_), ArrayRef<const uint8_t>(encoded_mapping_table_), ArrayRef<const uint8_t>(vmap_encoder.GetData()), ArrayRef<const uint8_t>(native_gc_map_), diff --git a/compiler/dex/quick/ralloc_util.cc b/compiler/dex/quick/ralloc_util.cc index d9d0434e8a..dceb118d1e 100644 --- a/compiler/dex/quick/ralloc_util.cc +++ b/compiler/dex/quick/ralloc_util.cc @@ -18,6 +18,7 @@ #include "mir_to_lir-inl.h" +#include "base/stringprintf.h" #include "dex/compiler_ir.h" #include "dex/dataflow_iterator-inl.h" #include "dex/mir_graph.h" diff --git a/compiler/driver/compiled_method_storage.cc b/compiler/driver/compiled_method_storage.cc new file mode 100644 index 0000000000..bc5c6cab87 --- /dev/null +++ b/compiler/driver/compiled_method_storage.cc @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <algorithm> +#include <ostream> + +#include "compiled_method_storage.h" + +#include "base/logging.h" +#include "compiled_method.h" +#include "thread-inl.h" +#include "utils.h" +#include "utils/dedupe_set-inl.h" +#include "utils/swap_space.h" + +namespace art { + +namespace { // anonymous namespace + +template <typename T> +const LengthPrefixedArray<T>* CopyArray(SwapSpace* swap_space, const ArrayRef<const T>& array) { + DCHECK(!array.empty()); + SwapAllocator<uint8_t> allocator(swap_space); + void* storage = allocator.allocate(LengthPrefixedArray<T>::ComputeSize(array.size())); + LengthPrefixedArray<T>* array_copy = new(storage) LengthPrefixedArray<T>(array.size()); + std::copy(array.begin(), array.end(), array_copy->begin()); + return array_copy; +} + +template <typename T> +void ReleaseArray(SwapSpace* swap_space, const LengthPrefixedArray<T>* array) { + SwapAllocator<uint8_t> allocator(swap_space); + size_t size = LengthPrefixedArray<T>::ComputeSize(array->size()); + array->~LengthPrefixedArray<T>(); + allocator.deallocate(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(array)), size); +} + +} // anonymous namespace + +template <typename T, typename DedupeSetType> +inline const LengthPrefixedArray<T>* CompiledMethodStorage::AllocateOrDeduplicateArray( + const ArrayRef<const T>& data, + DedupeSetType* dedupe_set) { + if (data.empty()) { + return nullptr; + } else if (!DedupeEnabled()) { + return CopyArray(swap_space_.get(), data); + } else { + return dedupe_set->Add(Thread::Current(), data); + } +} + +template <typename T> +inline void CompiledMethodStorage::ReleaseArrayIfNotDeduplicated( + const LengthPrefixedArray<T>* array) { + if (array != nullptr && !DedupeEnabled()) { + ReleaseArray(swap_space_.get(), array); + } +} + +template <typename ContentType> +class CompiledMethodStorage::DedupeHashFunc { + private: + static constexpr bool kUseMurmur3Hash = true; + + public: + size_t operator()(const ArrayRef<ContentType>& array) const { + const uint8_t* data = reinterpret_cast<const uint8_t*>(array.data()); + // TODO: More reasonable assertion. + // static_assert(IsPowerOfTwo(sizeof(ContentType)), + // "ContentType is not power of two, don't know whether array layout is as assumed"); + uint32_t len = sizeof(ContentType) * array.size(); + if (kUseMurmur3Hash) { + static constexpr uint32_t c1 = 0xcc9e2d51; + static constexpr uint32_t c2 = 0x1b873593; + static constexpr uint32_t r1 = 15; + static constexpr uint32_t r2 = 13; + static constexpr uint32_t m = 5; + static constexpr uint32_t n = 0xe6546b64; + + uint32_t hash = 0; + + const int nblocks = len / 4; + typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; + const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(data); + int i; + for (i = 0; i < nblocks; i++) { + uint32_t k = blocks[i]; + k *= c1; + k = (k << r1) | (k >> (32 - r1)); + k *= c2; + + hash ^= k; + hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; + } + + const uint8_t *tail = reinterpret_cast<const uint8_t*>(data + nblocks * 4); + uint32_t k1 = 0; + + switch (len & 3) { + case 3: + k1 ^= tail[2] << 16; + FALLTHROUGH_INTENDED; + case 2: + k1 ^= tail[1] << 8; + FALLTHROUGH_INTENDED; + case 1: + k1 ^= tail[0]; + + k1 *= c1; + k1 = (k1 << r1) | (k1 >> (32 - r1)); + k1 *= c2; + hash ^= k1; + } + + hash ^= len; + hash ^= (hash >> 16); + hash *= 0x85ebca6b; + hash ^= (hash >> 13); + hash *= 0xc2b2ae35; + hash ^= (hash >> 16); + + return hash; + } else { + size_t hash = 0x811c9dc5; + for (uint32_t i = 0; i < len; ++i) { + hash = (hash * 16777619) ^ data[i]; + } + hash += hash << 13; + hash ^= hash >> 7; + hash += hash << 3; + hash ^= hash >> 17; + hash += hash << 5; + return hash; + } + } +}; + +template <typename T> +class CompiledMethodStorage::LengthPrefixedArrayAlloc { + public: + explicit LengthPrefixedArrayAlloc(SwapSpace* swap_space) + : swap_space_(swap_space) { + } + + const LengthPrefixedArray<T>* Copy(const ArrayRef<const T>& array) { + return CopyArray(swap_space_, array); + } + + void Destroy(const LengthPrefixedArray<T>* array) { + ReleaseArray(swap_space_, array); + } + + private: + SwapSpace* const swap_space_; +}; + +CompiledMethodStorage::CompiledMethodStorage(int swap_fd) + : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), + dedupe_enabled_(true), + dedupe_code_("dedupe code", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_src_mapping_table_("dedupe source mapping table", + LengthPrefixedArrayAlloc<SrcMapElem>(swap_space_.get())), + dedupe_mapping_table_("dedupe mapping table", + LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_vmap_table_("dedupe vmap table", + LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_gc_map_("dedupe gc map", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_cfi_info_("dedupe cfi info", LengthPrefixedArrayAlloc<uint8_t>(swap_space_.get())), + dedupe_linker_patches_("dedupe cfi info", + LengthPrefixedArrayAlloc<LinkerPatch>(swap_space_.get())) { +} + +CompiledMethodStorage::~CompiledMethodStorage() { + // All done by member destructors. +} + +void CompiledMethodStorage::DumpMemoryUsage(std::ostream& os, bool extended) const { + if (swap_space_.get() != nullptr) { + os << " swap=" << PrettySize(swap_space_->GetSize()); + } + if (extended) { + Thread* self = Thread::Current(); + os << "\nCode dedupe: " << dedupe_code_.DumpStats(self); + os << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats(self); + os << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(self); + os << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats(self); + os << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(self); + } +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCode( + const ArrayRef<const uint8_t>& code) { + return AllocateOrDeduplicateArray(code, &dedupe_code_); +} + +void CompiledMethodStorage::ReleaseCode(const LengthPrefixedArray<uint8_t>* code) { + ReleaseArrayIfNotDeduplicated(code); +} + +const LengthPrefixedArray<SrcMapElem>* CompiledMethodStorage::DeduplicateSrcMappingTable( + const ArrayRef<const SrcMapElem>& src_map) { + return AllocateOrDeduplicateArray(src_map, &dedupe_src_mapping_table_); +} + +void CompiledMethodStorage::ReleaseSrcMappingTable(const LengthPrefixedArray<SrcMapElem>* src_map) { + ReleaseArrayIfNotDeduplicated(src_map); +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateMappingTable( + const ArrayRef<const uint8_t>& table) { + return AllocateOrDeduplicateArray(table, &dedupe_mapping_table_); +} + +void CompiledMethodStorage::ReleaseMappingTable(const LengthPrefixedArray<uint8_t>* table) { + ReleaseArrayIfNotDeduplicated(table); +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateVMapTable( + const ArrayRef<const uint8_t>& table) { + return AllocateOrDeduplicateArray(table, &dedupe_vmap_table_); +} + +void CompiledMethodStorage::ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table) { + ReleaseArrayIfNotDeduplicated(table); +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateGCMap( + const ArrayRef<const uint8_t>& gc_map) { + return AllocateOrDeduplicateArray(gc_map, &dedupe_gc_map_); +} + +void CompiledMethodStorage::ReleaseGCMap(const LengthPrefixedArray<uint8_t>* gc_map) { + ReleaseArrayIfNotDeduplicated(gc_map); +} + +const LengthPrefixedArray<uint8_t>* CompiledMethodStorage::DeduplicateCFIInfo( + const ArrayRef<const uint8_t>& cfi_info) { + return AllocateOrDeduplicateArray(cfi_info, &dedupe_cfi_info_); +} + +void CompiledMethodStorage::ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info) { + ReleaseArrayIfNotDeduplicated(cfi_info); +} + +const LengthPrefixedArray<LinkerPatch>* CompiledMethodStorage::DeduplicateLinkerPatches( + const ArrayRef<const LinkerPatch>& linker_patches) { + return AllocateOrDeduplicateArray(linker_patches, &dedupe_linker_patches_); +} + +void CompiledMethodStorage::ReleaseLinkerPatches( + const LengthPrefixedArray<LinkerPatch>* linker_patches) { + ReleaseArrayIfNotDeduplicated(linker_patches); +} + +} // namespace art diff --git a/compiler/driver/compiled_method_storage.h b/compiler/driver/compiled_method_storage.h new file mode 100644 index 0000000000..ef10b6768b --- /dev/null +++ b/compiler/driver/compiled_method_storage.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ +#define ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ + +#include <iosfwd> +#include <memory> + +#include "base/macros.h" +#include "length_prefixed_array.h" +#include "utils/array_ref.h" +#include "utils/dedupe_set.h" +#include "utils/swap_space.h" + +namespace art { + +class LinkerPatch; +class SrcMapElem; + +class CompiledMethodStorage { + public: + explicit CompiledMethodStorage(int swap_fd); + ~CompiledMethodStorage(); + + void DumpMemoryUsage(std::ostream& os, bool extended) const; + + void SetDedupeEnabled(bool dedupe_enabled) { + dedupe_enabled_ = dedupe_enabled; + } + bool DedupeEnabled() const { + return dedupe_enabled_; + } + + SwapAllocator<void> GetSwapSpaceAllocator() { + return SwapAllocator<void>(swap_space_.get()); + } + + const LengthPrefixedArray<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code); + void ReleaseCode(const LengthPrefixedArray<uint8_t>* code); + + const LengthPrefixedArray<SrcMapElem>* DeduplicateSrcMappingTable( + const ArrayRef<const SrcMapElem>& src_map); + void ReleaseSrcMappingTable(const LengthPrefixedArray<SrcMapElem>* src_map); + + const LengthPrefixedArray<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& table); + void ReleaseMappingTable(const LengthPrefixedArray<uint8_t>* table); + + const LengthPrefixedArray<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& table); + void ReleaseVMapTable(const LengthPrefixedArray<uint8_t>* table); + + const LengthPrefixedArray<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& gc_map); + void ReleaseGCMap(const LengthPrefixedArray<uint8_t>* gc_map); + + const LengthPrefixedArray<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info); + void ReleaseCFIInfo(const LengthPrefixedArray<uint8_t>* cfi_info); + + const LengthPrefixedArray<LinkerPatch>* DeduplicateLinkerPatches( + const ArrayRef<const LinkerPatch>& linker_patches); + void ReleaseLinkerPatches(const LengthPrefixedArray<LinkerPatch>* linker_patches); + + private: + template <typename T, typename DedupeSetType> + const LengthPrefixedArray<T>* AllocateOrDeduplicateArray(const ArrayRef<const T>& data, + DedupeSetType* dedupe_set); + + template <typename T> + void ReleaseArrayIfNotDeduplicated(const LengthPrefixedArray<T>* array); + + // DeDuplication data structures. + template <typename ContentType> + class DedupeHashFunc; + + template <typename T> + class LengthPrefixedArrayAlloc; + + template <typename T> + using ArrayDedupeSet = DedupeSet<ArrayRef<const T>, + LengthPrefixedArray<T>, + LengthPrefixedArrayAlloc<T>, + size_t, + DedupeHashFunc<const T>, + 4>; + + // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first + // as other fields rely on this. + std::unique_ptr<SwapSpace> swap_space_; + + bool dedupe_enabled_; + + ArrayDedupeSet<uint8_t> dedupe_code_; + ArrayDedupeSet<SrcMapElem> dedupe_src_mapping_table_; + ArrayDedupeSet<uint8_t> dedupe_mapping_table_; + ArrayDedupeSet<uint8_t> dedupe_vmap_table_; + ArrayDedupeSet<uint8_t> dedupe_gc_map_; + ArrayDedupeSet<uint8_t> dedupe_cfi_info_; + ArrayDedupeSet<LinkerPatch> dedupe_linker_patches_; + + DISALLOW_COPY_AND_ASSIGN(CompiledMethodStorage); +}; + +} // namespace art + +#endif // ART_COMPILER_DRIVER_COMPILED_METHOD_STORAGE_H_ diff --git a/compiler/driver/compiled_method_storage_test.cc b/compiler/driver/compiled_method_storage_test.cc new file mode 100644 index 0000000000..c6dbd24bf8 --- /dev/null +++ b/compiler/driver/compiled_method_storage_test.cc @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <gtest/gtest.h> + +#include "compiled_method_storage.h" +#include "compiled_method.h" +#include "compiler_driver.h" +#include "compiler_options.h" +#include "dex/verification_results.h" +#include "dex/quick/dex_file_to_method_inliner_map.h" + +namespace art { + +TEST(CompiledMethodStorage, Deduplicate) { + CompilerOptions compiler_options; + VerificationResults verification_results(&compiler_options); + DexFileToMethodInlinerMap method_inliner_map; + CompilerDriver driver(&compiler_options, + &verification_results, + &method_inliner_map, + Compiler::kOptimizing, kNone, + nullptr, + false, + nullptr, + nullptr, + nullptr, + 1u, + false, + false, + "", + false, + nullptr, + -1, + ""); + CompiledMethodStorage* storage = driver.GetCompiledMethodStorage(); + + ASSERT_TRUE(storage->DedupeEnabled()); // The default. + + const uint8_t raw_code1[] = { 1u, 2u, 3u }; + const uint8_t raw_code2[] = { 4u, 3u, 2u, 1u }; + ArrayRef<const uint8_t> code[] = { + ArrayRef<const uint8_t>(raw_code1), + ArrayRef<const uint8_t>(raw_code2), + }; + const SrcMapElem raw_src_map1[] = { { 1u, 2u }, { 3u, 4u }, { 5u, 6u } }; + const SrcMapElem raw_src_map2[] = { { 8u, 7u }, { 6u, 5u }, { 4u, 3u }, { 2u, 1u } }; + ArrayRef<const SrcMapElem> src_map[] = { + ArrayRef<const SrcMapElem>(raw_src_map1), + ArrayRef<const SrcMapElem>(raw_src_map2), + }; + const uint8_t raw_mapping_table1[] = { 5, 6, 7 }; + const uint8_t raw_mapping_table2[] = { 7, 6, 5, 4 }; + ArrayRef<const uint8_t> mapping_table[] = { + ArrayRef<const uint8_t>(raw_mapping_table1), + ArrayRef<const uint8_t>(raw_mapping_table2), + }; + const uint8_t raw_vmap_table1[] = { 2, 4, 6 }; + const uint8_t raw_vmap_table2[] = { 7, 5, 3, 1 }; + ArrayRef<const uint8_t> vmap_table[] = { + ArrayRef<const uint8_t>(raw_vmap_table1), + ArrayRef<const uint8_t>(raw_vmap_table2), + }; + const uint8_t raw_gc_map1[] = { 9, 8, 7 }; + const uint8_t raw_gc_map2[] = { 6, 7, 8, 9 }; + ArrayRef<const uint8_t> gc_map[] = { + ArrayRef<const uint8_t>(raw_gc_map1), + ArrayRef<const uint8_t>(raw_gc_map2), + }; + const uint8_t raw_cfi_info1[] = { 1, 3, 5 }; + const uint8_t raw_cfi_info2[] = { 8, 6, 4, 2 }; + ArrayRef<const uint8_t> cfi_info[] = { + ArrayRef<const uint8_t>(raw_cfi_info1), + ArrayRef<const uint8_t>(raw_cfi_info2), + }; + const LinkerPatch raw_patches1[] = { + LinkerPatch::CodePatch(0u, nullptr, 1u), + LinkerPatch::MethodPatch(4u, nullptr, 1u), + }; + const LinkerPatch raw_patches2[] = { + LinkerPatch::CodePatch(0u, nullptr, 1u), + LinkerPatch::MethodPatch(4u, nullptr, 2u), + }; + ArrayRef<const LinkerPatch> patches[] = { + ArrayRef<const LinkerPatch>(raw_patches1), + ArrayRef<const LinkerPatch>(raw_patches2), + }; + + std::vector<CompiledMethod*> compiled_methods; + compiled_methods.reserve(1u << 7); + for (auto&& c : code) { + for (auto&& s : src_map) { + for (auto&& m : mapping_table) { + for (auto&& v : vmap_table) { + for (auto&& g : gc_map) { + for (auto&& f : cfi_info) { + for (auto&& p : patches) { + compiled_methods.push_back(CompiledMethod::SwapAllocCompiledMethod( + &driver, kNone, c, 0u, 0u, 0u, s, m, v, g, f, p)); + } + } + } + } + } + } + } + constexpr size_t code_bit = 1u << 6; + constexpr size_t src_map_bit = 1u << 5; + constexpr size_t mapping_table_bit = 1u << 4; + constexpr size_t vmap_table_bit = 1u << 3; + constexpr size_t gc_map_bit = 1u << 2; + constexpr size_t cfi_info_bit = 1u << 1; + constexpr size_t patches_bit = 1u << 0; + CHECK_EQ(compiled_methods.size(), 1u << 7); + for (size_t i = 0; i != compiled_methods.size(); ++i) { + for (size_t j = 0; j != compiled_methods.size(); ++j) { + CompiledMethod* lhs = compiled_methods[i]; + CompiledMethod* rhs = compiled_methods[j]; + bool same_code = ((i ^ j) & code_bit) == 0u; + bool same_src_map = ((i ^ j) & src_map_bit) == 0u; + bool same_mapping_table = ((i ^ j) & mapping_table_bit) == 0u; + bool same_vmap_table = ((i ^ j) & vmap_table_bit) == 0u; + bool same_gc_map = ((i ^ j) & gc_map_bit) == 0u; + bool same_cfi_info = ((i ^ j) & cfi_info_bit) == 0u; + bool same_patches = ((i ^ j) & patches_bit) == 0u; + ASSERT_EQ(same_code, lhs->GetQuickCode().data() == rhs->GetQuickCode().data()) + << i << " " << j; + ASSERT_EQ(same_src_map, lhs->GetSrcMappingTable().data() == rhs->GetSrcMappingTable().data()) + << i << " " << j; + ASSERT_EQ(same_mapping_table, lhs->GetMappingTable().data() == rhs->GetMappingTable().data()) + << i << " " << j; + ASSERT_EQ(same_vmap_table, lhs->GetVmapTable().data() == rhs->GetVmapTable().data()) + << i << " " << j; + ASSERT_EQ(same_gc_map, lhs->GetGcMap().data() == rhs->GetGcMap().data()) + << i << " " << j; + ASSERT_EQ(same_cfi_info, lhs->GetCFIInfo().data() == rhs->GetCFIInfo().data()) + << i << " " << j; + ASSERT_EQ(same_patches, lhs->GetPatches().data() == rhs->GetPatches().data()) + << i << " " << j; + } + } + for (CompiledMethod* method : compiled_methods) { + CompiledMethod::ReleaseSwapAllocatedCompiledMethod(&driver, method); + } +} + +} // namespace art diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index b9565846ce..8750aa8e4e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -348,9 +348,8 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, const std::string& dump_cfg_file_name, bool dump_cfg_append, CumulativeLogger* timer, int swap_fd, const std::string& profile_file) - : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), - swap_space_allocator_(new SwapAllocator<void>(swap_space_.get())), - profile_present_(false), compiler_options_(compiler_options), + : profile_present_(false), + compiler_options_(compiler_options), verification_results_(verification_results), method_inliner_map_(method_inliner_map), compiler_(Compiler::Create(this, compiler_kind)), @@ -369,7 +368,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, had_hard_verifier_failure_(false), thread_count_(thread_count), stats_(new AOTCompilationStats), - dedupe_enabled_(true), dump_stats_(dump_stats), dump_passes_(dump_passes), dump_cfg_file_name_(dump_cfg_file_name), @@ -377,12 +375,7 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, timings_logger_(timer), compiler_context_(nullptr), support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64), - dedupe_code_("dedupe code", *swap_space_allocator_), - dedupe_src_mapping_table_("dedupe source mapping table", *swap_space_allocator_), - dedupe_mapping_table_("dedupe mapping table", *swap_space_allocator_), - dedupe_vmap_table_("dedupe vmap table", *swap_space_allocator_), - dedupe_gc_map_("dedupe gc map", *swap_space_allocator_), - dedupe_cfi_info_("dedupe cfi info", *swap_space_allocator_) { + compiled_method_storage_(swap_fd) { DCHECK(compiler_options_ != nullptr); DCHECK(verification_results_ != nullptr); DCHECK(method_inliner_map_ != nullptr); @@ -402,36 +395,6 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, } } -SwapVector<uint8_t>* CompilerDriver::DeduplicateCode(const ArrayRef<const uint8_t>& code) { - DCHECK(dedupe_enabled_); - return dedupe_code_.Add(Thread::Current(), code); -} - -SwapSrcMap* CompilerDriver::DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map) { - DCHECK(dedupe_enabled_); - return dedupe_src_mapping_table_.Add(Thread::Current(), src_map); -} - -SwapVector<uint8_t>* CompilerDriver::DeduplicateMappingTable(const ArrayRef<const uint8_t>& code) { - DCHECK(dedupe_enabled_); - return dedupe_mapping_table_.Add(Thread::Current(), code); -} - -SwapVector<uint8_t>* CompilerDriver::DeduplicateVMapTable(const ArrayRef<const uint8_t>& code) { - DCHECK(dedupe_enabled_); - return dedupe_vmap_table_.Add(Thread::Current(), code); -} - -SwapVector<uint8_t>* CompilerDriver::DeduplicateGCMap(const ArrayRef<const uint8_t>& code) { - DCHECK(dedupe_enabled_); - return dedupe_gc_map_.Add(Thread::Current(), code); -} - -SwapVector<uint8_t>* CompilerDriver::DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info) { - DCHECK(dedupe_enabled_); - return dedupe_cfi_info_.Add(Thread::Current(), cfi_info); -} - CompilerDriver::~CompilerDriver() { Thread* self = Thread::Current(); { @@ -447,6 +410,7 @@ CompilerDriver::~CompilerDriver() { compiler_->UnInit(); } + #define CREATE_TRAMPOLINE(type, abi, offset) \ if (Is64BitInstructionSet(instruction_set_)) { \ return CreateTrampoline64(instruction_set_, abi, \ @@ -2642,16 +2606,7 @@ std::string CompilerDriver::GetMemoryUsageString(bool extended) const { oss << " native alloc=" << PrettySize(allocated_space) << " free=" << PrettySize(free_space); #endif - if (swap_space_.get() != nullptr) { - oss << " swap=" << PrettySize(swap_space_->GetSize()); - } - if (extended) { - oss << "\nCode dedupe: " << dedupe_code_.DumpStats(); - oss << "\nMapping table dedupe: " << dedupe_mapping_table_.DumpStats(); - oss << "\nVmap table dedupe: " << dedupe_vmap_table_.DumpStats(); - oss << "\nGC map dedupe: " << dedupe_gc_map_.DumpStats(); - oss << "\nCFI info dedupe: " << dedupe_cfi_info_.DumpStats(); - } + compiled_method_storage_.DumpMemoryUsage(oss, extended); return oss.str(); } diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 0dc8261aac..485cdcfb1b 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -30,6 +30,7 @@ #include "class_reference.h" #include "compiler.h" #include "dex_file.h" +#include "driver/compiled_method_storage.h" #include "invoke_type.h" #include "method_reference.h" #include "mirror/class.h" // For mirror::Class::Status. @@ -38,10 +39,7 @@ #include "runtime.h" #include "safe_map.h" #include "thread_pool.h" -#include "utils/array_ref.h" -#include "utils/dedupe_set.h" #include "utils/dex_cache_arrays_layout.h" -#include "utils/swap_space.h" namespace art { @@ -80,8 +78,6 @@ enum EntryPointCallingConvention { kQuickAbi }; -static constexpr bool kUseMurmur3Hash = true; - class CompilerDriver { public: // Create a compiler targeting the requested "instruction_set". @@ -388,10 +384,6 @@ class CompilerDriver { support_boot_image_fixup_ = support_boot_image_fixup; } - SwapAllocator<void>& GetSwapSpaceAllocator() { - return *swap_space_allocator_.get(); - } - bool WriteElf(const std::string& android_root, bool is_host, const std::vector<const DexFile*>& dex_files, @@ -431,10 +423,10 @@ class CompilerDriver { } void SetDedupeEnabled(bool dedupe_enabled) { - dedupe_enabled_ = dedupe_enabled; + compiled_method_storage_.SetDedupeEnabled(dedupe_enabled); } bool DedupeEnabled() const { - return dedupe_enabled_; + return compiled_method_storage_.DedupeEnabled(); } // Checks if class specified by type_idx is one of the image_classes_ @@ -455,13 +447,6 @@ class CompilerDriver { uint16_t class_def_idx, const DexFile& dex_file) const; - SwapVector<uint8_t>* DeduplicateCode(const ArrayRef<const uint8_t>& code); - SwapSrcMap* DeduplicateSrcMappingTable(const ArrayRef<SrcMapElem>& src_map); - SwapVector<uint8_t>* DeduplicateMappingTable(const ArrayRef<const uint8_t>& code); - SwapVector<uint8_t>* DeduplicateVMapTable(const ArrayRef<const uint8_t>& code); - SwapVector<uint8_t>* DeduplicateGCMap(const ArrayRef<const uint8_t>& code); - SwapVector<uint8_t>* DeduplicateCFIInfo(const ArrayRef<const uint8_t>& cfi_info); - // Should the compiler run on this method given profile information? bool SkipCompilation(const std::string& method_name); @@ -479,6 +464,10 @@ class CompilerDriver { return compiler_kind_; } + CompiledMethodStorage* GetCompiledMethodStorage() { + return &compiled_method_storage_; + } + private: // Return whether the declaring class of `resolved_member` is // available to `referrer_class` for read or write access using two @@ -599,11 +588,6 @@ class CompilerDriver { ThreadPool* thread_pool, TimingLogger* timings) REQUIRES(!Locks::mutator_lock_); - // Swap pool and allocator used for native allocations. May be file-backed. Needs to be first - // as other fields rely on this. - std::unique_ptr<SwapSpace> swap_space_; - std::unique_ptr<SwapAllocator<void> > swap_space_allocator_; - ProfileFile profile_file_; bool profile_present_; @@ -663,7 +647,6 @@ class CompilerDriver { class AOTCompilationStats; std::unique_ptr<AOTCompilationStats> stats_; - bool dedupe_enabled_; bool dump_stats_; const bool dump_passes_; const std::string dump_cfg_file_name_; @@ -678,93 +661,7 @@ class CompilerDriver { bool support_boot_image_fixup_; - // DeDuplication data structures, these own the corresponding byte arrays. - template <typename ContentType> - class DedupeHashFunc { - public: - size_t operator()(const ArrayRef<ContentType>& array) const { - const uint8_t* data = reinterpret_cast<const uint8_t*>(array.data()); - static_assert(IsPowerOfTwo(sizeof(ContentType)), - "ContentType is not power of two, don't know whether array layout is as assumed"); - uint32_t len = sizeof(ContentType) * array.size(); - if (kUseMurmur3Hash) { - static constexpr uint32_t c1 = 0xcc9e2d51; - static constexpr uint32_t c2 = 0x1b873593; - static constexpr uint32_t r1 = 15; - static constexpr uint32_t r2 = 13; - static constexpr uint32_t m = 5; - static constexpr uint32_t n = 0xe6546b64; - - uint32_t hash = 0; - - const int nblocks = len / 4; - typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - const unaligned_uint32_t *blocks = reinterpret_cast<const uint32_t*>(data); - int i; - for (i = 0; i < nblocks; i++) { - uint32_t k = blocks[i]; - k *= c1; - k = (k << r1) | (k >> (32 - r1)); - k *= c2; - - hash ^= k; - hash = ((hash << r2) | (hash >> (32 - r2))) * m + n; - } - - const uint8_t *tail = reinterpret_cast<const uint8_t*>(data + nblocks * 4); - uint32_t k1 = 0; - - switch (len & 3) { - case 3: - k1 ^= tail[2] << 16; - FALLTHROUGH_INTENDED; - case 2: - k1 ^= tail[1] << 8; - FALLTHROUGH_INTENDED; - case 1: - k1 ^= tail[0]; - - k1 *= c1; - k1 = (k1 << r1) | (k1 >> (32 - r1)); - k1 *= c2; - hash ^= k1; - } - - hash ^= len; - hash ^= (hash >> 16); - hash *= 0x85ebca6b; - hash ^= (hash >> 13); - hash *= 0xc2b2ae35; - hash ^= (hash >> 16); - - return hash; - } else { - size_t hash = 0x811c9dc5; - for (uint32_t i = 0; i < len; ++i) { - hash = (hash * 16777619) ^ data[i]; - } - hash += hash << 13; - hash ^= hash >> 7; - hash += hash << 3; - hash ^= hash >> 17; - hash += hash << 5; - return hash; - } - } - }; - - DedupeSet<ArrayRef<const uint8_t>, - SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_code_; - DedupeSet<ArrayRef<SrcMapElem>, - SwapSrcMap, size_t, DedupeHashFunc<SrcMapElem>, 4> dedupe_src_mapping_table_; - DedupeSet<ArrayRef<const uint8_t>, - SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_mapping_table_; - DedupeSet<ArrayRef<const uint8_t>, - SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_vmap_table_; - DedupeSet<ArrayRef<const uint8_t>, - SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_gc_map_; - DedupeSet<ArrayRef<const uint8_t>, - SwapVector<uint8_t>, size_t, DedupeHashFunc<const uint8_t>, 4> dedupe_cfi_info_; + CompiledMethodStorage compiled_method_storage_; friend class CompileClassVisitor; DISALLOW_COPY_AND_ASSIGN(CompilerDriver); diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc index a07d27c1d2..3ba380e9db 100644 --- a/compiler/dwarf/dwarf_test.cc +++ b/compiler/dwarf/dwarf_test.cc @@ -126,7 +126,7 @@ TEST_F(DwarfTest, DebugFrame) { initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; std::vector<uintptr_t> expected_patches { 28 }; // NOLINT - WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(), + WriteDebugFrameFDE(is64bit, 0, 0x01000000, 0x01000000, ArrayRef<const uint8_t>(*opcodes.data()), kCFIFormat, &debug_frame_data_, &debug_frame_patches); EXPECT_EQ(expected_patches, debug_frame_patches); @@ -142,7 +142,8 @@ TEST_F(DwarfTest, DebugFrame64) { std::vector<uintptr_t> debug_frame_patches; std::vector<uintptr_t> expected_patches { 32 }; // NOLINT WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000, - opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches); + ArrayRef<const uint8_t>(*opcodes.data()), + kCFIFormat, &debug_frame_data_, &debug_frame_patches); DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000"); EXPECT_EQ(expected_patches, debug_frame_patches); @@ -179,7 +180,8 @@ TEST_F(DwarfTest, x86_64_RegisterMapping) { initial_opcodes, kCFIFormat, &debug_frame_data_); std::vector<uintptr_t> debug_frame_patches; WriteDebugFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000, - opcodes.data(), kCFIFormat, &debug_frame_data_, &debug_frame_patches); + ArrayRef<const uint8_t>(*opcodes.data()), + kCFIFormat, &debug_frame_data_, &debug_frame_patches); CheckObjdumpOutput(is64bit, "-W"); } diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h index b7eff19bed..f3fba4b1fa 100644 --- a/compiler/dwarf/headers.h +++ b/compiler/dwarf/headers.h @@ -25,6 +25,7 @@ #include "dwarf/dwarf_constants.h" #include "dwarf/register.h" #include "dwarf/writer.h" +#include "utils/array_ref.h" namespace art { namespace dwarf { @@ -70,21 +71,19 @@ void WriteDebugFrameCIE(bool is64bit, writer.PushUint8(DW_EH_PE_absptr | DW_EH_PE_udata4); // R: Pointer encoding. } } - writer.PushData(opcodes.data()); + writer.PushData(*opcodes.data()); writer.Pad(is64bit ? 8 : 4); writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4); } // Write frame description entry (FDE) to .debug_frame or .eh_frame section. -template<typename Vector> +inline void WriteDebugFrameFDE(bool is64bit, size_t cie_offset, uint64_t initial_address, uint64_t address_range, - const Vector* opcodes, + const ArrayRef<const uint8_t>& opcodes, CFIFormat format, std::vector<uint8_t>* debug_frame, std::vector<uintptr_t>* debug_frame_patches) { - static_assert(std::is_same<typename Vector::value_type, uint8_t>::value, "Invalid value type"); - Writer<> writer(debug_frame); size_t fde_header_start = writer.data()->size(); writer.PushUint32(0); // Length placeholder. @@ -125,7 +124,7 @@ void WriteDebugInfoCU(uint32_t debug_abbrev_offset, writer.PushUint32(debug_abbrev_offset); writer.PushUint8(entries.Is64bit() ? 8 : 4); size_t entries_offset = writer.data()->size(); - writer.PushData(entries.data()); + writer.PushData(*entries.data()); writer.UpdateUint32(start, writer.data()->size() - start - 4); // Copy patch locations and make them relative to .debug_info section. for (uintptr_t patch_location : entries.GetPatchLocations()) { @@ -181,7 +180,7 @@ void WriteDebugLineTable(const std::vector<std::string>& include_directories, writer.PushUint8(0); // Terminate file list. writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4); size_t opcodes_offset = writer.data()->size(); - writer.PushData(opcodes.data()); + writer.PushData(*opcodes.data()); writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4); // Copy patch locations and make them relative to .debug_line section. for (uintptr_t patch_location : opcodes.GetPatchLocations()) { diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h index 42c32c4303..00b9dfa303 100644 --- a/compiler/dwarf/writer.h +++ b/compiler/dwarf/writer.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_DWARF_WRITER_H_ #define ART_COMPILER_DWARF_WRITER_H_ +#include <type_traits> #include <vector> #include "base/bit_utils.h" #include "base/logging.h" @@ -119,9 +120,10 @@ class Writer { } template<typename Vector2> - void PushData(const Vector2* buffer) { - static_assert(std::is_same<typename Vector2::value_type, uint8_t>::value, "Invalid value type"); - data_->insert(data_->end(), buffer->begin(), buffer->end()); + void PushData(const Vector2& buffer) { + static_assert(std::is_same<typename std::add_const<typename Vector::value_type>::type, + const uint8_t>::value, "Invalid value type"); + data_->insert(data_->end(), buffer.begin(), buffer.end()); } void UpdateUint32(size_t offset, uint32_t value) { diff --git a/compiler/elf_writer_debug.cc b/compiler/elf_writer_debug.cc index c10ffebbbc..3a9e312225 100644 --- a/compiler/elf_writer_debug.cc +++ b/compiler/elf_writer_debug.cc @@ -182,8 +182,8 @@ void WriteCFISection(const CompilerDriver* compiler, WriteDebugFrameCIE(isa, address_type, format, debug_frame); for (const OatWriter::DebugInfo& mi : method_infos) { if (!mi.deduped_) { // Only one FDE per unique address. - const SwapVector<uint8_t>* opcodes = mi.compiled_method_->GetCFIInfo(); - if (opcodes != nullptr) { + ArrayRef<const uint8_t> opcodes = mi.compiled_method_->GetCFIInfo(); + if (!opcodes.empty()) { address_to_fde_offset_map.emplace(mi.low_pc_, debug_frame->size()); WriteDebugFrameFDE(Is64BitInstructionSet(isa), cie_offset, mi.low_pc_, mi.high_pc_ - mi.low_pc_, diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 4310be6464..0e5a97ffbd 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -796,7 +796,7 @@ void ImageWriter::WalkFieldsInOrder(mirror::Object* obj) { offset, kNativeObjectRelocationTypeArtFieldArray }); offset += header_size; // Forward individual fields so that we can quickly find where they belong. - for (size_t i = 0, count = cur_fields->Length(); i < count; ++i) { + for (size_t i = 0, count = cur_fields->size(); i < count; ++i) { // Need to forward arrays separate of fields. ArtField* field = &cur_fields->At(i); auto it2 = native_object_relocations_.find(field); diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index 3d1b42f51c..a47c601de8 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -231,39 +231,39 @@ bool JitCompiler::AddToCodeCache(ArtMethod* method, OatFile::OatMethod* out_method) { Runtime* runtime = Runtime::Current(); JitCodeCache* const code_cache = runtime->GetJit()->GetCodeCache(); - const auto* quick_code = compiled_method->GetQuickCode(); - if (quick_code == nullptr) { + auto const quick_code = compiled_method->GetQuickCode(); + if (quick_code.empty()) { return false; } - const auto code_size = quick_code->size(); + const auto code_size = quick_code.size(); Thread* const self = Thread::Current(); - auto* const mapping_table = compiled_method->GetMappingTable(); - auto* const vmap_table = compiled_method->GetVmapTable(); - auto* const gc_map = compiled_method->GetGcMap(); + auto const mapping_table = compiled_method->GetMappingTable(); + auto const vmap_table = compiled_method->GetVmapTable(); + auto const gc_map = compiled_method->GetGcMap(); uint8_t* mapping_table_ptr = nullptr; uint8_t* vmap_table_ptr = nullptr; uint8_t* gc_map_ptr = nullptr; - if (mapping_table != nullptr) { + if (!mapping_table.empty()) { // Write out pre-header stuff. mapping_table_ptr = code_cache->AddDataArray( - self, mapping_table->data(), mapping_table->data() + mapping_table->size()); + self, mapping_table.data(), mapping_table.data() + mapping_table.size()); if (mapping_table_ptr == nullptr) { return false; // Out of data cache. } } - if (vmap_table != nullptr) { + if (!vmap_table.empty()) { vmap_table_ptr = code_cache->AddDataArray( - self, vmap_table->data(), vmap_table->data() + vmap_table->size()); + self, vmap_table.data(), vmap_table.data() + vmap_table.size()); if (vmap_table_ptr == nullptr) { return false; // Out of data cache. } } - if (gc_map != nullptr) { + if (!gc_map.empty()) { gc_map_ptr = code_cache->AddDataArray( - self, gc_map->data(), gc_map->data() + gc_map->size()); + self, gc_map.data(), gc_map.data() + gc_map.size()); if (gc_map_ptr == nullptr) { return false; // Out of data cache. } @@ -276,8 +276,8 @@ bool JitCompiler::AddToCodeCache(ArtMethod* method, compiled_method->GetFrameSizeInBytes(), compiled_method->GetCoreSpillMask(), compiled_method->GetFpSpillMask(), - compiled_method->GetQuickCode()->data(), - compiled_method->GetQuickCode()->size()); + compiled_method->GetQuickCode().data(), + compiled_method->GetQuickCode().size()); if (code == nullptr) { return false; diff --git a/compiler/jni/quick/jni_compiler.cc b/compiler/jni/quick/jni_compiler.cc index 34f0802444..52a238233b 100644 --- a/compiler/jni/quick/jni_compiler.cc +++ b/compiler/jni/quick/jni_compiler.cc @@ -487,7 +487,7 @@ CompiledMethod* ArtJniCompileMethodInternal(CompilerDriver* driver, frame_size, main_jni_conv->CoreSpillMask(), main_jni_conv->FpSpillMask(), - nullptr, // src_mapping_table. + ArrayRef<const SrcMapElem>(), ArrayRef<const uint8_t>(), // mapping_table. ArrayRef<const uint8_t>(), // vmap_table. ArrayRef<const uint8_t>(), // native_gc_map. diff --git a/compiler/linker/arm/relative_patcher_arm_base.cc b/compiler/linker/arm/relative_patcher_arm_base.cc index cb9ea38b1c..ac38f3da8a 100644 --- a/compiler/linker/arm/relative_patcher_arm_base.cc +++ b/compiler/linker/arm/relative_patcher_arm_base.cc @@ -85,8 +85,7 @@ uint32_t ArmBaseRelativePatcher::ReserveSpaceInternal(uint32_t offset, const CompiledMethod* compiled_method, MethodReference method_ref, uint32_t max_extra_space) { - DCHECK(compiled_method->GetQuickCode() != nullptr); - uint32_t quick_code_size = compiled_method->GetQuickCode()->size(); + uint32_t quick_code_size = compiled_method->GetQuickCode().size(); uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader); uint32_t next_aligned_offset = compiled_method->AlignCode(quick_code_offset + quick_code_size); // Adjust for extra space required by the subclass. diff --git a/compiler/linker/arm64/relative_patcher_arm64.cc b/compiler/linker/arm64/relative_patcher_arm64.cc index 6f234a8367..57018af840 100644 --- a/compiler/linker/arm64/relative_patcher_arm64.cc +++ b/compiler/linker/arm64/relative_patcher_arm64.cc @@ -74,7 +74,7 @@ uint32_t Arm64RelativePatcher::ReserveSpace(uint32_t offset, // Now that we have the actual offset where the code will be placed, locate the ADRP insns // that actually require the thunk. uint32_t quick_code_offset = compiled_method->AlignCode(offset) + sizeof(OatQuickMethodHeader); - ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode()); + ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); uint32_t thunk_offset = compiled_method->AlignCode(quick_code_offset + code.size()); DCHECK(compiled_method != nullptr); for (const LinkerPatch& patch : compiled_method->GetPatches()) { diff --git a/compiler/linker/arm64/relative_patcher_arm64_test.cc b/compiler/linker/arm64/relative_patcher_arm64_test.cc index 857d5842b5..2a426b5daf 100644 --- a/compiler/linker/arm64/relative_patcher_arm64_test.cc +++ b/compiler/linker/arm64/relative_patcher_arm64_test.cc @@ -237,7 +237,7 @@ class Arm64RelativePatcherTest : public RelativePatcherTest { CHECK(!compiled_method_refs_.empty()); CHECK_EQ(compiled_method_refs_[0].dex_method_index, 1u); CHECK_EQ(compiled_method_refs_.size(), compiled_methods_.size()); - uint32_t method1_size = compiled_methods_[0]->GetQuickCode()->size(); + uint32_t method1_size = compiled_methods_[0]->GetQuickCode().size(); uint32_t thunk_offset = CompiledCode::AlignCode(method1_offset + method1_size, kArm64); uint32_t b_diff = thunk_offset - (method1_offset + num_nops * 4u); ASSERT_EQ(b_diff & 3u, 0u); diff --git a/compiler/linker/relative_patcher_test.h b/compiler/linker/relative_patcher_test.h index e357662388..92cf8ca7ff 100644 --- a/compiler/linker/relative_patcher_test.h +++ b/compiler/linker/relative_patcher_test.h @@ -74,8 +74,8 @@ class RelativePatcherTest : public testing::Test { compiled_method_refs_.push_back(method_ref); compiled_methods_.emplace_back(new CompiledMethod( &driver_, instruction_set_, code, - 0u, 0u, 0u, nullptr, ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), - ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), + 0u, 0u, 0u, ArrayRef<const SrcMapElem>(), ArrayRef<const uint8_t>(), + ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), ArrayRef<const uint8_t>(), patches)); } @@ -93,7 +93,7 @@ class RelativePatcherTest : public testing::Test { offset += sizeof(OatQuickMethodHeader); uint32_t quick_code_offset = offset + compiled_method->CodeDelta(); - const auto& code = *compiled_method->GetQuickCode(); + const auto code = compiled_method->GetQuickCode(); offset += code.size(); method_offset_map_.map.Put(compiled_method_refs_[idx], quick_code_offset); @@ -125,7 +125,7 @@ class RelativePatcherTest : public testing::Test { out_.WriteFully(dummy_header, sizeof(OatQuickMethodHeader)); offset += sizeof(OatQuickMethodHeader); - ArrayRef<const uint8_t> code(*compiled_method->GetQuickCode()); + ArrayRef<const uint8_t> code = compiled_method->GetQuickCode(); if (!compiled_method->GetPatches().empty()) { patched_code_.assign(code.begin(), code.end()); code = ArrayRef<const uint8_t>(patched_code_); @@ -164,7 +164,7 @@ class RelativePatcherTest : public testing::Test { ++idx; } CHECK_NE(idx, compiled_method_refs_.size()); - CHECK_EQ(compiled_methods_[idx]->GetQuickCode()->size(), expected_code.size()); + CHECK_EQ(compiled_methods_[idx]->GetQuickCode().size(), expected_code.size()); auto result = method_offset_map_.FindMethodOffset(method_ref); CHECK(result.first); // Must have been linked. diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index 2d9d91a7ee..06576cc9e1 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -63,9 +63,9 @@ class OatTest : public CommonCompilerTest { EXPECT_EQ(oat_method.GetFpSpillMask(), compiled_method->GetFpSpillMask()); uintptr_t oat_code_aligned = RoundDown(reinterpret_cast<uintptr_t>(quick_oat_code), 2); quick_oat_code = reinterpret_cast<const void*>(oat_code_aligned); - const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - EXPECT_TRUE(quick_code != nullptr); - size_t code_size = quick_code->size() * sizeof(quick_code[0]); + ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); + EXPECT_FALSE(quick_code.empty()); + size_t code_size = quick_code.size() * sizeof(quick_code[0]); EXPECT_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size)) << PrettyMethod(method) << " " << code_size; CHECK_EQ(0, memcmp(quick_oat_code, &quick_code[0], code_size)); diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 640698b0e9..dcb23bf1ec 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -172,7 +172,7 @@ OatWriter::~OatWriter() { } struct OatWriter::GcMapDataAccess { - static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + static ArrayRef<const uint8_t> GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { return compiled_method->GetGcMap(); } @@ -194,7 +194,7 @@ struct OatWriter::GcMapDataAccess { }; struct OatWriter::MappingTableDataAccess { - static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + static ArrayRef<const uint8_t> GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { return compiled_method->GetMappingTable(); } @@ -216,7 +216,7 @@ struct OatWriter::MappingTableDataAccess { }; struct OatWriter::VmapTableDataAccess { - static const SwapVector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { + static ArrayRef<const uint8_t> GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE { return compiled_method->GetVmapTable(); } @@ -388,8 +388,8 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { // Derived from CompiledMethod. uint32_t quick_code_offset = 0; - const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - uint32_t code_size = quick_code->size() * sizeof(uint8_t); + ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); + uint32_t code_size = quick_code.size() * sizeof(uint8_t); uint32_t thumb_offset = compiled_method->CodeDelta(); // Deduplicate code arrays if we are not producing debuggable code. @@ -428,7 +428,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { uint32_t vmap_table_offset = method_header->vmap_table_offset_; // If we don't have quick code, then we must have a vmap, as that is how the dex2dex // compiler records its transformations. - DCHECK(quick_code != nullptr || vmap_table_offset != 0); + DCHECK(!quick_code.empty() || vmap_table_offset != 0); uint32_t gc_map_offset = method_header->gc_map_offset_; // The code offset was 0 when the mapping/vmap table offset was set, so it's set // to 0-offset and we need to adjust it by code_offset. @@ -496,12 +496,12 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { } else { status = mirror::Class::kStatusNotReady; } - const SwapVector<uint8_t>* gc_map = compiled_method->GetGcMap(); - if (gc_map != nullptr) { - size_t gc_map_size = gc_map->size() * sizeof(gc_map[0]); + ArrayRef<const uint8_t> gc_map = compiled_method->GetGcMap(); + if (!gc_map.empty()) { + size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]); bool is_native = it.MemberIsNative(); CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified) - << gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " " + << gc_map_size << " " << (is_native ? "true" : "false") << " " << (status < mirror::Class::kStatusVerified) << " " << status << " " << PrettyMethod(it.GetMemberIndex(), *dex_file_); } @@ -519,30 +519,22 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { private: struct CodeOffsetsKeyComparator { bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const { - if (lhs->GetQuickCode() != rhs->GetQuickCode()) { - return lhs->GetQuickCode() < rhs->GetQuickCode(); + // Code is deduplicated by CompilerDriver, compare only data pointers. + if (lhs->GetQuickCode().data() != rhs->GetQuickCode().data()) { + return lhs->GetQuickCode().data() < rhs->GetQuickCode().data(); } // If the code is the same, all other fields are likely to be the same as well. - if (UNLIKELY(lhs->GetMappingTable() != rhs->GetMappingTable())) { - return lhs->GetMappingTable() < rhs->GetMappingTable(); + if (UNLIKELY(lhs->GetMappingTable().data() != rhs->GetMappingTable().data())) { + return lhs->GetMappingTable().data() < rhs->GetMappingTable().data(); } - if (UNLIKELY(lhs->GetVmapTable() != rhs->GetVmapTable())) { - return lhs->GetVmapTable() < rhs->GetVmapTable(); + if (UNLIKELY(lhs->GetVmapTable().data() != rhs->GetVmapTable().data())) { + return lhs->GetVmapTable().data() < rhs->GetVmapTable().data(); } - if (UNLIKELY(lhs->GetGcMap() != rhs->GetGcMap())) { - return lhs->GetGcMap() < rhs->GetGcMap(); + if (UNLIKELY(lhs->GetGcMap().data() != rhs->GetGcMap().data())) { + return lhs->GetGcMap().data() < rhs->GetGcMap().data(); } - const auto& lhs_patches = lhs->GetPatches(); - const auto& rhs_patches = rhs->GetPatches(); - if (UNLIKELY(lhs_patches.size() != rhs_patches.size())) { - return lhs_patches.size() < rhs_patches.size(); - } - auto rit = rhs_patches.begin(); - for (const LinkerPatch& lpatch : lhs_patches) { - if (UNLIKELY(!(lpatch == *rit))) { - return lpatch < *rit; - } - ++rit; + if (UNLIKELY(lhs->GetPatches().data() != rhs->GetPatches().data())) { + return lhs->GetPatches().data() < rhs->GetPatches().data(); } return false; } @@ -583,17 +575,17 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u); - const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method); - uint32_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]); + ArrayRef<const uint8_t> map = DataAccess::GetData(compiled_method); + uint32_t map_size = map.size() * sizeof(map[0]); if (map_size != 0u) { - auto lb = dedupe_map_.lower_bound(map); - if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map, lb->first)) { + auto lb = dedupe_map_.lower_bound(map.data()); + if (lb != dedupe_map_.end() && !dedupe_map_.key_comp()(map.data(), lb->first)) { DataAccess::SetOffset(oat_class, method_offsets_index_, lb->second); } else { DataAccess::SetOffset(oat_class, method_offsets_index_, offset_); - dedupe_map_.PutBefore(lb, map, offset_); + dedupe_map_.PutBefore(lb, map.data(), offset_); offset_ += map_size; - writer_->oat_header_->UpdateChecksum(&(*map)[0], map_size); + writer_->oat_header_->UpdateChecksum(&map[0], map_size); } } ++method_offsets_index_; @@ -605,7 +597,7 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { private: // Deduplication is already done on a pointer basis by the compiler driver, // so we can simply compare the pointers to find out if things are duplicated. - SafeMap<const SwapVector<uint8_t>*, uint32_t> dedupe_map_; + SafeMap<const uint8_t*, uint32_t> dedupe_map_; }; class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { @@ -647,7 +639,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { UNREACHABLE(); } - if (compiled_method != nullptr && compiled_method->GetQuickCode()->size() != 0) { + if (compiled_method != nullptr && compiled_method->GetQuickCode().size() != 0) { method->SetEntryPointFromQuickCompiledCodePtrSize( reinterpret_cast<void*>(offsets.code_offset_), pointer_size_); } @@ -713,10 +705,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { size_t file_offset = file_offset_; OutputStream* out = out_; - const SwapVector<uint8_t>* quick_code = compiled_method->GetQuickCode(); - // Need a wrapper if we create a copy for patching. - ArrayRef<const uint8_t> wrapped(*quick_code); - uint32_t code_size = quick_code->size() * sizeof(uint8_t); + ArrayRef<const uint8_t> quick_code = compiled_method->GetQuickCode(); + uint32_t code_size = quick_code.size() * sizeof(uint8_t); // Deduplicate code arrays. const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_]; @@ -753,8 +743,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { DCHECK_OFFSET_(); if (!compiled_method->GetPatches().empty()) { - patched_code_.assign(quick_code->begin(), quick_code->end()); - wrapped = ArrayRef<const uint8_t>(patched_code_); + patched_code_.assign(quick_code.begin(), quick_code.end()); + quick_code = ArrayRef<const uint8_t>(patched_code_); for (const LinkerPatch& patch : compiled_method->GetPatches()) { if (patch.Type() == kLinkerPatchCallRelative) { // NOTE: Relative calls across oat files are not supported. @@ -781,8 +771,8 @@ class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { } } - writer_->oat_header_->UpdateChecksum(wrapped.data(), code_size); - if (!out->WriteFully(wrapped.data(), code_size)) { + writer_->oat_header_->UpdateChecksum(quick_code.data(), code_size); + if (!out->WriteFully(quick_code.data(), code_size)) { ReportWriteFailure("method code", it); return false; } @@ -947,14 +937,14 @@ class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { ++method_offsets_index_; // Write deduplicated map. - const SwapVector<uint8_t>* map = DataAccess::GetData(compiled_method); - size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]); + ArrayRef<const uint8_t> map = DataAccess::GetData(compiled_method); + size_t map_size = map.size() * sizeof(map[0]); DCHECK((map_size == 0u && map_offset == 0u) || (map_size != 0u && map_offset != 0u && map_offset <= offset_)) << map_size << " " << map_offset << " " << offset_ << " " << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " for " << DataAccess::Name(); if (map_size != 0u && map_offset == offset_) { - if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) { + if (UNLIKELY(!out->WriteFully(&map[0], map_size))) { ReportWriteFailure(it); return false; } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index d6f2543890..575483c162 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -607,7 +607,7 @@ CompiledMethod* OptimizingCompiler::CompileOptimized(HGraph* graph, codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), - &src_mapping_table, + ArrayRef<const SrcMapElem>(src_mapping_table), ArrayRef<const uint8_t>(), // mapping_table. ArrayRef<const uint8_t>(stack_map), ArrayRef<const uint8_t>(), // native_gc_map. @@ -652,7 +652,7 @@ CompiledMethod* OptimizingCompiler::CompileBaseline( codegen->HasEmptyFrame() ? 0 : codegen->GetFrameSize(), codegen->GetCoreSpillMask(), codegen->GetFpuSpillMask(), - &src_mapping_table, + ArrayRef<const SrcMapElem>(src_mapping_table), AlignVectorSize(mapping_table), AlignVectorSize(vmap_table), AlignVectorSize(gc_map), diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h new file mode 100644 index 0000000000..ac5481336b --- /dev/null +++ b/compiler/utils/dedupe_set-inl.h @@ -0,0 +1,253 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_UTILS_DEDUPE_SET_INL_H_ +#define ART_COMPILER_UTILS_DEDUPE_SET_INL_H_ + +#include "dedupe_set.h" + +#include <algorithm> +#include <inttypes.h> +#include <unordered_map> + +#include "base/mutex.h" +#include "base/hash_set.h" +#include "base/stl_util.h" +#include "base/stringprintf.h" +#include "base/time_utils.h" + +namespace art { + +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, + HashType kShard> +struct DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Stats { + size_t collision_sum = 0u; + size_t collision_max = 0u; + size_t total_probe_distance = 0u; + size_t total_size = 0u; +}; + +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, + HashType kShard> +class DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Shard { + public: + Shard(const Alloc& alloc, const std::string& lock_name) + : alloc_(alloc), + lock_name_(lock_name), + lock_(lock_name_.c_str()), + keys_() { + } + + ~Shard() { + for (const HashedKey<StoreKey>& key : keys_) { + DCHECK(key.Key() != nullptr); + alloc_.Destroy(key.Key()); + } + } + + const StoreKey* Add(Thread* self, size_t hash, const InKey& in_key) REQUIRES(!lock_) { + MutexLock lock(self, lock_); + HashedKey<InKey> hashed_in_key(hash, &in_key); + auto it = keys_.Find(hashed_in_key); + if (it != keys_.end()) { + DCHECK(it->Key() != nullptr); + return it->Key(); + } + const StoreKey* store_key = alloc_.Copy(in_key); + keys_.Insert(HashedKey<StoreKey> { hash, store_key }); + return store_key; + } + + void UpdateStats(Thread* self, Stats* global_stats) REQUIRES(!lock_) { + // HashSet<> doesn't keep entries ordered by hash, so we actually allocate memory + // for bookkeeping while collecting the stats. + std::unordered_map<HashType, size_t> stats; + { + MutexLock lock(self, lock_); + // Note: The total_probe_distance will be updated with the current state. + // It may have been higher before a re-hash. + global_stats->total_probe_distance += keys_.TotalProbeDistance(); + global_stats->total_size += keys_.Size(); + for (const HashedKey<StoreKey>& key : keys_) { + auto it = stats.find(key.Hash()); + if (it == stats.end()) { + stats.insert({key.Hash(), 1u}); + } else { + ++it->second; + } + } + } + for (const auto& entry : stats) { + size_t number_of_entries = entry.second; + if (number_of_entries > 1u) { + global_stats->collision_sum += number_of_entries - 1u; + global_stats->collision_max = std::max(global_stats->collision_max, number_of_entries); + } + } + } + + private: + template <typename T> + class HashedKey { + public: + HashedKey() : hash_(0u), key_(nullptr) { } + HashedKey(size_t hash, const T* key) : hash_(hash), key_(key) { } + + size_t Hash() const { + return hash_; + } + + const T* Key() const { + return key_; + } + + bool IsEmpty() const { + return Key() == nullptr; + } + + void MakeEmpty() { + key_ = nullptr; + } + + private: + size_t hash_; + const T* key_; + }; + + class ShardEmptyFn { + public: + bool IsEmpty(const HashedKey<StoreKey>& key) const { + return key.IsEmpty(); + } + + void MakeEmpty(HashedKey<StoreKey>& key) { + key.MakeEmpty(); + } + }; + + struct ShardHashFn { + template <typename T> + size_t operator()(const HashedKey<T>& key) const { + return key.Hash(); + } + }; + + struct ShardPred { + typename std::enable_if<!std::is_same<StoreKey, InKey>::value, bool>::type + operator()(const HashedKey<StoreKey>& lhs, const HashedKey<StoreKey>& rhs) const { + DCHECK(lhs.Key() != nullptr); + DCHECK(rhs.Key() != nullptr); + // Rehashing: stored keys are already deduplicated, so we can simply compare key pointers. + return lhs.Key() == rhs.Key(); + } + + template <typename LeftT, typename RightT> + bool operator()(const HashedKey<LeftT>& lhs, const HashedKey<RightT>& rhs) const { + DCHECK(lhs.Key() != nullptr); + DCHECK(rhs.Key() != nullptr); + return lhs.Hash() == rhs.Hash() && + lhs.Key()->size() == rhs.Key()->size() && + std::equal(lhs.Key()->begin(), lhs.Key()->end(), rhs.Key()->begin()); + } + }; + + Alloc alloc_; + const std::string lock_name_; + Mutex lock_; + HashSet<HashedKey<StoreKey>, ShardEmptyFn, ShardHashFn, ShardPred> keys_ GUARDED_BY(lock_); +}; + +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, + HashType kShard> +const StoreKey* DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::Add( + Thread* self, const InKey& key) { + uint64_t hash_start; + if (kIsDebugBuild) { + hash_start = NanoTime(); + } + HashType raw_hash = HashFunc()(key); + if (kIsDebugBuild) { + uint64_t hash_end = NanoTime(); + hash_time_ += hash_end - hash_start; + } + HashType shard_hash = raw_hash / kShard; + HashType shard_bin = raw_hash % kShard; + return shards_[shard_bin]->Add(self, shard_hash, key); +} + +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, + HashType kShard> +DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DedupeSet(const char* set_name, + const Alloc& alloc) + : hash_time_(0) { + for (HashType i = 0; i < kShard; ++i) { + std::ostringstream oss; + oss << set_name << " lock " << i; + shards_[i].reset(new Shard(alloc, oss.str())); + } +} + +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, + HashType kShard> +DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::~DedupeSet() { + // Everything done by member destructors. +} + +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, + HashType kShard> +std::string DedupeSet<InKey, StoreKey, Alloc, HashType, HashFunc, kShard>::DumpStats( + Thread* self) const { + Stats stats; + for (HashType shard = 0; shard < kShard; ++shard) { + shards_[shard]->UpdateStats(self, &stats); + } + return StringPrintf("%zu collisions, %zu max hash collisions, " + "%zu/%zu probe distance, %" PRIu64 " ns hash time", + stats.collision_sum, + stats.collision_max, + stats.total_probe_distance, + stats.total_size, + hash_time_); +} + + +} // namespace art + +#endif // ART_COMPILER_UTILS_DEDUPE_SET_INL_H_ diff --git a/compiler/utils/dedupe_set.h b/compiler/utils/dedupe_set.h index 2c4a689096..b62f216842 100644 --- a/compiler/utils/dedupe_set.h +++ b/compiler/utils/dedupe_set.h @@ -17,151 +17,41 @@ #ifndef ART_COMPILER_UTILS_DEDUPE_SET_H_ #define ART_COMPILER_UTILS_DEDUPE_SET_H_ -#include <algorithm> -#include <inttypes.h> #include <memory> -#include <set> +#include <stdint.h> #include <string> -#include "base/mutex.h" -#include "base/stl_util.h" -#include "base/stringprintf.h" -#include "base/time_utils.h" -#include "utils/swap_space.h" +#include "base/macros.h" namespace art { +class Thread; + // A set of Keys that support a HashFunc returning HashType. Used to find duplicates of Key in the // Add method. The data-structure is thread-safe through the use of internal locks, it also // supports the lock being sharded. -template <typename InKey, typename StoreKey, typename HashType, typename HashFunc, +template <typename InKey, + typename StoreKey, + typename Alloc, + typename HashType, + typename HashFunc, HashType kShard = 1> class DedupeSet { - typedef std::pair<HashType, const InKey*> HashedInKey; - struct HashedKey { - StoreKey* store_ptr; - union { - HashType store_hash; // Valid if store_ptr != null. - const HashedInKey* in_key; // Valid if store_ptr == null. - }; - }; - - class Comparator { - public: - bool operator()(const HashedKey& a, const HashedKey& b) const { - HashType a_hash = (a.store_ptr != nullptr) ? a.store_hash : a.in_key->first; - HashType b_hash = (b.store_ptr != nullptr) ? b.store_hash : b.in_key->first; - if (a_hash != b_hash) { - return a_hash < b_hash; - } - if (a.store_ptr != nullptr && b.store_ptr != nullptr) { - return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(), - b.store_ptr->begin(), b.store_ptr->end()); - } else if (a.store_ptr != nullptr && b.store_ptr == nullptr) { - return std::lexicographical_compare(a.store_ptr->begin(), a.store_ptr->end(), - b.in_key->second->begin(), b.in_key->second->end()); - } else if (a.store_ptr == nullptr && b.store_ptr != nullptr) { - return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(), - b.store_ptr->begin(), b.store_ptr->end()); - } else { - return std::lexicographical_compare(a.in_key->second->begin(), a.in_key->second->end(), - b.in_key->second->begin(), b.in_key->second->end()); - } - } - }; - public: - StoreKey* Add(Thread* self, const InKey& key) { - uint64_t hash_start; - if (kIsDebugBuild) { - hash_start = NanoTime(); - } - HashType raw_hash = HashFunc()(key); - if (kIsDebugBuild) { - uint64_t hash_end = NanoTime(); - hash_time_ += hash_end - hash_start; - } - HashType shard_hash = raw_hash / kShard; - HashType shard_bin = raw_hash % kShard; - HashedInKey hashed_in_key(shard_hash, &key); - HashedKey hashed_key; - hashed_key.store_ptr = nullptr; - hashed_key.in_key = &hashed_in_key; - MutexLock lock(self, *lock_[shard_bin]); - auto it = keys_[shard_bin].find(hashed_key); - if (it != keys_[shard_bin].end()) { - DCHECK(it->store_ptr != nullptr); - return it->store_ptr; - } - hashed_key.store_ptr = CreateStoreKey(key); - hashed_key.store_hash = shard_hash; - keys_[shard_bin].insert(hashed_key); - return hashed_key.store_ptr; - } + // Add a new key to the dedupe set if not present. Return the equivalent deduplicated stored key. + const StoreKey* Add(Thread* self, const InKey& key); - DedupeSet(const char* set_name, SwapAllocator<void>& alloc) - : allocator_(alloc), hash_time_(0) { - for (HashType i = 0; i < kShard; ++i) { - std::ostringstream oss; - oss << set_name << " lock " << i; - lock_name_[i] = oss.str(); - lock_[i].reset(new Mutex(lock_name_[i].c_str())); - } - } + DedupeSet(const char* set_name, const Alloc& alloc); - ~DedupeSet() { - // Have to manually free all pointers. - for (auto& shard : keys_) { - for (const auto& hashed_key : shard) { - DCHECK(hashed_key.store_ptr != nullptr); - DeleteStoreKey(hashed_key.store_ptr); - } - } - } + ~DedupeSet(); - std::string DumpStats() const { - size_t collision_sum = 0; - size_t collision_max = 0; - for (HashType shard = 0; shard < kShard; ++shard) { - HashType last_hash = 0; - size_t collision_cur_max = 0; - for (const HashedKey& key : keys_[shard]) { - DCHECK(key.store_ptr != nullptr); - if (key.store_hash == last_hash) { - collision_cur_max++; - if (collision_cur_max > 1) { - collision_sum++; - if (collision_cur_max > collision_max) { - collision_max = collision_cur_max; - } - } - } else { - collision_cur_max = 1; - last_hash = key.store_hash; - } - } - } - return StringPrintf("%zu collisions, %zu max bucket size, %" PRIu64 " ns hash time", - collision_sum, collision_max, hash_time_); - } + std::string DumpStats(Thread* self) const; private: - StoreKey* CreateStoreKey(const InKey& key) { - StoreKey* ret = allocator_.allocate(1); - allocator_.construct(ret, key.begin(), key.end(), allocator_); - return ret; - } - - void DeleteStoreKey(StoreKey* key) { - SwapAllocator<StoreKey> alloc(allocator_); - alloc.destroy(key); - alloc.deallocate(key, 1); - } + struct Stats; + class Shard; - std::string lock_name_[kShard]; - std::unique_ptr<Mutex> lock_[kShard]; - std::set<HashedKey, Comparator> keys_[kShard]; - SwapAllocator<StoreKey> allocator_; + std::unique_ptr<Shard> shards_[kShard]; uint64_t hash_time_; DISALLOW_COPY_AND_ASSIGN(DedupeSet); diff --git a/compiler/utils/dedupe_set_test.cc b/compiler/utils/dedupe_set_test.cc index 637964e484..60a891d6a2 100644 --- a/compiler/utils/dedupe_set_test.cc +++ b/compiler/utils/dedupe_set_test.cc @@ -18,15 +18,18 @@ #include <algorithm> #include <cstdio> +#include <vector> +#include "dedupe_set-inl.h" #include "gtest/gtest.h" #include "thread-inl.h" +#include "utils/array_ref.h" namespace art { -class DedupeHashFunc { +class DedupeSetTestHashFunc { public: - size_t operator()(const std::vector<uint8_t>& array) const { + size_t operator()(const ArrayRef<const uint8_t>& array) const { size_t hash = 0; for (uint8_t c : array) { hash += c; @@ -36,46 +39,52 @@ class DedupeHashFunc { return hash; } }; + +class DedupeSetTestAlloc { + public: + const std::vector<uint8_t>* Copy(const ArrayRef<const uint8_t>& src) { + return new std::vector<uint8_t>(src.begin(), src.end()); + } + + void Destroy(const std::vector<uint8_t>* key) { + delete key; + } +}; + TEST(DedupeSetTest, Test) { Thread* self = Thread::Current(); - typedef std::vector<uint8_t> ByteArray; - SwapAllocator<void> swap(nullptr); - DedupeSet<ByteArray, SwapVector<uint8_t>, size_t, DedupeHashFunc> deduplicator("test", swap); - SwapVector<uint8_t>* array1; + DedupeSetTestAlloc alloc; + DedupeSet<ArrayRef<const uint8_t>, + std::vector<uint8_t>, + DedupeSetTestAlloc, + size_t, + DedupeSetTestHashFunc> deduplicator("test", alloc); + const std::vector<uint8_t>* array1; { - ByteArray test1; - test1.push_back(10); - test1.push_back(20); - test1.push_back(30); - test1.push_back(45); - + uint8_t raw_test1[] = { 10u, 20u, 30u, 45u }; + ArrayRef<const uint8_t> test1(raw_test1); array1 = deduplicator.Add(self, test1); ASSERT_NE(array1, nullptr); ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array1->begin())); } - SwapVector<uint8_t>* array2; + const std::vector<uint8_t>* array2; { - ByteArray test1; - test1.push_back(10); - test1.push_back(20); - test1.push_back(30); - test1.push_back(45); - array2 = deduplicator.Add(self, test1); + uint8_t raw_test2[] = { 10u, 20u, 30u, 45u }; + ArrayRef<const uint8_t> test2(raw_test2); + array2 = deduplicator.Add(self, test2); ASSERT_EQ(array2, array1); - ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array2->begin())); + ASSERT_TRUE(std::equal(test2.begin(), test2.end(), array2->begin())); } - SwapVector<uint8_t>* array3; + const std::vector<uint8_t>* array3; { - ByteArray test1; - test1.push_back(10); - test1.push_back(22); - test1.push_back(30); - test1.push_back(47); - array3 = deduplicator.Add(self, test1); + uint8_t raw_test3[] = { 10u, 22u, 30u, 47u }; + ArrayRef<const uint8_t> test3(raw_test3); + array3 = deduplicator.Add(self, test3); ASSERT_NE(array3, nullptr); - ASSERT_TRUE(std::equal(test1.begin(), test1.end(), array3->begin())); + ASSERT_NE(array3, array1); + ASSERT_TRUE(std::equal(test3.begin(), test3.end(), array3->begin())); } } diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc index 81622e14ed..e253e13f86 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -5325,7 +5325,7 @@ bool ClassLinker::LinkInterfaceMethods( ScopedArenaUnorderedMap<ArtMethod*, ArtMethod*> move_table(allocator.Adapter()); if (virtuals != old_virtuals) { // Maps from heap allocated miranda method to linear alloc miranda method. - StrideIterator<ArtMethod> out = virtuals->Begin(method_size, method_alignment); + StrideIterator<ArtMethod> out = virtuals->begin(method_size, method_alignment); // Copy over the old methods + miranda methods. for (auto& m : klass->GetVirtualMethods(image_pointer_size_)) { move_table.emplace(&m, &*out); @@ -5335,7 +5335,7 @@ bool ClassLinker::LinkInterfaceMethods( ++out; } } - StrideIterator<ArtMethod> out(virtuals->Begin(method_size, method_alignment) + StrideIterator<ArtMethod> out(virtuals->begin(method_size, method_alignment) + old_method_count); // Copy over miranda methods before copying vtable since CopyOf may cause thread suspension and // we want the roots of the miranda methods to get visited. @@ -5367,7 +5367,7 @@ bool ClassLinker::LinkInterfaceMethods( move_table.emplace(def_method, &new_method); ++out; } - virtuals->SetLength(new_method_count); + virtuals->SetSize(new_method_count); UpdateClassVirtualMethods(klass.Get(), virtuals); // Done copying methods, they are all roots in the class now, so we can end the no thread // suspension assert. @@ -5382,7 +5382,7 @@ bool ClassLinker::LinkInterfaceMethods( self->AssertPendingOOMException(); return false; } - out = virtuals->Begin(method_size, method_alignment) + old_method_count; + out = virtuals->begin(method_size, method_alignment) + old_method_count; size_t vtable_pos = old_vtable_count; for (size_t i = old_method_count; i < new_method_count; ++i) { // Leave the declaring class alone as type indices are relative to it diff --git a/runtime/image.cc b/runtime/image.cc index 192371fe75..1bc19ff656 100644 --- a/runtime/image.cc +++ b/runtime/image.cc @@ -150,10 +150,10 @@ std::ostream& operator<<(std::ostream& os, const ImageSection& section) { void ImageSection::VisitPackedArtFields(ArtFieldVisitor* visitor, uint8_t* base) const { for (size_t pos = 0; pos < Size(); ) { auto* array = reinterpret_cast<LengthPrefixedArray<ArtField>*>(base + Offset() + pos); - for (size_t i = 0; i < array->Length(); ++i) { + for (size_t i = 0; i < array->size(); ++i) { visitor->Visit(&array->At(i, sizeof(ArtField))); } - pos += array->ComputeSize(array->Length()); + pos += array->ComputeSize(array->size()); } } @@ -164,10 +164,10 @@ void ImageSection::VisitPackedArtMethods(ArtMethodVisitor* visitor, const size_t method_size = ArtMethod::Size(pointer_size); for (size_t pos = 0; pos < Size(); ) { auto* array = reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(base + Offset() + pos); - for (size_t i = 0; i < array->Length(); ++i) { + for (size_t i = 0; i < array->size(); ++i) { visitor->Visit(&array->At(i, method_size, method_alignment)); } - pos += array->ComputeSize(array->Length(), method_size, method_alignment); + pos += array->ComputeSize(array->size(), method_size, method_alignment); } } diff --git a/runtime/length_prefixed_array.h b/runtime/length_prefixed_array.h index 0ff6d7a9aa..e01b6ccd26 100644 --- a/runtime/length_prefixed_array.h +++ b/runtime/length_prefixed_array.h @@ -30,19 +30,34 @@ template<typename T> class LengthPrefixedArray { public: explicit LengthPrefixedArray(size_t length) - : length_(dchecked_integral_cast<uint32_t>(length)) {} + : size_(dchecked_integral_cast<uint32_t>(length)) {} T& At(size_t index, size_t element_size = sizeof(T), size_t alignment = alignof(T)) { - DCHECK_LT(index, length_); + DCHECK_LT(index, size_); return AtUnchecked(index, element_size, alignment); } - StrideIterator<T> Begin(size_t element_size = sizeof(T), size_t alignment = alignof(T)) { + const T& At(size_t index, size_t element_size = sizeof(T), size_t alignment = alignof(T)) const { + DCHECK_LT(index, size_); + return AtUnchecked(index, element_size, alignment); + } + + StrideIterator<T> begin(size_t element_size = sizeof(T), size_t alignment = alignof(T)) { return StrideIterator<T>(&AtUnchecked(0, element_size, alignment), element_size); } - StrideIterator<T> End(size_t element_size = sizeof(T), size_t alignment = alignof(T)) { - return StrideIterator<T>(&AtUnchecked(length_, element_size, alignment), element_size); + StrideIterator<const T> begin(size_t element_size = sizeof(T), + size_t alignment = alignof(T)) const { + return StrideIterator<const T>(&AtUnchecked(0, element_size, alignment), element_size); + } + + StrideIterator<T> end(size_t element_size = sizeof(T), size_t alignment = alignof(T)) { + return StrideIterator<T>(&AtUnchecked(size_, element_size, alignment), element_size); + } + + StrideIterator<const T> end(size_t element_size = sizeof(T), + size_t alignment = alignof(T)) const { + return StrideIterator<const T>(&AtUnchecked(size_, element_size, alignment), element_size); } static size_t OffsetOfElement(size_t index, @@ -60,13 +75,13 @@ class LengthPrefixedArray { return result; } - uint64_t Length() const { - return length_; + size_t size() const { + return size_; } // Update the length but does not reallocate storage. - void SetLength(size_t length) { - length_ = dchecked_integral_cast<uint32_t>(length); + void SetSize(size_t length) { + size_ = dchecked_integral_cast<uint32_t>(length); } private: @@ -75,7 +90,12 @@ class LengthPrefixedArray { reinterpret_cast<uintptr_t>(this) + OffsetOfElement(index, element_size, alignment)); } - uint32_t length_; + const T& AtUnchecked(size_t index, size_t element_size, size_t alignment) const { + return *reinterpret_cast<T*>( + reinterpret_cast<uintptr_t>(this) + OffsetOfElement(index, element_size, alignment)); + } + + uint32_t size_; uint8_t data[0]; }; @@ -84,7 +104,7 @@ template<typename T> IterationRange<StrideIterator<T>> MakeIterationRangeFromLengthPrefixedArray( LengthPrefixedArray<T>* arr, size_t element_size = sizeof(T), size_t alignment = alignof(T)) { return arr != nullptr ? - MakeIterationRange(arr->Begin(element_size, alignment), arr->End(element_size, alignment)) : + MakeIterationRange(arr->begin(element_size, alignment), arr->end(element_size, alignment)) : MakeEmptyIterationRange(StrideIterator<T>(nullptr, 0)); } diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h index a528c3b890..19ee7f4dd4 100644 --- a/runtime/mirror/class-inl.h +++ b/runtime/mirror/class-inl.h @@ -928,22 +928,22 @@ inline bool Class::IsAssignableFrom(Class* src) { inline uint32_t Class::NumDirectMethods() { LengthPrefixedArray<ArtMethod>* arr = GetDirectMethodsPtrUnchecked(); - return arr != nullptr ? arr->Length() : 0u; + return arr != nullptr ? arr->size() : 0u; } inline uint32_t Class::NumVirtualMethods() { LengthPrefixedArray<ArtMethod>* arr = GetVirtualMethodsPtrUnchecked(); - return arr != nullptr ? arr->Length() : 0u; + return arr != nullptr ? arr->size() : 0u; } inline uint32_t Class::NumInstanceFields() { LengthPrefixedArray<ArtField>* arr = GetIFieldsPtrUnchecked(); - return arr != nullptr ? arr->Length() : 0u; + return arr != nullptr ? arr->size() : 0u; } inline uint32_t Class::NumStaticFields() { LengthPrefixedArray<ArtField>* arr = GetSFieldsPtrUnchecked(); - return arr != nullptr ? arr->Length() : 0u; + return arr != nullptr ? arr->size() : 0u; } } // namespace mirror diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc index 53fedab377..9d01a1db60 100644 --- a/runtime/mirror/class.cc +++ b/runtime/mirror/class.cc @@ -574,7 +574,7 @@ static ArtField* FindFieldByNameAndType(LengthPrefixedArray<ArtField>* fields, return nullptr; } size_t low = 0; - size_t high = fields->Length(); + size_t high = fields->size(); ArtField* ret = nullptr; while (low < high) { size_t mid = (low + high) / 2; diff --git a/runtime/native/java_lang_Class.cc b/runtime/native/java_lang_Class.cc index 3a73900efa..5e423920c0 100644 --- a/runtime/native/java_lang_Class.cc +++ b/runtime/native/java_lang_Class.cc @@ -190,7 +190,7 @@ ALWAYS_INLINE static inline ArtField* FindFieldByName( return nullptr; } size_t low = 0; - size_t high = fields->Length(); + size_t high = fields->size(); const uint16_t* const data = name->GetValue(); const size_t length = name->GetLength(); while (low < high) { diff --git a/runtime/proxy_test.cc b/runtime/proxy_test.cc index bc9ba377bc..57472adb64 100644 --- a/runtime/proxy_test.cc +++ b/runtime/proxy_test.cc @@ -216,10 +216,10 @@ TEST_F(ProxyTest, CheckArtMirrorFieldsOfProxyStaticFields) { LengthPrefixedArray<ArtField>* static_fields0 = proxyClass0->GetSFieldsPtr(); ASSERT_TRUE(static_fields0 != nullptr); - ASSERT_EQ(2u, static_fields0->Length()); + ASSERT_EQ(2u, static_fields0->size()); LengthPrefixedArray<ArtField>* static_fields1 = proxyClass1->GetSFieldsPtr(); ASSERT_TRUE(static_fields1 != nullptr); - ASSERT_EQ(2u, static_fields1->Length()); + ASSERT_EQ(2u, static_fields1->size()); EXPECT_EQ(static_fields0->At(0).GetDeclaringClass(), proxyClass0.Get()); EXPECT_EQ(static_fields0->At(1).GetDeclaringClass(), proxyClass0.Get()); diff --git a/runtime/stride_iterator.h b/runtime/stride_iterator.h index a9da51ba29..ac04c3b403 100644 --- a/runtime/stride_iterator.h +++ b/runtime/stride_iterator.h @@ -19,6 +19,8 @@ #include <iterator> +#include "base/logging.h" + namespace art { template<typename T> |