Fix unquickening logic for quickened input dexes
In the case where an input dex is already quickened (i.e.
obfuscation), do not attempt to unquicken it. This is done by
switching the quicken info table to the compact offset table (used by
cdex debug infos).
Added test.
Posssible to get a bit of extra savings by deleting the quicken info
num entries field.
Quicken filter size regression average on golem: -0.14%.
Bug: 72608794
Bug: 63756964
Test: test-art-host-gtest-dex2oat_test -j64
Change-Id: I5534a7509b4c718a9b959fa43b82bde857e0b59e
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index 9f0aaa4..0caf1b1 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -341,10 +341,8 @@
DCHECK_EQ(quicken_index_, existing_quicken_info_.NumIndices());
}
- if (GetQuickenedInfo().empty()) {
- // No need to create a CompiledMethod if there are no quickened opcodes.
- return std::vector<uint8_t>();
- }
+ // Even if there are no indicies, generate an empty quicken info so that we know the method was
+ // quickened.
std::vector<uint8_t> quicken_data;
if (kIsDebugBuild) {
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index 6d3658f..b16c56a 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -997,7 +997,7 @@
if (class_it.IsAtMethod() && class_it.GetMethodCodeItem() != nullptr) {
for (const DexInstructionPcPair& inst :
CodeItemInstructionAccessor(*dex_file, class_it.GetMethodCodeItem())) {
- ASSERT_FALSE(inst->IsQuickened()) << output_;
+ ASSERT_FALSE(inst->IsQuickened()) << inst->Opcode() << " " << output_;
}
}
}
@@ -1871,4 +1871,91 @@
EXPECT_TRUE(found_fast_verify) << "Expected to find " << kFastVerifyString << "\n" << output_;
}
+// Test that dex files with quickened opcodes aren't dequickened.
+TEST_F(Dex2oatTest, QuickenedInput) {
+ std::string error_msg;
+ ScratchFile temp_dex;
+ MutateDexFile(temp_dex.GetFile(), GetTestDexFileName("ManyMethods"), [] (DexFile* dex) {
+ bool mutated_successfully = false;
+ // Change the dex instructions to make an opcode that spans past the end of the code item.
+ for (size_t i = 0; i < dex->NumClassDefs(); ++i) {
+ const DexFile::ClassDef& def = dex->GetClassDef(i);
+ const uint8_t* data = dex->GetClassData(def);
+ if (data == nullptr) {
+ continue;
+ }
+ ClassDataItemIterator it(*dex, data);
+ it.SkipAllFields();
+ while (it.HasNextMethod()) {
+ DexFile::CodeItem* item = const_cast<DexFile::CodeItem*>(it.GetMethodCodeItem());
+ if (item != nullptr) {
+ CodeItemInstructionAccessor instructions(*dex, item);
+ // Make a quickened instruction that doesn't run past the end of the code item.
+ if (instructions.InsnsSizeInCodeUnits() > 2) {
+ const_cast<Instruction&>(instructions.InstructionAt(0)).SetOpcode(
+ Instruction::IGET_BYTE_QUICK);
+ mutated_successfully = true;
+ }
+ }
+ it.Next();
+ }
+ }
+ CHECK(mutated_successfully)
+ << "Failed to find candidate code item with only one code unit in last instruction.";
+ });
+
+ std::string dex_location = temp_dex.GetFilename();
+ std::string odex_location = GetOdexDir() + "/quickened.odex";
+ std::string vdex_location = GetOdexDir() + "/quickened.vdex";
+ std::unique_ptr<File> vdex_output(OS::CreateEmptyFile(vdex_location.c_str()));
+ // Quicken the dex
+ {
+ std::string input_vdex = "--input-vdex-fd=-1";
+ std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_output->Fd());
+ GenerateOdexForTest(dex_location,
+ odex_location,
+ CompilerFilter::kQuicken,
+ // Disable cdex since we want to compare against the original dex file
+ // after unquickening.
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success */ true,
+ /* use_fd */ true);
+ }
+ // Unquicken by running the verify compiler filter on the vdex file and verify it matches.
+ std::string odex_location2 = GetOdexDir() + "/unquickened.odex";
+ std::string vdex_location2 = GetOdexDir() + "/unquickened.vdex";
+ std::unique_ptr<File> vdex_unquickened(OS::CreateEmptyFile(vdex_location2.c_str()));
+ {
+ std::string input_vdex = StringPrintf("--input-vdex-fd=%d", vdex_output->Fd());
+ std::string output_vdex = StringPrintf("--output-vdex-fd=%d", vdex_unquickened->Fd());
+ GenerateOdexForTest(dex_location,
+ odex_location2,
+ CompilerFilter::kVerify,
+ // Disable cdex to avoid needing to write out the shared section.
+ { input_vdex, output_vdex, kDisableCompactDex },
+ /* expect_success */ true,
+ /* use_fd */ true);
+ }
+ ASSERT_EQ(vdex_unquickened->Flush(), 0) << "Could not flush and close vdex file";
+ ASSERT_TRUE(success_);
+ {
+ // Check that hte vdex has one dex and compare it to the original one.
+ std::unique_ptr<VdexFile> vdex(VdexFile::Open(vdex_location2.c_str(),
+ /*writable*/ false,
+ /*low_4gb*/ false,
+ /*unquicken*/ false,
+ &error_msg));
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ bool result = vdex->OpenAllDexFiles(&dex_files, &error_msg);
+ ASSERT_TRUE(result) << error_msg;
+ ASSERT_EQ(dex_files.size(), 1u) << error_msg;
+ ScratchFile temp;
+ ASSERT_TRUE(temp.GetFile()->WriteFully(dex_files[0]->Begin(), dex_files[0]->Size()));
+ ASSERT_EQ(temp.GetFile()->Flush(), 0) << "Could not flush extracted dex";
+ EXPECT_EQ(temp.GetFile()->Compare(temp_dex.GetFile()), 0);
+ }
+ ASSERT_EQ(vdex_output->FlushCloseOrErase(), 0) << "Could not flush and close";
+ ASSERT_EQ(vdex_unquickened->FlushCloseOrErase(), 0) << "Could not flush and close";
+}
+
} // namespace art
diff --git a/dex2oat/linker/oat_writer.cc b/dex2oat/linker/oat_writer.cc
index ee872db..ff0729f 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -2631,45 +2631,37 @@
out_(out) {}
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());
- }
+ // Map of offsets for quicken info related to method indices.
+ SafeMap<const uint8_t*, uint32_t> offset_map;
+ // Use method index order to minimize the encoded size of the offset table.
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.
+ uint32_t offset = 0u;
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();
- }
+ ArrayRef<const uint8_t> 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_);
+ // Record each index if required. written_bytes_ is the offset from the start of the
+ // quicken info data.
+ // May be already inserted for deduplicate items.
+ // Add offset of one to make sure 0 represents unused.
+ auto pair = offset_map.emplace(map.data(), written_bytes_ + 1);
+ offset = pair.first->second;
+ // Write out the map if it's not already written.
+ if (pair.second) {
+ const uint32_t length = map.size() * sizeof(map.front());
+ if (!out_->WriteFully(map.data(), length)) {
+ PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod()
+ << " to " << out_->GetLocation();
+ return false;
+ }
+ written_bytes_ += length;
+ }
}
-
- if (!out_->WriteFully(map.data(), length)) {
- PLOG(ERROR) << "Failed to write quickening info for " << method_ref.PrettyMethod()
- << " to " << out_->GetLocation();
- return false;
- }
- written_bytes_ += length;
+ offsets->push_back(offset);
}
}
return true;
@@ -2683,12 +2675,10 @@
return quicken_info_offset_indices_;
}
-
private:
OatWriter* const writer_;
OutputStream* const out_;
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_;
};
@@ -2712,14 +2702,11 @@
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());
+ CHECK_ALIGNED_PARAM(current_offset, CompactOffsetTable::kAlignment);
// Generate and write the data.
std::vector<uint8_t> table_data;
- QuickenInfoOffsetTableAccessor::Builder builder(&table_data);
- for (uint32_t offset : *offsets) {
- builder.AddOffset(offset);
- }
+ CompactOffsetTable::Build(*offsets, &table_data);
// 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.
@@ -2780,7 +2767,7 @@
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());
+ current_offset = RoundUp(current_offset, CompactOffsetTable::kAlignment);
const size_t extra_bytes = current_offset - before_offset;
quicken_info_offset += extra_bytes;
actual_offset = vdex_out->Seek(current_offset, kSeekSet);
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index d9a93dd..981f901 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -321,31 +321,6 @@
return true;
}
- template <typename Mutator>
- bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) {
- std::vector<std::unique_ptr<const DexFile>> dex_files;
- std::string error_msg;
- const ArtDexFileLoader dex_file_loader;
- CHECK(dex_file_loader.Open(input_jar.c_str(),
- input_jar.c_str(),
- /*verify*/ true,
- /*verify_checksum*/ true,
- &error_msg,
- &dex_files)) << error_msg;
- EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported";
- for (const std::unique_ptr<const DexFile>& dex : dex_files) {
- CHECK(dex->EnableWrite()) << "Failed to enable write";
- mutator(const_cast<DexFile*>(dex.get()));
- if (!output_dex->WriteFully(dex->Begin(), dex->Size())) {
- return false;
- }
- }
- if (output_dex->Flush() != 0) {
- PLOG(FATAL) << "Could not flush the output file.";
- }
- return true;
- }
-
// Create a profile with some subset of methods and classes.
void CreateProfile(const std::string& input_dex,
const std::string& out_profile,
diff --git a/libdexfile/dex/compact_offset_table.cc b/libdexfile/dex/compact_offset_table.cc
index 8cee0e3..60a7b61 100644
--- a/libdexfile/dex/compact_offset_table.cc
+++ b/libdexfile/dex/compact_offset_table.cc
@@ -30,6 +30,11 @@
minimum_offset_(minimum_offset),
data_begin_(data_begin) {}
+CompactOffsetTable::Accessor::Accessor(const uint8_t* data_begin)
+ : Accessor(data_begin + 2 * sizeof(uint32_t),
+ reinterpret_cast<const uint32_t*>(data_begin)[0],
+ reinterpret_cast<const uint32_t*>(data_begin)[1]) {}
+
uint32_t CompactOffsetTable::Accessor::GetOffset(uint32_t index) const {
const uint32_t offset = table_[index / kElementsPerIndex];
const size_t bit_index = index % kElementsPerIndex;
@@ -56,6 +61,17 @@
}
void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets,
+ std::vector<uint8_t>* out_data) {
+ static constexpr size_t kNumOffsets = 2;
+ uint32_t out_offsets[kNumOffsets] = {};
+ CompactOffsetTable::Build(offsets, out_data, &out_offsets[0], &out_offsets[1]);
+ // Write the offsets at the start of the debug info.
+ out_data->insert(out_data->begin(),
+ reinterpret_cast<const uint8_t*>(&out_offsets[0]),
+ reinterpret_cast<const uint8_t*>(&out_offsets[kNumOffsets]));
+}
+
+void CompactOffsetTable::Build(const std::vector<uint32_t>& offsets,
std::vector<uint8_t>* out_data,
uint32_t* out_min_offset,
uint32_t* out_table_offset) {
diff --git a/libdexfile/dex/compact_offset_table.h b/libdexfile/dex/compact_offset_table.h
index 17e6bb4..ec759e2 100644
--- a/libdexfile/dex/compact_offset_table.h
+++ b/libdexfile/dex/compact_offset_table.h
@@ -37,9 +37,10 @@
class Accessor {
public:
- Accessor(const uint8_t* data_begin,
- uint32_t minimum_offset,
- uint32_t table_offset);
+ // Read the minimum and table offsets from the data pointer.
+ explicit Accessor(const uint8_t* data_begin);
+
+ Accessor(const uint8_t* data_begin, uint32_t minimum_offset, uint32_t table_offset);
// Return the offset for the index.
uint32_t GetOffset(uint32_t index) const;
@@ -50,6 +51,9 @@
const uint8_t* const data_begin_;
};
+ // Version that also serializes the min offset and table offset.
+ static void Build(const std::vector<uint32_t>& offsets, std::vector<uint8_t>* out_data);
+
// Returned offsets are all relative to out_min_offset.
static void Build(const std::vector<uint32_t>& offsets,
std::vector<uint8_t>* out_data,
diff --git a/libdexfile/dex/compact_offset_table_test.cc b/libdexfile/dex/compact_offset_table_test.cc
index 7eb0156..724978d 100644
--- a/libdexfile/dex/compact_offset_table_test.cc
+++ b/libdexfile/dex/compact_offset_table_test.cc
@@ -74,6 +74,16 @@
<< " table size " << data.size()
<< " sorted table size " << sorted_data.size();
}
+
+ // Test constructor and accessor that serialize/read offsets.
+ {
+ std::vector<uint8_t> data2;
+ CompactOffsetTable::Build(offsets, /*out*/ &data2);
+ CompactOffsetTable::Accessor accessor2(&data2[0]);
+ for (size_t i = 0; i < offsets.size(); ++i) {
+ EXPECT_EQ(offsets[i], accessor2.GetOffset(i));
+ }
+ }
}
} // namespace art
diff --git a/runtime/base/unix_file/fd_file.cc b/runtime/base/unix_file/fd_file.cc
index 37f239d..f9da178 100644
--- a/runtime/base/unix_file/fd_file.cc
+++ b/runtime/base/unix_file/fd_file.cc
@@ -459,12 +459,13 @@
static const size_t kBufferSize = 4096;
std::unique_ptr<uint8_t[]> buffer1(new uint8_t[kBufferSize]);
std::unique_ptr<uint8_t[]> buffer2(new uint8_t[kBufferSize]);
+ size_t offset = 0;
while (length > 0) {
size_t len = std::min(kBufferSize, static_cast<size_t>(length));
- if (!ReadFully(&buffer1[0], len)) {
+ if (!PreadFully(&buffer1[0], len, offset)) {
return -1;
}
- if (!other->ReadFully(&buffer2[0], len)) {
+ if (!other->PreadFully(&buffer2[0], len, offset)) {
return 1;
}
int result = memcmp(&buffer1[0], &buffer2[0], len);
@@ -472,6 +473,7 @@
return result;
}
length -= len;
+ offset += len;
}
return 0;
}
diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h
index b2b4d54..85b0dbb 100644
--- a/runtime/common_runtime_test.h
+++ b/runtime/common_runtime_test.h
@@ -26,6 +26,8 @@
#include "arch/instruction_set.h"
#include "base/mutex.h"
+#include "base/unix_file/fd_file.h"
+#include "dex/art_dex_file_loader.h"
#include "dex/compact_dex_level.h"
#include "globals.h"
// TODO: Add inl file and avoid including inl.
@@ -119,6 +121,32 @@
// A helper to set up a small heap (4M) to make FillHeap faster.
static void SetUpRuntimeOptionsForFillHeap(RuntimeOptions *options);
+ template <typename Mutator>
+ bool MutateDexFile(File* output_dex, const std::string& input_jar, const Mutator& mutator) {
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ std::string error_msg;
+ const ArtDexFileLoader dex_file_loader;
+ CHECK(dex_file_loader.Open(input_jar.c_str(),
+ input_jar.c_str(),
+ /*verify*/ true,
+ /*verify_checksum*/ true,
+ &error_msg,
+ &dex_files)) << error_msg;
+ EXPECT_EQ(dex_files.size(), 1u) << "Only one input dex is supported";
+ const std::unique_ptr<const DexFile>& dex = dex_files[0];
+ CHECK(dex->EnableWrite()) << "Failed to enable write";
+ DexFile* dex_file = const_cast<DexFile*>(dex.get());
+ mutator(dex_file);
+ const_cast<DexFile::Header&>(dex_file->GetHeader()).checksum_ = dex_file->CalculateChecksum();
+ if (!output_dex->WriteFully(dex->Begin(), dex->Size())) {
+ return false;
+ }
+ if (output_dex->Flush() != 0) {
+ PLOG(FATAL) << "Could not flush the output file.";
+ }
+ return true;
+ }
+
protected:
// Allow subclases such as CommonCompilerTest to add extra options.
virtual void SetUpRuntimeOptions(RuntimeOptions* options ATTRIBUTE_UNUSED) {}
diff --git a/runtime/quicken_info.h b/runtime/quicken_info.h
index 32f7005..f20aa0c 100644
--- a/runtime/quicken_info.h
+++ b/runtime/quicken_info.h
@@ -18,66 +18,12 @@
#define ART_RUNTIME_QUICKEN_INFO_H_
#include "base/array_ref.h"
+#include "dex/compact_offset_table.h"
#include "dex/dex_instruction.h"
#include "leb128.h"
namespace art {
-// 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;
- }
-
- QuickenInfoOffsetTableAccessor(const ArrayRef<const uint8_t>& data, uint32_t max_index)
- : table_(ArrayRef<const TableType>::Cast(data).SubArray(
- 0,
- RoundUp(max_index, kElementsPerIndex) / kElementsPerIndex)) {}
-
- size_t SizeInBytes() const {
- return NumIndices() * sizeof(table_[0]);
- }
-
- uint32_t NumIndices() const {
- return table_.size();
- }
-
- // 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];
- }
-
- static uint32_t Alignment() {
- return alignof(TableType);
- }
-
- private:
- const ArrayRef<const TableType> table_;
-};
-
// QuickenInfoTable is a table of 16 bit dex indices. There is one slot for every instruction that
// is possibly dequickenable.
class QuickenInfoTable {
diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc
index 443c35f..34b9fcc 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -222,41 +222,27 @@
return reinterpret_cast<const QuickeningTableOffsetType*>(source_dex_begin)[-1];
}
-QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable(
+CompactOffsetTable::Accessor 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);
- return QuickenInfoOffsetTableAccessor(quickening_info.SubArray(offset), num_method_ids);
+ return CompactOffsetTable::Accessor(quickening_info.SubArray(offset).data());
}
-QuickenInfoOffsetTableAccessor VdexFile::GetQuickenInfoOffsetTable(
+CompactOffsetTable::Accessor VdexFile::GetQuickenInfoOffsetTable(
const DexFile& dex_file,
const ArrayRef<const uint8_t>& quickening_info) const {
- return GetQuickenInfoOffsetTable(dex_file.Begin(), dex_file.NumMethodIds(), quickening_info);
+ return GetQuickenInfoOffsetTable(dex_file.Begin(), quickening_info);
}
static ArrayRef<const uint8_t> GetQuickeningInfoAt(const ArrayRef<const uint8_t>& quickening_info,
uint32_t quickening_offset) {
- ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset);
+ // Subtract offset of one since 0 represents unused and cannot be in the table.
+ ArrayRef<const uint8_t> remaining = quickening_info.SubArray(quickening_offset - 1);
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 {
@@ -267,13 +253,15 @@
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.
+ if (quickening_info.empty()) {
+ // Bail early if there is no quickening info and no need to decompile. This means there is also
+ // no RETURN_VOID to decompile since the empty table takes a non zero amount of space.
return;
}
// Make sure to not unquicken the same code item multiple times.
std::unordered_set<const DexFile::CodeItem*> unquickened_code_item;
+ CompactOffsetTable::Accessor accessor(GetQuickenInfoOffsetTable(source_dex_begin,
+ quickening_info));
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);
@@ -284,21 +272,16 @@
if (class_it.IsAtMethod()) {
const DexFile::CodeItem* code_item = class_it.GetMethodCodeItem();
if (code_item != nullptr && unquickened_code_item.emplace(code_item).second) {
- 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);
+ const uint32_t offset = accessor.GetOffset(class_it.GetMemberIndex());
+ // Offset being 0 means not quickened.
+ if (offset != 0u) {
+ ArrayRef<const uint8_t> quicken_data = GetQuickeningInfoAt(quickening_info, offset);
+ optimizer::ArtDecompileDEX(
+ target_dex_file,
+ *code_item,
+ quicken_data,
+ decompile_return_instruction);
}
- optimizer::ArtDecompileDEX(
- target_dex_file,
- *code_item,
- quicken_data,
- decompile_return_instruction);
}
}
DexFile::UnHideAccessFlags(class_it);
@@ -313,10 +296,11 @@
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);
+ const uint32_t quickening_offset =
+ GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx);
+ if (quickening_offset == 0u) {
+ return ArrayRef<const uint8_t>();
+ }
return GetQuickeningInfoAt(quickening_info, quickening_offset);
}
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 0f34795..0c7200f 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -22,6 +22,7 @@
#include "base/array_ref.h"
#include "base/macros.h"
+#include "dex/compact_offset_table.h"
#include "mem_map.h"
#include "os.h"
#include "quicken_info.h"
@@ -238,13 +239,12 @@
const uint8_t* source_dex_begin,
bool decompile_return_instruction) const;
- QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable(
+ CompactOffsetTable::Accessor GetQuickenInfoOffsetTable(
const DexFile& dex_file,
const ArrayRef<const uint8_t>& quickening_info) const;
- QuickenInfoOffsetTableAccessor GetQuickenInfoOffsetTable(
+ CompactOffsetTable::Accessor GetQuickenInfoOffsetTable(
const uint8_t* source_dex_begin,
- uint32_t num_method_ids,
const ArrayRef<const uint8_t>& quickening_info) const;
bool ContainsDexFile(const DexFile& dex_file) const;