Revert "Revert "Move quickening info logic to its own table""

Bug: 71605148
Bug: 63756964

Test: test-art-target on angler

This reverts commit 6716941120ae9f47ba1b8ef8e79820c4b5640350.

Change-Id: Ic01ea4e8bb2c1de761fab354c5bbe27290538631
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 23af2ab..83a8656 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 cecd376..a81fa76 100644
--- a/dex2oat/linker/oat_writer.cc
+++ b/dex2oat/linker/oat_writer.cc
@@ -58,6 +58,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"
@@ -2618,42 +2619,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;
   }
 
@@ -2661,71 +2674,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;
   }
@@ -2736,14 +2737,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);
@@ -2754,36 +2757,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;
@@ -3358,9 +3396,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 8132323..df13d1f 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -1187,7 +1187,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 e452e98..afbd446 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -101,7 +101,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 963c6f8..dcc834a 100644
--- a/openjdkjvmti/fixed_up_dex_file.cc
+++ b/openjdkjvmti/fixed_up_dex_file.cc
@@ -63,8 +63,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 f9eedae..db7289a 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -577,14 +577,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 446a004..c03dbcc 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -1535,21 +1535,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 c16cfb6..118cffe 100644
--- a/runtime/vdex_file.cc
+++ b/runtime/vdex_file.cc
@@ -29,6 +29,7 @@
 #include "dex/dex_file.h"
 #include "dex/dex_file_loader.h"
 #include "dex_to_dex_decompiler.h"
+#include "quicken_info.h"
 
 namespace art {
 
@@ -144,9 +145,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;
   }
@@ -159,14 +159,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);
   }
 }
 
@@ -197,64 +198,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.
@@ -269,19 +274,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);
         }
       }
@@ -289,25 +295,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..73190c9 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: Revert^2 compact quicken info tables that don't modify the dex code items.
+    static constexpr uint8_t kVdexVersion[] = { '0', '1', '3', '\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);