diff options
26 files changed, 396 insertions, 301 deletions
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc index 52cb217980..308e75d9c1 100644 --- a/compiler/dex/dex_to_dex_compiler.cc +++ b/compiler/dex/dex_to_dex_compiler.cc @@ -373,15 +373,15 @@ CompiledMethod* ArtCompileDEX( CHECK_EQ(quicken_count, dex_compiler.GetQuickenedInfo().size()); } std::vector<uint8_t> quicken_data; + QuickenInfoTable::Builder builder(&quicken_data, dex_compiler.GetQuickenedInfo().size()); + // Length is encoded by the constructor. for (QuickenedInfo info : dex_compiler.GetQuickenedInfo()) { // Dex pc is not serialized, only used for checking the instructions. Since we access the // array based on the index of the quickened instruction, the indexes must line up perfectly. // The reader side uses the NeedsIndexForInstruction function too. const Instruction& inst = unit.GetCodeItemAccessor().InstructionAt(info.dex_pc); CHECK(QuickenInfoTable::NeedsIndexForInstruction(&inst)) << inst.Opcode(); - // Add the index. - quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 0)); - quicken_data.push_back(static_cast<uint8_t>(info.dex_member_index >> 8)); + builder.AddIndex(info.dex_member_index); } InstructionSet instruction_set = driver->GetInstructionSet(); if (instruction_set == InstructionSet::kThumb2) { diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index c0886d0185..869865956c 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -424,10 +424,6 @@ static optimizer::DexToDexCompilationLevel GetDexToDexCompilationLevel( // optimizations that could break that. max_level = optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; } - if (!VdexFile::CanEncodeQuickenedData(dex_file)) { - // Don't do any dex level optimizations if we cannot encode the quickening. - return optimizer::DexToDexCompilationLevel::kDontDexToDexCompile; - } if (klass->IsVerified()) { // Class is verified so we can enable DEX-to-DEX compilation for performance. return max_level; diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index af537dd653..a1a5692ef6 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -43,7 +43,7 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, CompilerDriver* driver, CodeGenerator* code_generator, OptimizingCompilerStats* compiler_stats, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, VariableSizedHandleScope* handles) : graph_(graph), dex_file_(&graph->GetDexFile()), @@ -70,7 +70,6 @@ HGraphBuilder::HGraphBuilder(HGraph* graph, compiler_driver_(nullptr), code_generator_(nullptr), compilation_stats_(nullptr), - interpreter_metadata_(nullptr), handles_(handles), return_type_(return_type) {} diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index c16a3a928d..5a1914ce08 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -18,6 +18,7 @@ #define ART_COMPILER_OPTIMIZING_BUILDER_H_ #include "base/arena_object.h" +#include "base/array_ref.h" #include "dex/code_item_accessors.h" #include "dex/dex_file-inl.h" #include "dex/dex_file.h" @@ -40,7 +41,7 @@ class HGraphBuilder : public ValueObject { CompilerDriver* driver, CodeGenerator* code_generator, OptimizingCompilerStats* compiler_stats, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, VariableSizedHandleScope* handles); // Only for unit testing. @@ -73,7 +74,7 @@ class HGraphBuilder : public ValueObject { CodeGenerator* const code_generator_; OptimizingCompilerStats* const compilation_stats_; - const uint8_t* const interpreter_metadata_; + const ArrayRef<const uint8_t> interpreter_metadata_; VariableSizedHandleScope* const handles_; const DataType::Type return_type_; diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 72a93c1f77..64a1eccf60 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -49,7 +49,7 @@ HInstructionBuilder::HInstructionBuilder(HGraph* graph, const DexCompilationUnit* outer_compilation_unit, CompilerDriver* compiler_driver, CodeGenerator* code_generator, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, ScopedArenaAllocator* local_allocator) diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index 708a09711a..4428c53277 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_ #define ART_COMPILER_OPTIMIZING_INSTRUCTION_BUILDER_H_ +#include "base/array_ref.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "data_type.h" @@ -57,7 +58,7 @@ class HInstructionBuilder : public ValueObject { const DexCompilationUnit* outer_compilation_unit, CompilerDriver* compiler_driver, CodeGenerator* code_generator, - const uint8_t* interpreter_metadata, + ArrayRef<const uint8_t> interpreter_metadata, OptimizingCompilerStats* compiler_stats, VariableSizedHandleScope* handles, ScopedArenaAllocator* local_allocator); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index f4115f7e7b..8966d560db 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -783,7 +783,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* allocator, compiler_driver->GetCompilerOptions().GetDebuggable(), osr); - const uint8_t* interpreter_metadata = nullptr; + ArrayRef<const uint8_t> interpreter_metadata; // For AOT compilation, we may not get a method, for example if its class is erroneous. // JIT should always have a method. DCHECK(Runtime::Current()->IsAotCompiler() || method != nullptr); @@ -940,7 +940,7 @@ CodeGenerator* OptimizingCompiler::TryCompileIntrinsic( compiler_driver, codegen.get(), compilation_stats_.get(), - /* interpreter_metadata */ nullptr, + /* interpreter_metadata */ ArrayRef<const uint8_t>(), handles); builder.BuildIntrinsicGraph(method); } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 8d0d89ed5c..06cc71b4db 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -1807,9 +1807,7 @@ class Dex2Oat FINAL { // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening // optimization does not depend on the boot image (the optimization relies on not // having final fields in a class, which does not change for an app). - VdexFile::Unquicken(dex_files_, - input_vdex_file_->GetQuickeningInfo(), - /* decompile_return_instruction */ false); + input_vdex_file_->Unquicken(dex_files_, /* decompile_return_instruction */ false); } else { // Create the main VerifierDeps, here instead of in the compiler since we want to aggregate // the results for all the dex files, not just the results for the current dex file. diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc index 16d70daddf..cec7960682 100644 --- a/dex2oat/linker/oat_writer.cc +++ b/dex2oat/linker/oat_writer.cc @@ -57,6 +57,7 @@ #include "mirror/object-inl.h" #include "oat_quick_method_header.h" #include "os.h" +#include "quicken_info.h" #include "safe_map.h" #include "scoped_thread_state_change-inl.h" #include "type_lookup_table.h" @@ -2617,42 +2618,54 @@ bool OatWriter::WriteRodata(OutputStream* out) { return true; } -class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { +class OatWriter::WriteQuickeningInfoMethodVisitor { public: - WriteQuickeningInfoMethodVisitor(OatWriter* writer, - OutputStream* out, - uint32_t offset, - SafeMap<const uint8_t*, uint32_t>* offset_map) - : DexMethodVisitor(writer, offset), - out_(out), - written_bytes_(0u), - offset_map_(offset_map) {} + WriteQuickeningInfoMethodVisitor(OatWriter* writer, OutputStream* out) + : writer_(writer), + out_(out) {} - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) - OVERRIDE { - uint32_t method_idx = it.GetMemberIndex(); - CompiledMethod* compiled_method = - writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx)); + bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { + std::vector<uint8_t> empty_quicken_info; + { + // Since we need to be able to access by dex method index, put a one byte empty quicken info + // for any method that isn't quickened. + QuickenInfoTable::Builder empty_info(&empty_quicken_info, /*num_elements*/ 0u); + CHECK(!empty_quicken_info.empty()); + } + for (const DexFile* dex_file : dex_files) { + std::vector<uint32_t>* const offsets = + &quicken_info_offset_indices_.Put(dex_file, std::vector<uint32_t>())->second; + + // Every method needs an index in the table. + for (uint32_t method_idx = 0; method_idx < dex_file->NumMethodIds(); ++method_idx) { + ArrayRef<const uint8_t> map(empty_quicken_info); + + // Use the existing quicken info if it exists. + MethodReference method_ref(dex_file, method_idx); + CompiledMethod* compiled_method = writer_->compiler_driver_->GetCompiledMethod(method_ref); + if (compiled_method != nullptr && HasQuickeningInfo(compiled_method)) { + map = compiled_method->GetVmapTable(); + } - if (HasQuickeningInfo(compiled_method)) { - ArrayRef<const uint8_t> map = compiled_method->GetVmapTable(); - // 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. - if (offset_map_->find(map.data()) == offset_map_->end()) { - uint32_t length = map.size() * sizeof(map.front()); - offset_map_->Put(map.data(), written_bytes_); - if (!out_->WriteFully(&length, sizeof(length)) || - !out_->WriteFully(map.data(), length)) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file_->PrettyMethod(it.GetMemberIndex()) << " to " - << out_->GetLocation(); + // The current approach prevents deduplication of quicken infos since each method index + // has one unique quicken info. Deduplication does not provide much savings for dex indices + // since they are rarely duplicated. + const uint32_t length = map.size() * sizeof(map.front()); + + // Record each index if required. written_bytes_ is the offset from the start of the + // quicken info data. + if (QuickenInfoOffsetTableAccessor::IsCoveredIndex(method_idx)) { + offsets->push_back(written_bytes_); + } + + if (!out_->WriteFully(map.data(), length)) { + PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod() + << " to " << out_->GetLocation(); return false; } - written_bytes_ += sizeof(length) + length; - offset_ += sizeof(length) + length; + written_bytes_ += length; } } - return true; } @@ -2660,71 +2673,59 @@ class OatWriter::WriteQuickeningInfoMethodVisitor : public DexMethodVisitor { return written_bytes_; } + SafeMap<const DexFile*, std::vector<uint32_t>>& GetQuickenInfoOffsetIndicies() { + return quicken_info_offset_indices_; + } + + private: + OatWriter* const writer_; OutputStream* const out_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - SafeMap<const uint8_t*, uint32_t>* offset_map_; + size_t written_bytes_ = 0u; + // Map of offsets for quicken info related to method indices. + SafeMap<const DexFile*, std::vector<uint32_t>> quicken_info_offset_indices_; }; -class OatWriter::WriteQuickeningIndicesMethodVisitor { +class OatWriter::WriteQuickeningInfoOffsetsMethodVisitor { public: - WriteQuickeningIndicesMethodVisitor(OutputStream* out, - uint32_t quickening_info_bytes, - const SafeMap<const uint8_t*, uint32_t>& offset_map) + WriteQuickeningInfoOffsetsMethodVisitor( + OutputStream* out, + uint32_t start_offset, + SafeMap<const DexFile*, std::vector<uint32_t>>* quicken_info_offset_indices, + std::vector<uint32_t>* out_table_offsets) : out_(out), - quickening_info_bytes_(quickening_info_bytes), - written_bytes_(0u), - offset_map_(offset_map) {} + start_offset_(start_offset), + quicken_info_offset_indices_(quicken_info_offset_indices), + out_table_offsets_(out_table_offsets) {} - bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) { + bool VisitDexMethods(const std::vector<const DexFile*>& dex_files) { for (const DexFile* dex_file : dex_files) { - const size_t class_def_count = dex_file->NumClassDefs(); - for (size_t class_def_index = 0; class_def_index != class_def_count; ++class_def_index) { - const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); - const uint8_t* class_data = dex_file->GetClassData(class_def); - if (class_data == nullptr) { - continue; - } - for (ClassDataItemIterator class_it(*dex_file, class_data); - class_it.HasNext(); - class_it.Next()) { - if (!class_it.IsAtMethod() || class_it.GetMethodCodeItem() == nullptr) { - continue; - } - uint32_t method_idx = class_it.GetMemberIndex(); - CompiledMethod* compiled_method = - driver.GetCompiledMethod(MethodReference(dex_file, method_idx)); - const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - CodeItemDebugInfoAccessor accessor(*dex_file, code_item); - const uint32_t existing_debug_info_offset = accessor.DebugInfoOffset(); - // If the existing offset is already out of bounds (and not magic marker 0xFFFFFFFF) - // we will pretend the method has been quickened. - bool existing_offset_out_of_bounds = - (existing_debug_info_offset >= dex_file->Size() && - existing_debug_info_offset != 0xFFFFFFFF); - bool has_quickening_info = HasQuickeningInfo(compiled_method); - if (has_quickening_info || existing_offset_out_of_bounds) { - uint32_t new_debug_info_offset = - dex_file->Size() + quickening_info_bytes_ + written_bytes_; - // Abort if overflow. - CHECK_GE(new_debug_info_offset, dex_file->Size()); - const_cast<DexFile::CodeItem*>(code_item)->SetDebugInfoOffset(new_debug_info_offset); - uint32_t quickening_offset = has_quickening_info - ? offset_map_.Get(compiled_method->GetVmapTable().data()) - : VdexFile::kNoQuickeningInfoOffset; - if (!out_->WriteFully(&existing_debug_info_offset, - sizeof(existing_debug_info_offset)) || - !out_->WriteFully(&quickening_offset, sizeof(quickening_offset))) { - PLOG(ERROR) << "Failed to write quickening info for " - << dex_file->PrettyMethod(method_idx) << " to " - << out_->GetLocation(); - return false; - } - written_bytes_ += sizeof(existing_debug_info_offset) + sizeof(quickening_offset); - } - } + auto it = quicken_info_offset_indices_->find(dex_file); + DCHECK(it != quicken_info_offset_indices_->end()) << "Failed to find dex file " + << dex_file->GetLocation(); + const std::vector<uint32_t>* const offsets = &it->second; + + const uint32_t current_offset = start_offset_ + written_bytes_; + CHECK_ALIGNED_PARAM(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + + // Generate and write the data. + std::vector<uint8_t> table_data; + QuickenInfoOffsetTableAccessor::Builder builder(&table_data); + for (uint32_t offset : *offsets) { + builder.AddOffset(offset); + } + + // Store the offset since we need to put those after the dex file. Table offsets are relative + // to the start of the quicken info section. + out_table_offsets_->push_back(current_offset); + + const uint32_t length = table_data.size() * sizeof(table_data.front()); + if (!out_->WriteFully(table_data.data(), length)) { + PLOG(ERROR) << "Failed to write quickening offset table for " << dex_file->GetLocation() + << " to " << out_->GetLocation(); + return false; } + written_bytes_ += length; } return true; } @@ -2735,14 +2736,16 @@ class OatWriter::WriteQuickeningIndicesMethodVisitor { private: OutputStream* const out_; - const uint32_t quickening_info_bytes_; - size_t written_bytes_; - // Maps quickening map to its offset in the file. - const SafeMap<const uint8_t*, uint32_t>& offset_map_; + const uint32_t start_offset_; + size_t written_bytes_ = 0u; + // Maps containing the offsets for the tables. + SafeMap<const DexFile*, std::vector<uint32_t>>* const quicken_info_offset_indices_; + std::vector<uint32_t>* const out_table_offsets_; }; bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { size_t initial_offset = vdex_size_; + // Make sure the table is properly aligned. size_t start_offset = RoundUp(initial_offset, 4u); off_t actual_offset = vdex_out->Seek(start_offset, kSeekSet); @@ -2753,36 +2756,71 @@ bool OatWriter::WriteQuickeningInfo(OutputStream* vdex_out) { return false; } - if (compiler_driver_->GetCompilerOptions().IsAnyCompilationEnabled()) { + size_t current_offset = start_offset; + if (compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { std::vector<uint32_t> dex_files_indices; - SafeMap<const uint8_t*, uint32_t> offset_map; - WriteQuickeningInfoMethodVisitor visitor1(this, vdex_out, start_offset, &offset_map); - if (!VisitDexMethods(&visitor1)) { + WriteQuickeningInfoMethodVisitor write_quicken_info_visitor(this, vdex_out); + if (!write_quicken_info_visitor.VisitDexMethods(*dex_files_)) { PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation(); return false; } - if (visitor1.GetNumberOfWrittenBytes() > 0) { - WriteQuickeningIndicesMethodVisitor visitor2(vdex_out, - visitor1.GetNumberOfWrittenBytes(), - offset_map); - if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) { - PLOG(ERROR) << "Failed to write the vdex quickening info. File: " - << vdex_out->GetLocation(); + uint32_t quicken_info_offset = write_quicken_info_visitor.GetNumberOfWrittenBytes(); + current_offset = current_offset + quicken_info_offset; + uint32_t before_offset = current_offset; + current_offset = RoundUp(current_offset, QuickenInfoOffsetTableAccessor::Alignment()); + const size_t extra_bytes = current_offset - before_offset; + quicken_info_offset += extra_bytes; + actual_offset = vdex_out->Seek(current_offset, kSeekSet); + if (actual_offset != static_cast<off_t>(current_offset)) { + PLOG(ERROR) << "Failed to seek to quickening offset table section. Actual: " << actual_offset + << " Expected: " << current_offset + << " Output: " << vdex_out->GetLocation(); + return false; + } + + std::vector<uint32_t> table_offsets; + WriteQuickeningInfoOffsetsMethodVisitor table_visitor( + vdex_out, + quicken_info_offset, + &write_quicken_info_visitor.GetQuickenInfoOffsetIndicies(), + /*out*/ &table_offsets); + if (!table_visitor.VisitDexMethods(*dex_files_)) { + PLOG(ERROR) << "Failed to write the vdex quickening info. File: " + << vdex_out->GetLocation(); + return false; + } + + CHECK_EQ(table_offsets.size(), dex_files_->size()); + + current_offset += table_visitor.GetNumberOfWrittenBytes(); + + // Store the offset table offset as a preheader for each dex. + size_t index = 0; + for (const OatDexFile& oat_dex_file : oat_dex_files_) { + const off_t desired_offset = oat_dex_file.dex_file_offset_ - + sizeof(VdexFile::QuickeningTableOffsetType); + actual_offset = vdex_out->Seek(desired_offset, kSeekSet); + if (actual_offset != desired_offset) { + PLOG(ERROR) << "Failed to seek to before dex file for writing offset table offset: " + << actual_offset << " Expected: " << desired_offset + << " Output: " << vdex_out->GetLocation(); return false; } - - if (!vdex_out->Flush()) { - PLOG(ERROR) << "Failed to flush stream after writing quickening info." + uint32_t offset = table_offsets[index]; + if (!vdex_out->WriteFully(reinterpret_cast<const uint8_t*>(&offset), sizeof(offset))) { + PLOG(ERROR) << "Failed to write verifier deps." << " File: " << vdex_out->GetLocation(); return false; } - size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() + - visitor2.GetNumberOfWrittenBytes(); - } else { - // We know we did not quicken. - size_quickening_info_ = 0; + ++index; } + if (!vdex_out->Flush()) { + PLOG(ERROR) << "Failed to flush stream after writing quickening info." + << " File: " << vdex_out->GetLocation(); + return false; + } + size_quickening_info_ = current_offset - start_offset; } else { // We know we did not quicken. size_quickening_info_ = 0; @@ -3357,9 +3395,15 @@ bool OatWriter::SeekToDexFile(OutputStream* out, File* file, OatDexFile* oat_dex // Dex files are required to be 4 byte aligned. size_t initial_offset = vdex_size_; size_t start_offset = RoundUp(initial_offset, 4); - size_t file_offset = start_offset; size_dex_file_alignment_ += start_offset - initial_offset; + // Leave extra room for the quicken offset table offset. + start_offset += sizeof(VdexFile::QuickeningTableOffsetType); + // TODO: Not count the offset as part of alignment. + size_dex_file_alignment_ += sizeof(VdexFile::QuickeningTableOffsetType); + + size_t file_offset = start_offset; + // Seek to the start of the dex file and flush any pending operations in the stream. // Verify that, after flushing the stream, the file is at the same offset as the stream. off_t actual_offset = out->Seek(file_offset, kSeekSet); diff --git a/dex2oat/linker/oat_writer.h b/dex2oat/linker/oat_writer.h index c9deea9a4b..824b395b02 100644 --- a/dex2oat/linker/oat_writer.h +++ b/dex2oat/linker/oat_writer.h @@ -275,7 +275,7 @@ class OatWriter { class WriteMapMethodVisitor; class WriteMethodInfoVisitor; class WriteQuickeningInfoMethodVisitor; - class WriteQuickeningIndicesMethodVisitor; + class WriteQuickeningInfoOffsetsMethodVisitor; // Visit all the methods in all the compiled dex files in their definition order // with a given DexMethodVisitor. diff --git a/dex2oat/linker/oat_writer_test.cc b/dex2oat/linker/oat_writer_test.cc index 99bc1adabb..321a2e4d46 100644 --- a/dex2oat/linker/oat_writer_test.cc +++ b/dex2oat/linker/oat_writer_test.cc @@ -659,7 +659,11 @@ void OatTest::TestDexFileInput(bool verify, bool low_4gb, bool use_profile) { ASSERT_EQ(dex_file2_data->GetLocation(), opened_dex_file2->GetLocation()); const VdexFile::Header &vdex_header = opened_oat_file->GetVdexFile()->GetHeader(); - ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + if (!compiler_driver_->GetCompilerOptions().IsQuickeningCompilationEnabled()) { + // If quickening is enabled we will always write the table since there is no special logic that + // checks for all methods not being quickened (not worth the complexity). + ASSERT_EQ(vdex_header.GetQuickeningInfoSize(), 0u); + } int64_t actual_vdex_size = vdex_file.GetFile()->GetLength(); ASSERT_GE(actual_vdex_size, 0); diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc index 2c98e12741..c2532e4235 100644 --- a/dexdump/dexdump.cc +++ b/dexdump/dexdump.cc @@ -1186,7 +1186,7 @@ static void dumpBytecodes(const DexFile* pDexFile, u4 idx, */ static void dumpCode(const DexFile* pDexFile, u4 idx, u4 flags, const DexFile::CodeItem* pCode, u4 codeOffset) { - CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + CodeItemDebugInfoAccessor accessor(*pDexFile, pCode); fprintf(gOutFile, " registers : %d\n", accessor.RegistersSize()); fprintf(gOutFile, " ins : %d\n", accessor.InsSize()); diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc index 556938b563..28370aac44 100644 --- a/dexlist/dexlist.cc +++ b/dexlist/dexlist.cc @@ -100,7 +100,7 @@ static void dumpMethod(const DexFile* pDexFile, if (pCode == nullptr || codeOffset == 0) { return; } - CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, pDexFile->GetDebugInfoOffset(pCode)); + CodeItemDebugInfoAccessor accessor(*pDexFile, pCode); // Method information. const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx); diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc index 6668dace89..f53846c04d 100644 --- a/oatdump/oatdump.cc +++ b/oatdump/oatdump.cc @@ -718,7 +718,6 @@ class OatDumper { } vdex_file->Unquicken(MakeNonOwningPointerVector(tmp_dex_files), - vdex_file->GetQuickeningInfo(), /* decompile_return_instruction */ true); *dex_files = std::move(tmp_dex_files); diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc index da7d60ac2f..731566f267 100644 --- a/openjdkjvmti/fixed_up_dex_file.cc +++ b/openjdkjvmti/fixed_up_dex_file.cc @@ -62,8 +62,7 @@ static void DoDexUnquicken(const art::DexFile& new_dex_file, const art::DexFile& if (vdex == nullptr) { return; } - art::VdexFile::UnquickenDexFile( - new_dex_file, vdex->GetQuickeningInfo(), /* decompile_return_instruction */true); + vdex->UnquickenDexFile(new_dex_file, original_dex_file, /* decompile_return_instruction */true); } std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) { diff --git a/runtime/art_method.cc b/runtime/art_method.cc index 44a5dde485..96468bba60 100644 --- a/runtime/art_method.cc +++ b/runtime/art_method.cc @@ -562,14 +562,14 @@ bool ArtMethod::EqualParameters(Handle<mirror::ObjectArray<mirror::Class>> param return true; } -const uint8_t* ArtMethod::GetQuickenedInfo() { +ArrayRef<const uint8_t> ArtMethod::GetQuickenedInfo() { const DexFile& dex_file = GetDeclaringClass()->GetDexFile(); const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return nullptr; + return ArrayRef<const uint8_t>(); } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf( - dex_file, GetCodeItemOffset()); + return oat_dex_file->GetOatFile()->GetVdexFile()->GetQuickenedInfoOf(dex_file, + GetDexMethodIndex()); } const OatQuickMethodHeader* ArtMethod::GetOatQuickMethodHeader(uintptr_t pc) { diff --git a/runtime/art_method.h b/runtime/art_method.h index c4a586ed92..cd06354859 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -21,6 +21,7 @@ #include <android-base/logging.h> +#include "base/array_ref.h" #include "base/bit_utils.h" #include "base/casts.h" #include "base/enums.h" @@ -662,7 +663,7 @@ class ArtMethod FINAL { return hotness_count_; } - const uint8_t* GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); + ArrayRef<const uint8_t> GetQuickenedInfo() REQUIRES_SHARED(Locks::mutator_lock_); // Returns the method header for the compiled code containing 'pc'. Note that runtime // methods will return null for this method, as they are not oat based. diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h index 2792dc0663..01a2b2f8e6 100644 --- a/runtime/dex/code_item_accessors-inl.h +++ b/runtime/dex/code_item_accessors-inl.h @@ -36,14 +36,6 @@ inline CodeItemDataAccessor::CodeItemDataAccessor(ArtMethod* method) inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method) : CodeItemDebugInfoAccessor(*method->GetDexFile(), method->GetCodeItem()) {} -inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile& dex_file, - const DexFile::CodeItem* code_item) { - if (code_item == nullptr) { - return; - } - Init(dex_file, code_item, OatFile::GetDebugInfoOffset(dex_file, code_item->debug_info_off_)); -} - } // namespace art #endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_INL_H_ diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h index baea856e71..a613559644 100644 --- a/runtime/dex/code_item_accessors-no_art-inl.h +++ b/runtime/dex/code_item_accessors-no_art-inl.h @@ -180,6 +180,14 @@ inline bool CodeItemDebugInfoAccessor::DecodeDebugLocalInfo(bool is_static, context); } +inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(const DexFile& dex_file, + const DexFile::CodeItem* code_item) { + if (code_item == nullptr) { + return; + } + Init(dex_file, code_item, code_item->debug_info_off_); +} + } // namespace art #endif // ART_RUNTIME_DEX_CODE_ITEM_ACCESSORS_NO_ART_INL_H_ diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h index c2a36ce01a..9b31a75ac5 100644 --- a/runtime/dex/dex_file.h +++ b/runtime/dex/dex_file.h @@ -303,15 +303,6 @@ class DexFile { // Raw code_item. struct CodeItem { - // Used when quickening / unquickening. - void SetDebugInfoOffset(uint32_t new_offset) { - debug_info_off_ = new_offset; - } - - uint32_t GetDebugInfoOffset() const { - return debug_info_off_; - } - protected: uint16_t registers_size_; // the number of registers used by this code // (locals + parameters) @@ -322,12 +313,7 @@ class DexFile { uint16_t tries_size_; // the number of try_items for this instance. If non-zero, // then these appear as the tries array just after the // insns in this instance. - // Normally holds file offset to debug info stream. In case the method has been quickened - // holds an offset in the Vdex file containing both the actual debug_info_off and the - // quickening info offset. - // Don't use this field directly, use OatFile::GetDebugInfoOffset in general ART code, - // or DexFile::GetDebugInfoOffset in code that are not using a Runtime. - uint32_t debug_info_off_; + uint32_t debug_info_off_; // Holds file offset to debug info stream. uint32_t insns_size_in_code_units_; // size of the insns array, in 2 byte code units uint16_t insns_[1]; // actual array of bytecode. @@ -337,7 +323,6 @@ class DexFile { friend class CodeItemDataAccessor; friend class CodeItemDebugInfoAccessor; friend class CodeItemInstructionAccessor; - friend class VdexFile; // TODO: Remove this one when it's cleaned up. DISALLOW_COPY_AND_ASSIGN(CodeItem); }; @@ -712,15 +697,6 @@ class DexFile { return reinterpret_cast<const CodeItem*>(addr); } - uint32_t GetDebugInfoOffset(const CodeItem* code_item) const { - if (code_item == nullptr) { - return 0; - } - CHECK(oat_dex_file_ == nullptr) - << "Should only use GetDebugInfoOffset in a non runtime setup"; - return code_item->GetDebugInfoOffset(); - } - const char* GetReturnTypeDescriptor(const ProtoId& proto_id) const; // Returns the number of prototype identifiers in the .dex file. diff --git a/runtime/dex_to_dex_decompiler.cc b/runtime/dex_to_dex_decompiler.cc index e1c07baede..7887191713 100644 --- a/runtime/dex_to_dex_decompiler.cc +++ b/runtime/dex_to_dex_decompiler.cc @@ -36,8 +36,7 @@ class DexDecompiler { const ArrayRef<const uint8_t>& quickened_info, bool decompile_return_instruction) : code_item_accessor_(dex_file, &code_item), - quicken_info_(quickened_info.data()), - quicken_info_number_of_indices_(QuickenInfoTable::NumberOfIndices(quickened_info.size())), + quicken_info_(quickened_info), decompile_return_instruction_(decompile_return_instruction) {} bool Decompile(); @@ -72,7 +71,7 @@ class DexDecompiler { } uint16_t NextIndex() { - DCHECK_LT(quicken_index_, quicken_info_number_of_indices_); + DCHECK_LT(quicken_index_, quicken_info_.NumIndices()); const uint16_t ret = quicken_info_.GetData(quicken_index_); quicken_index_++; return ret; @@ -80,7 +79,6 @@ class DexDecompiler { const CodeItemInstructionAccessor code_item_accessor_; const QuickenInfoTable quicken_info_; - const size_t quicken_info_number_of_indices_; const bool decompile_return_instruction_; size_t quicken_index_ = 0u; @@ -104,7 +102,7 @@ bool DexDecompiler::Decompile() { break; case Instruction::NOP: - if (quicken_info_number_of_indices_ > 0) { + if (quicken_info_.NumIndices() > 0) { // Only try to decompile NOP if there are more than 0 indices. Not having // any index happens when we unquicken a code item that only has // RETURN_VOID_NO_BARRIER as quickened instruction. @@ -181,14 +179,14 @@ bool DexDecompiler::Decompile() { } } - if (quicken_index_ != quicken_info_number_of_indices_) { + if (quicken_index_ != quicken_info_.NumIndices()) { if (quicken_index_ == 0) { LOG(WARNING) << "Failed to use any value in quickening info," << " potentially due to duplicate methods."; } else { LOG(FATAL) << "Failed to use all values in quickening info." << " Actual: " << std::hex << quicken_index_ - << " Expected: " << quicken_info_number_of_indices_; + << " Expected: " << quicken_info_.NumIndices(); return false; } } diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc index c6664411e4..110b60e9e0 100644 --- a/runtime/oat_file.cc +++ b/runtime/oat_file.cc @@ -1534,21 +1534,6 @@ ArrayRef<GcRoot<mirror::Object>> OatFile::GetBssGcRoots() const { } } -uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off) { - // Note that although the specification says that 0 should be used if there - // is no debug information, some applications incorrectly use 0xFFFFFFFF. - // The following check also handles debug_info_off == 0. - if (debug_info_off < dex_file.Size() || debug_info_off == 0xFFFFFFFF) { - return debug_info_off; - } - const OatFile::OatDexFile* oat_dex_file = dex_file.GetOatDexFile(); - if (oat_dex_file == nullptr || (oat_dex_file->GetOatFile() == nullptr)) { - return debug_info_off; - } - return oat_dex_file->GetOatFile()->GetVdexFile()->GetDebugInfoOffset( - dex_file, debug_info_off); -} - const OatFile::OatDexFile* OatFile::GetOatDexFile(const char* dex_location, const uint32_t* dex_location_checksum, std::string* error_msg) const { diff --git a/runtime/oat_file.h b/runtime/oat_file.h index e9f7edca61..bf22e0b88b 100644 --- a/runtime/oat_file.h +++ b/runtime/oat_file.h @@ -115,10 +115,6 @@ class OatFile { const char* abs_dex_location, std::string* error_msg); - // Return the actual debug info offset for an offset that might be actually pointing to - // dequickening info. The returned debug info offset is the one originally in the the dex file. - static uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t debug_info_off); - virtual ~OatFile(); bool IsExecutable() const { diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h index ce11f3c19b..52eca61c06 100644 --- a/runtime/quicken_info.h +++ b/runtime/quicken_info.h @@ -17,15 +17,93 @@ #ifndef ART_RUNTIME_QUICKEN_INFO_H_ #define ART_RUNTIME_QUICKEN_INFO_H_ +#include "base/array_ref.h" #include "dex/dex_instruction.h" +#include "leb128.h" namespace art { -// QuickenInfoTable is a table of 16 bit dex indices. There is one slot fo every instruction that is -// possibly dequickenable. +// Table for getting the offset of quicken info. Doesn't have one slot for each index, so a +// combination of iteration and indexing is required to get the quicken info for a given dex method +// index. +class QuickenInfoOffsetTableAccessor { + public: + using TableType = uint32_t; + static constexpr uint32_t kElementsPerIndex = 16; + + class Builder { + public: + explicit Builder(std::vector<uint8_t>* out_data) : out_data_(out_data) {} + + void AddOffset(uint32_t index) { + out_data_->insert(out_data_->end(), + reinterpret_cast<const uint8_t*>(&index), + reinterpret_cast<const uint8_t*>(&index + 1)); + } + + private: + std::vector<uint8_t>* const out_data_; + }; + + // The table only covers every kElementsPerIndex indices. + static bool IsCoveredIndex(uint32_t index) { + return index % kElementsPerIndex == 0; + } + + explicit QuickenInfoOffsetTableAccessor(const uint8_t* data, uint32_t max_index) + : table_(reinterpret_cast<const uint32_t*>(data)), + num_indices_(RoundUp(max_index, kElementsPerIndex) / kElementsPerIndex) {} + + size_t SizeInBytes() const { + return NumIndices() * sizeof(table_[0]); + } + + uint32_t NumIndices() const { + return num_indices_; + } + + // Returns the offset for the index at or before the desired index. If the offset is for an index + // before the desired one, remainder is how many elements to traverse to reach the desired index. + TableType ElementOffset(uint32_t index, uint32_t* remainder) const { + *remainder = index % kElementsPerIndex; + return table_[index / kElementsPerIndex]; + } + + const uint8_t* DataEnd() const { + return reinterpret_cast<const uint8_t*>(table_ + NumIndices()); + } + + static uint32_t Alignment() { + return alignof(TableType); + } + + private: + const TableType* table_; + uint32_t num_indices_; +}; + +// QuickenInfoTable is a table of 16 bit dex indices. There is one slot for every instruction that +// is possibly dequickenable. class QuickenInfoTable { public: - explicit QuickenInfoTable(const uint8_t* data) : data_(data) {} + class Builder { + public: + Builder(std::vector<uint8_t>* out_data, size_t num_elements) : out_data_(out_data) { + EncodeUnsignedLeb128(out_data_, num_elements); + } + + void AddIndex(uint16_t index) { + out_data_->push_back(static_cast<uint8_t>(index)); + out_data_->push_back(static_cast<uint8_t>(index >> 8)); + } + + private: + std::vector<uint8_t>* const out_data_; + }; + + explicit QuickenInfoTable(ArrayRef<const uint8_t> data) + : data_(data.data()), + num_elements_(!data.empty() ? DecodeUnsignedLeb128(&data_) : 0u) {} bool IsNull() const { return data_ == nullptr; @@ -44,8 +122,18 @@ class QuickenInfoTable { return bytes / sizeof(uint16_t); } + static size_t SizeInBytes(ArrayRef<const uint8_t> data) { + QuickenInfoTable table(data); + return table.data_ + table.NumIndices() * 2 - data.data(); + } + + uint32_t NumIndices() const { + return num_elements_; + } + private: - const uint8_t* const data_; + const uint8_t* data_; + const uint32_t num_elements_; DISALLOW_COPY_AND_ASSIGN(QuickenInfoTable); }; diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index 3ac4aa24c9..aa77b21757 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -28,6 +28,7 @@ #include "dex/dex_file.h" #include "dex/dex_file_loader.h" #include "dex_to_dex_decompiler.h" +#include "quicken_info.h" namespace art { @@ -148,9 +149,8 @@ std::unique_ptr<VdexFile> VdexFile::OpenAtAddress(uint8_t* mmap_addr, if (!vdex->OpenAllDexFiles(&unique_ptr_dex_files, error_msg)) { return nullptr; } - Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), - vdex->GetQuickeningInfo(), - /* decompile_return_instruction */ false); + vdex->Unquicken(MakeNonOwningPointerVector(unique_ptr_dex_files), + /* decompile_return_instruction */ false); // Update the quickening info size to pretend there isn't any. reinterpret_cast<Header*>(vdex->mmap_->Begin())->quickening_info_size_ = 0; } @@ -163,14 +163,15 @@ const uint8_t* VdexFile::GetNextDexFileData(const uint8_t* cursor) const { DCHECK(cursor == nullptr || (cursor > Begin() && cursor <= End())); if (cursor == nullptr) { // Beginning of the iteration, return the first dex file if there is one. - return HasDexSection() ? DexBegin() : nullptr; + return HasDexSection() ? DexBegin() + sizeof(QuickeningTableOffsetType) : nullptr; } else { // Fetch the next dex file. Return null if there is none. const uint8_t* data = cursor + reinterpret_cast<const DexFile::Header*>(cursor)->file_size_; // Dex files are required to be 4 byte aligned. the OatWriter makes sure they are, see // OatWriter::SeekToDexFiles. data = AlignUp(data, 4); - return (data == DexEnd()) ? nullptr : data; + + return (data == DexEnd()) ? nullptr : data + sizeof(QuickeningTableOffsetType); } } @@ -200,64 +201,68 @@ bool VdexFile::OpenAllDexFiles(std::vector<std::unique_ptr<const DexFile>>* dex_ return true; } -void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction) { - if (quickening_info.size() == 0 && !decompile_return_instruction) { - // Bail early if there is no quickening info and no need to decompile - // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. - return; - } - - for (uint32_t i = 0; i < dex_files.size(); ++i) { - UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction); +void VdexFile::Unquicken(const std::vector<const DexFile*>& target_dex_files, + bool decompile_return_instruction) const { + const uint8_t* source_dex = GetNextDexFileData(nullptr); + for (const DexFile* target_dex : target_dex_files) { + UnquickenDexFile(*target_dex, source_dex, decompile_return_instruction); + source_dex = GetNextDexFileData(source_dex); } + DCHECK(source_dex == nullptr); } -typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t; - -static uint32_t GetDebugInfoOffsetInternal(const DexFile& dex_file, - uint32_t offset_in_code_item, - const ArrayRef<const uint8_t>& quickening_info) { - if (quickening_info.size() == 0) { - // No quickening info: offset is the right one, return it. - return offset_in_code_item; - } - uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); - return *reinterpret_cast<const unaligned_uint32_t*>(quickening_info.data() + quickening_offset); +uint32_t VdexFile::GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const { + DCHECK_GE(source_dex_begin, DexBegin()); + DCHECK_LT(source_dex_begin, DexEnd()); + return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1]; } -static uint32_t GetQuickeningInfoOffsetFrom(const DexFile& dex_file, - uint32_t offset_in_code_item, - const ArrayRef<const uint8_t>& quickening_info) { - if (offset_in_code_item < dex_file.Size()) { - return VdexFile::kNoQuickeningInfoOffset; - } - if (quickening_info.size() == 0) { - // No quickening info. - return VdexFile::kNoQuickeningInfoOffset; - } - uint32_t quickening_offset = offset_in_code_item - dex_file.Size(); +QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( + const uint8_t* source_dex_begin, + uint32_t num_method_ids, + const ArrayRef<const uint8_t>& quickening_info) const { + // The offset a is in preheader right before the dex file. + const uint32_t offset = GetQuickeningInfoTableOffset(source_dex_begin); + const uint8_t* data_ptr = quickening_info.data() + offset; + return QuickenInfoOffsetTableAccessor(data_ptr, num_method_ids); +} - // Add 2 * sizeof(uint32_t) for the debug info offset and the data offset. - CHECK_LE(quickening_offset + 2 * sizeof(uint32_t), quickening_info.size()); - return *reinterpret_cast<const unaligned_uint32_t*>( - quickening_info.data() + quickening_offset + sizeof(uint32_t)); +QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable( + const DexFile& dex_file, + const ArrayRef<const uint8_t>& quickening_info) const { + return GetQuickenInfoOffsetTable(dex_file.Begin(), dex_file.NumMethodIds(), quickening_info); } static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info, uint32_t quickening_offset) { - return (quickening_offset == VdexFile::kNoQuickeningInfoOffset) - ? ArrayRef<const uint8_t>(nullptr, 0) - : quickening_info.SubArray( - quickening_offset + sizeof(uint32_t), - *reinterpret_cast<const unaligned_uint32_t*>( - quickening_info.data() + quickening_offset)); + ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset); + return remaining.SubArray(0u, QuickenInfoTable::SizeInBytes(remaining)); +} + +static uint32_t GetQuickeningInfoOffset(const QuickenInfoOffsetTableAccessor& table, + uint32_t dex_method_index, + const ArrayRef<const uint8_t>& quickening_info) { + DCHECK(!quickening_info.empty()); + uint32_t remainder; + uint32_t offset = table.ElementOffset(dex_method_index, &remainder); + // Decode the sizes for the remainder offsets (not covered by the table). + while (remainder != 0) { + offset += GetQuickeningInfoAt(quickening_info, offset).size(); + --remainder; + } + return offset; +} + +void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, + const DexFile& source_dex_file, + bool decompile_return_instruction) const { + UnquickenDexFile(target_dex_file, source_dex_file.Begin(), decompile_return_instruction); } void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction) { + const uint8_t* source_dex_begin, + bool decompile_return_instruction) const { + ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); if (quickening_info.size() == 0 && !decompile_return_instruction) { // Bail early if there is no quickening info and no need to decompile // RETURN_VOID_NO_BARRIER instructions to RETURN_VOID instructions. @@ -272,19 +277,20 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, class_it.Next()) { if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) { const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem(); - uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( - target_dex_file, code_item->debug_info_off_, quickening_info); - if (quickening_offset != VdexFile::kNoQuickeningInfoOffset) { - // If we have quickening data, put back the original debug_info_off. - const_cast<DexFile::CodeItem*>(code_item)->SetDebugInfoOffset( - GetDebugInfoOffsetInternal(target_dex_file, - code_item->debug_info_off_, - quickening_info)); + ArrayRef<const uint8_t> quicken_data; + if (!quickening_info.empty()) { + const uint32_t quickening_offset = GetQuickeningInfoOffset( + GetQuickenInfoOffsetTable(source_dex_begin, + target_dex_file.NumMethodIds(), + quickening_info), + class_it.GetMemberIndex(), + quickening_info); + quicken_data = GetQuickeningInfoAt(quickening_info, quickening_offset); } optimizer::ArtDecompileDEX( target_dex_file, *code_item, - GetQuickeningInfoAt(quickening_info, quickening_offset), + quicken_data, decompile_return_instruction); } } @@ -292,25 +298,17 @@ void VdexFile::UnquickenDexFile(const DexFile& target_dex_file, } } -uint32_t VdexFile::GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const { - return GetDebugInfoOffsetInternal(dex_file, offset_in_code_item, GetQuickeningInfo()); -} - -const uint8_t* VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, - uint32_t code_item_offset) const { +ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, + uint32_t dex_method_idx) const { ArrayRef<const uint8_t> quickening_info = GetQuickeningInfo(); - uint32_t quickening_offset = GetQuickeningInfoOffsetFrom( - dex_file, dex_file.GetCodeItem(code_item_offset)->debug_info_off_, quickening_info); - - return GetQuickeningInfoAt(quickening_info, quickening_offset).data(); -} - -bool VdexFile::CanEncodeQuickenedData(const DexFile& dex_file) { - // We are going to use the debug_info_off_ to signal there is - // quickened data, by putting a value greater than dex_file.Size(). So - // make sure we have some room in the offset by checking that we have at least - // half of the range of a uint32_t. - return dex_file.Size() <= (std::numeric_limits<uint32_t>::max() >> 1); + if (quickening_info.empty()) { + return ArrayRef<const uint8_t>(); + } + const uint32_t quickening_offset = GetQuickeningInfoOffset( + GetQuickenInfoOffsetTable(dex_file, quickening_info), + dex_method_idx, + quickening_info); + return GetQuickeningInfoAt(quickening_info, quickening_offset); } } // namespace art diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h index f78335d347..22599b0b13 100644 --- a/runtime/vdex_file.h +++ b/runtime/vdex_file.h @@ -24,6 +24,7 @@ #include "base/macros.h" #include "mem_map.h" #include "os.h" +#include "quicken_info.h" namespace art { @@ -35,18 +36,17 @@ class DexFile; // File format: // VdexFile::Header fixed-length header // -// DEX[0] array of the input DEX files -// DEX[1] the bytecode may have been quickened +// quicken_table_off[0] offset into QuickeningInfo section for offset table for DEX[0]. +// DEX[0] array of the input DEX files, the bytecode may have been quickened. +// quicken_table_off[1] +// DEX[1] // ... // DEX[D] // VerifierDeps // uint8[D][] verification dependencies // QuickeningInfo // uint8[D][] quickening data -// unaligned_uint32_t[D][2][] table of offsets pair: -// uint32_t[0] contains original CodeItem::debug_info_off_ -// uint32_t[1] contains quickening data offset from the start -// of QuickeningInfo +// uint32[D][] quickening data offset tables class VdexFile { public: @@ -84,8 +84,8 @@ class VdexFile { private: static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' }; - // Last update: Lookup-friendly encoding for quickening info. - static constexpr uint8_t kVdexVersion[] = { '0', '1', '1', '\0' }; + // Last update: Compact quicken info tables that don't modify the dex code items. + static constexpr uint8_t kVdexVersion[] = { '0', '1', '2', '\0' }; uint8_t magic_[4]; uint8_t version_[4]; @@ -98,6 +98,7 @@ class VdexFile { }; typedef uint32_t VdexChecksum; + using QuickeningTableOffsetType = uint32_t; explicit VdexFile(MemMap* mmap) : mmap_(mmap) {} @@ -204,29 +205,42 @@ class VdexFile { // `decompile_return_instruction` controls if RETURN_VOID_BARRIER instructions are // decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator // instead of the faster QuickeningInfoIterator. - static void Unquicken(const std::vector<const DexFile*>& dex_files, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction); + // Always unquickens using the vdex dex files as the source for quicken tables. + void Unquicken(const std::vector<const DexFile*>& target_dex_files, + bool decompile_return_instruction) const; // Fully unquicken `target_dex_file` based on `quickening_info`. - static void UnquickenDexFile(const DexFile& target_dex_file, - ArrayRef<const uint8_t> quickening_info, - bool decompile_return_instruction); + void UnquickenDexFile(const DexFile& target_dex_file, + const DexFile& source_dex_file, + bool decompile_return_instruction) const; - // Return the quickening info of the given code item. - const uint8_t* GetQuickenedInfoOf(const DexFile& dex_file, uint32_t code_item_offset) const; + // Return the quickening info of a given method index (or null if it's empty). + ArrayRef<const uint8_t> GetQuickenedInfoOf(const DexFile& dex_file, + uint32_t dex_method_idx) const; - uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const; + private: + uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const; - static bool CanEncodeQuickenedData(const DexFile& dex_file); + // Source dex must be the in the vdex file. + void UnquickenDexFile(const DexFile& target_dex_file, + const uint8_t* source_dex_begin, + bool decompile_return_instruction) const; - static constexpr uint32_t kNoQuickeningInfoOffset = -1; + QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + const DexFile& dex_file, + const ArrayRef<const uint8_t>& quickening_info) const; + + QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable( + const uint8_t* source_dex_begin, + uint32_t num_method_ids, + const ArrayRef<const uint8_t>& quickening_info) const; - private: bool HasDexSection() const { return GetHeader().GetDexSize() != 0; } + bool ContainsDexFile(const DexFile& dex_file) const; + const uint8_t* DexBegin() const { return Begin() + sizeof(Header) + GetHeader().GetSizeOfChecksumsSection(); } @@ -235,8 +249,6 @@ class VdexFile { return DexBegin() + GetHeader().GetDexSize(); } - uint32_t GetDexFileIndex(const DexFile& dex_file) const; - std::unique_ptr<MemMap> mmap_; DISALLOW_COPY_AND_ASSIGN(VdexFile); |