Move quickening info logic to its own table
Added a table that is indexed by dex method index. To prevent size
overhead, there is only one slot for each 16 method indices. This
means there is up to 15 loop iterations to get the quickening info
for a method. The quickening infos are now prefixed by a leb
encoded length. This allows methods that aren't quickened to only
have 1.25 bytes of space overhead.
The value was picked arbitrarily, there is little advantage to
increasing the value since the table only takes 1 byte per 4 method
indices currently. JIT benchmarks do not regress with the change.
There is a net space saving from removing 8 bytes from each
quickening info since most scenarios have more quickened methods than
compiled methods.
For getting quick access to the table, a 4 byte preheader was added
to each dex in the vdex file
Removed logic that stored the quickening info in the CodeItem
debug_info_offset field.
The change adds a small quicken table for each method index, this
means that filters that don't quicken will have a slight increase in
size. The worst case scenario is compiling all the methods, this
results in 0.3% larger vdex for this case. The change also disables
deduping since the quicken infos need to be in dex method index
order.
For filters that don't compile most methods like quicken and
speed-profile, there is space savings. For quicken, the vdex is 2%
smaller.
Bug: 71605148
Bug: 63756964
Test: test-art-host
Change-Id: I89cb679538811369c36b6ac8c40ea93135f813cd
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 52cb217..308e75d 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -373,15 +373,15 @@
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 c0886d0..8698659 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -424,10 +424,6 @@
// 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 af537dd..a1a5692 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -43,7 +43,7 @@
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 @@
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 c16a3a9..5a1914c 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 @@
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 @@
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 72a93c1..64a1ecc 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -49,7 +49,7 @@
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 708a097..4428c53 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 @@
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 f4115f7..8966d56 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -783,7 +783,7 @@
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 @@
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 8d0d89e..06cc71b 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -1807,9 +1807,7 @@
// 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 16d70da..cec7960 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 @@
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;
- 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();
+ // 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();
+ }
+
+ // 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 @@
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 @@
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 @@
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 @@
// 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 c9deea9..824b395 100644
--- a/dex2oat/linker/oat_writer.h
+++ b/dex2oat/linker/oat_writer.h
@@ -275,7 +275,7 @@
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 99bc1ad..321a2e4 100644
--- a/dex2oat/linker/oat_writer_test.cc
+++ b/dex2oat/linker/oat_writer_test.cc
@@ -659,7 +659,11 @@
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 2c98e12..c2532e4 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1186,7 +1186,7 @@
*/
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 556938b..28370aa 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -100,7 +100,7 @@
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 6668dac..f53846c 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -718,7 +718,6 @@
}
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 da7d60a..731566f 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -62,8 +62,7 @@
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 44a5dde..96468bb 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -562,14 +562,14 @@
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 c4a586e..cd06354 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 @@
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 2792dc0..01a2b2f 100644
--- a/runtime/dex/code_item_accessors-inl.h
+++ b/runtime/dex/code_item_accessors-inl.h
@@ -36,14 +36,6 @@
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 baea856..a613559 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 @@
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 c2a36ce..9b31a75 100644
--- a/runtime/dex/dex_file.h
+++ b/runtime/dex/dex_file.h
@@ -303,15 +303,6 @@
// 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 @@
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 @@
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 @@
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 e1c07ba..7887191 100644
--- a/runtime/dex_to_dex_decompiler.cc
+++ b/runtime/dex_to_dex_decompiler.cc
@@ -36,8 +36,7 @@
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 @@
}
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 @@
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 @@
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 @@
}
}
- 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 c666441..110b60e 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1534,21 +1534,6 @@
}
}
-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 e9f7edc..bf22e0b 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -115,10 +115,6 @@
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 ce11f3c..52eca61 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 @@
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 3ac4aa2..aa77b21 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 @@
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 @@
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 @@
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;
+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);
}
-
- for (uint32_t i = 0; i < dex_files.size(); ++i) {
- UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction);
- }
+ 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,
- ArrayRef<const uint8_t> quickening_info,
- bool decompile_return_instruction) {
+ 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,
+ 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 @@
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 @@
}
}
-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 f78335d..22599b0 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 @@
// 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 @@
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 @@
};
typedef uint32_t VdexChecksum;
+ using QuickeningTableOffsetType = uint32_t;
explicit VdexFile(MemMap* mmap) : mmap_(mmap) {}
@@ -204,29 +205,42 @@
// `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;
-
- uint32_t GetDebugInfoOffset(const DexFile& dex_file, uint32_t offset_in_code_item) const;
-
- static bool CanEncodeQuickenedData(const DexFile& dex_file);
-
- static constexpr uint32_t kNoQuickeningInfoOffset = -1;
+ // 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;
private:
+ uint32_t GetQuickeningInfoTableOffset(const uint8_t* source_dex_begin) const;
+
+ // 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;
+
+ 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;
+
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 @@
return DexBegin() + GetHeader().GetDexSize();
}
- uint32_t GetDexFileIndex(const DexFile& dex_file) const;
-
std::unique_ptr<MemMap> mmap_;
DISALLOW_COPY_AND_ASSIGN(VdexFile);