Optimize lookup of quickening data.
Use the debug_info_off_ of CodeItem to store the quickening
offset in the vdex.
Impact:
- Code size almost unchanged (1 word saved per dex file in a vdex)
- GetQuickenedInfoOf doesn't show up in simpleperf during app startup
Test: test.py, run-libcore-tests, run-jdwp-tests
Test: 628-vdex
Change-Id: I15c3151feb58980a4c4d7469ca02728e94d36c07
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index e4dd544..fd7ae9f 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -429,6 +429,10 @@
// 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/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index 99c6258..3e7a7cd 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -2596,20 +2596,15 @@
class OatWriter::WriteQuickeningIndicesMethodVisitor {
public:
WriteQuickeningIndicesMethodVisitor(OutputStream* out,
- uint32_t indices_offset,
- const SafeMap<const uint8_t*, uint32_t>& offset_map,
- std::vector<uint32_t>* dex_files_offset)
+ uint32_t quickening_info_bytes,
+ const SafeMap<const uint8_t*, uint32_t>& offset_map)
: out_(out),
- indices_offset_(indices_offset),
+ quickening_info_bytes_(quickening_info_bytes),
written_bytes_(0u),
- dex_files_offset_(dex_files_offset),
offset_map_(offset_map) {}
bool VisitDexMethods(const std::vector<const DexFile*>& dex_files, const CompilerDriver& driver) {
for (const DexFile* dex_file : dex_files) {
- // Record the offset for this current dex file. It will be written in the vdex file
- // later.
- dex_files_offset_->push_back(indices_offset_ + GetNumberOfWrittenBytes());
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);
@@ -2620,23 +2615,38 @@
for (ClassDataItemIterator class_it(*dex_file, class_data);
class_it.HasNext();
class_it.Next()) {
- if (!class_it.IsAtMethod()) {
+ 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));
- if (HasQuickeningInfo(compiled_method)) {
- uint32_t code_item_offset = class_it.GetMethodCodeItemOffset();
- uint32_t offset = offset_map_.Get(compiled_method->GetVmapTable().data());
- if (!out_->WriteFully(&code_item_offset, sizeof(code_item_offset)) ||
- !out_->WriteFully(&offset, sizeof(offset))) {
+ const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem();
+ uint32_t existing_debug_info_offset = OatFile::GetDebugInfoOffset(*dex_file, code_item);
+ // 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(code_item_offset) + sizeof(offset);
+ written_bytes_ += sizeof(existing_debug_info_offset) + sizeof(quickening_offset);
}
}
}
@@ -2650,9 +2660,8 @@
private:
OutputStream* const out_;
- const uint32_t indices_offset_;
+ const uint32_t quickening_info_bytes_;
size_t written_bytes_;
- std::vector<uint32_t>* dex_files_offset_;
// Maps quickening map to its offset in the file.
const SafeMap<const uint8_t*, uint32_t>& offset_map_;
};
@@ -2682,30 +2691,27 @@
return false;
}
- WriteQuickeningIndicesMethodVisitor visitor2(vdex_out,
- visitor1.GetNumberOfWrittenBytes(),
- offset_map,
- &dex_files_indices);
- if (!visitor2.VisitDexMethods(*dex_files_, *compiler_driver_)) {
- 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();
+ return false;
+ }
- DCHECK_EQ(dex_files_->size(), dex_files_indices.size());
- if (!vdex_out->WriteFully(
- dex_files_indices.data(), sizeof(dex_files_indices[0]) * dex_files_indices.size())) {
- PLOG(ERROR) << "Failed to write the vdex quickening info. File: " << vdex_out->GetLocation();
- return false;
+ if (!vdex_out->Flush()) {
+ PLOG(ERROR) << "Failed to flush stream after writing quickening info."
+ << " File: " << vdex_out->GetLocation();
+ return false;
+ }
+ size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() +
+ visitor2.GetNumberOfWrittenBytes();
+ } else {
+ // We know we did not quicken.
+ size_quickening_info_ = 0;
}
-
- if (!vdex_out->Flush()) {
- PLOG(ERROR) << "Failed to flush stream after writing quickening info."
- << " File: " << vdex_out->GetLocation();
- return false;
- }
- size_quickening_info_ = visitor1.GetNumberOfWrittenBytes() +
- visitor2.GetNumberOfWrittenBytes() +
- dex_files_->size() * sizeof(uint32_t);
} else {
// We know we did not quicken.
size_quickening_info_ = 0;
diff --git a/openjdkjvmti/fixed_up_dex_file.cc b/openjdkjvmti/fixed_up_dex_file.cc
index c498869..64dc3a5 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -60,7 +60,8 @@
if (vdex == nullptr) {
return;
}
- vdex->FullyUnquickenDexFile(new_dex_file, original_dex_file);
+ art::VdexFile::UnquickenDexFile(
+ new_dex_file, vdex->GetQuickeningInfo(), /* decompile_return_instruction */true);
}
std::unique_ptr<FixedUpDexFile> FixedUpDexFile::Create(const art::DexFile& original) {
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index 2166ed1..944a308 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -313,6 +313,11 @@
return *Instruction::At(insns_ + dex_pc);
}
+ // Used when quickening / unquickening.
+ void SetDebugInfoOffset(uint32_t new_offset) {
+ debug_info_off_ = new_offset;
+ }
+
uint16_t registers_size_; // the number of registers used by this code
// (locals + parameters)
uint16_t ins_size_; // the number of words of incoming arguments to the method
@@ -322,7 +327,13 @@
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.
- uint32_t debug_info_off_; // file offset to debug info stream
+ // 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 insns_size_in_code_units_; // size of the insns array, in 2 byte code units
uint16_t insns_[1]; // actual array of bytecode.
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 5f54d5d..c82df71 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1499,12 +1499,23 @@
}
}
-uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file ATTRIBUTE_UNUSED,
+uint32_t OatFile::GetDebugInfoOffset(const DexFile& dex_file,
const DexFile::CodeItem* code_item) {
if (code_item == nullptr) {
return 0;
}
- return code_item->debug_info_off_;
+ const uint32_t debug_info_off = code_item->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.
+ 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,
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 955098d..fb9d24f 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -171,56 +171,8 @@
return true;
}
-// Utility class to easily iterate over the quickening data.
-class QuickeningInfoIterator {
- public:
- QuickeningInfoIterator(uint32_t dex_file_index,
- uint32_t number_of_dex_files,
- const ArrayRef<const uint8_t>& quickening_info)
- : quickening_info_(quickening_info) {
- const unaligned_uint32_t* dex_file_indices = reinterpret_cast<const unaligned_uint32_t*>(
- quickening_info.data() +
- quickening_info.size() -
- number_of_dex_files * sizeof(uint32_t));
- current_code_item_end_ = (dex_file_index == number_of_dex_files - 1)
- ? dex_file_indices
- : reinterpret_cast<const unaligned_uint32_t*>(
- quickening_info_.data() + dex_file_indices[dex_file_index + 1]);
- current_code_item_ptr_ = reinterpret_cast<const uint32_t*>(
- quickening_info_.data() + dex_file_indices[dex_file_index]);
- }
-
- bool Done() const {
- return current_code_item_ptr_ == current_code_item_end_;
- }
-
- void Advance() {
- current_code_item_ptr_ += 2;
- }
-
- uint32_t GetCurrentCodeItemOffset() const {
- return current_code_item_ptr_[0];
- }
-
- const ArrayRef<const uint8_t> GetCurrentQuickeningInfo() const {
- return ArrayRef<const uint8_t>(
- // Add sizeof(uint32_t) to remove the length from the data pointer.
- quickening_info_.data() + current_code_item_ptr_[1] + sizeof(uint32_t),
- *reinterpret_cast<const unaligned_uint32_t*>(
- quickening_info_.data() + current_code_item_ptr_[1]));
- }
-
- private:
- typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
- const ArrayRef<const uint8_t>& quickening_info_;
- const unaligned_uint32_t* current_code_item_ptr_;
- const unaligned_uint32_t* current_code_item_end_;
-
- DISALLOW_COPY_AND_ASSIGN(QuickeningInfoIterator);
-};
-
void VdexFile::Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info,
+ 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
@@ -228,77 +180,60 @@
return;
}
- // When we do not decompile RETURN_VOID_NO_BARRIER use the faster
- // QuickeningInfoIterator, otherwise use the slower ClassDataItemIterator
- if (!decompile_return_instruction) {
- for (uint32_t i = 0; i < dex_files.size(); ++i) {
- for (QuickeningInfoIterator it(i, dex_files.size(), quickening_info);
- !it.Done();
- it.Advance()) {
- optimizer::ArtDecompileDEX(
- *dex_files[i]->GetCodeItem(it.GetCurrentCodeItemOffset()),
- it.GetCurrentQuickeningInfo(),
- decompile_return_instruction);
- }
- }
- } else {
- for (uint32_t i = 0; i < dex_files.size(); ++i) {
- QuickeningInfoIterator quick_it(i, dex_files.size(), quickening_info);
- for (uint32_t j = 0; j < dex_files[i]->NumClassDefs(); ++j) {
- const DexFile::ClassDef& class_def = dex_files[i]->GetClassDef(j);
- const uint8_t* class_data = dex_files[i]->GetClassData(class_def);
- if (class_data != nullptr) {
- for (ClassDataItemIterator class_it(*dex_files[i], class_data);
- class_it.HasNext();
- class_it.Next()) {
- if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
- uint32_t offset = class_it.GetMethodCodeItemOffset();
- if (!quick_it.Done() && offset == quick_it.GetCurrentCodeItemOffset()) {
- optimizer::ArtDecompileDEX(
- *class_it.GetMethodCodeItem(),
- quick_it.GetCurrentQuickeningInfo(),
- decompile_return_instruction);
- quick_it.Advance();
- } else {
- optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(),
- /* quickened_info */ {},
- decompile_return_instruction);
- }
- }
- }
- }
- }
- DCHECK(quick_it.Done()) << "Failed to use all quickening info";
- }
+ for (uint32_t i = 0; i < dex_files.size(); ++i) {
+ UnquickenDexFile(*dex_files[i], quickening_info, decompile_return_instruction);
}
}
-static constexpr uint32_t kNoDexFile = -1;
+typedef __attribute__((__aligned__(1))) uint32_t unaligned_uint32_t;
-uint32_t VdexFile::GetDexFileIndex(const DexFile& dex_file) const {
- uint32_t dex_index = 0;
- for (const uint8_t* dex_file_start = GetNextDexFileData(nullptr);
- dex_file_start != dex_file.Begin();
- dex_file_start = GetNextDexFileData(dex_file_start)) {
- if (dex_file_start == nullptr) {
- return kNoDexFile;
- }
- dex_index++;
+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;
}
- return dex_index;
+ uint32_t quickening_offset = offset_in_code_item - dex_file.Size();
+ return *reinterpret_cast<const unaligned_uint32_t*>(quickening_info.data() + quickening_offset);
}
-void VdexFile::FullyUnquickenDexFile(const DexFile& target_dex_file,
- const DexFile& original_dex_file) const {
- uint32_t dex_index = GetDexFileIndex(original_dex_file);
- if (dex_index == kNoDexFile) {
+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();
+
+ // 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));
+}
+
+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));
+}
+
+void VdexFile::UnquickenDexFile(const DexFile& target_dex_file,
+ 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;
}
-
- constexpr bool kDecompileReturnInstruction = true;
- QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo());
- // Iterate over the class definitions. Even if there is no quickening info,
- // we want to unquicken RETURN_VOID_NO_BARRIER instruction.
for (uint32_t i = 0; i < target_dex_file.NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = target_dex_file.GetClassDef(i);
const uint8_t* class_data = target_dex_file.GetClassData(class_def);
@@ -307,44 +242,45 @@
class_it.HasNext();
class_it.Next()) {
if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
- uint32_t offset = class_it.GetMethodCodeItemOffset();
- if (!it.Done() && offset == it.GetCurrentCodeItemOffset()) {
- optimizer::ArtDecompileDEX(
- *class_it.GetMethodCodeItem(),
- it.GetCurrentQuickeningInfo(),
- kDecompileReturnInstruction);
- it.Advance();
- } else {
- optimizer::ArtDecompileDEX(*class_it.GetMethodCodeItem(),
- ArrayRef<const uint8_t>(nullptr, 0),
- kDecompileReturnInstruction);
+ 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));
}
+ optimizer::ArtDecompileDEX(
+ *code_item,
+ GetQuickeningInfoAt(quickening_info, quickening_offset),
+ decompile_return_instruction);
}
}
}
}
}
+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 {
- if (GetQuickeningInfo().size() == 0) {
- // Bail early if there is no quickening info.
- return nullptr;
- }
+ 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);
- uint32_t dex_index = GetDexFileIndex(dex_file);
- if (dex_index == kNoDexFile) {
- return nullptr;
- }
+ return GetQuickeningInfoAt(quickening_info, quickening_offset).data();
+}
- for (QuickeningInfoIterator it(dex_index, GetHeader().GetNumberOfDexFiles(), GetQuickeningInfo());
- !it.Done();
- it.Advance()) {
- if (code_item_offset == it.GetCurrentCodeItemOffset()) {
- return it.GetCurrentQuickeningInfo().data();
- }
- }
- return nullptr;
+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);
}
} // namespace art
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 11f1f52..3e08826 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -39,14 +39,14 @@
// DEX[1] the bytecode may have been quickened
// ...
// DEX[D]
+// VerifierDeps
+// uint8[D][] verification dependencies
// QuickeningInfo
-// uint8[] quickening data
-// unaligned_uint32_t[2][] table of offsets pair:
-// uint32_t[0] contains code_item_offset
-// uint32_t[1] contains quickening data offset from the start
+// 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
-// unalgined_uint32_t[D] start offsets (from the start of QuickeningInfo) in previous
-// table for each dex file
class VdexFile {
public:
@@ -72,8 +72,8 @@
private:
static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
- // Last update: Use set for unverified_classes_.
- static constexpr uint8_t kVdexVersion[] = { '0', '1', '0', '\0' };
+ // Last update: Lookup-friendly encoding for quickening info.
+ static constexpr uint8_t kVdexVersion[] = { '0', '1', '1', '\0' };
uint8_t magic_[4];
uint8_t version_[4];
@@ -149,17 +149,23 @@
// decompiled to RETURN_VOID instructions using the slower ClassDataItemIterator
// instead of the faster QuickeningInfoIterator.
static void Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info,
+ ArrayRef<const uint8_t> quickening_info,
bool decompile_return_instruction);
- // Fully unquicken `target_dex_file` based on quickening info stored
- // in this vdex file for `original_dex_file`.
- void FullyUnquickenDexFile(const DexFile& target_dex_file,
- const DexFile& original_dex_file) 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);
// 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;
+
private:
bool HasDexSection() const {
return GetHeader().GetDexSize() != 0;