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;