Move debug info offsets into a side table

Add a compact side table for figuring out the debug info offsets
for a given method index. This reduces dex size by ~1.2%.

The debug table is keyed by method index and has leb encoded
offsets for the offsets. This means the table is smaller if debug
infos are encoded by method index order.

To prevent expansion for method indicies without debug info, there
is a bitmap that specifies if a method index has a debug info offset.

Motivation: Reduce code item size and allow more deduping in the
future.

Test: test-art-host
Bug: 63756964

Change-Id: Ib983e85c1727f58c97676bde275f4a9756314da0
diff --git a/compiler/debug/elf_debug_info_writer.h b/compiler/debug/elf_debug_info_writer.h
index 713f8eb..893cad2 100644
--- a/compiler/debug/elf_debug_info_writer.h
+++ b/compiler/debug/elf_debug_info_writer.h
@@ -49,7 +49,7 @@
 
 static std::vector<const char*> GetParamNames(const MethodDebugInfo* mi) {
   std::vector<const char*> names;
-  CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item);
+  CodeItemDebugInfoAccessor accessor(*mi->dex_file, mi->code_item, mi->dex_method_index);
   if (accessor.HasCodeItem()) {
     DCHECK(mi->dex_file != nullptr);
     const uint8_t* stream = mi->dex_file->GetDebugInfoStream(accessor.DebugInfoOffset());
@@ -163,7 +163,7 @@
     for (auto mi : compilation_unit.methods) {
       DCHECK(mi->dex_file != nullptr);
       const DexFile* dex = mi->dex_file;
-      CodeItemDebugInfoAccessor accessor(*dex, mi->code_item);
+      CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index);
       const DexFile::MethodId& dex_method = dex->GetMethodId(mi->dex_method_index);
       const DexFile::ProtoId& dex_proto = dex->GetMethodPrototype(dex_method);
       const DexFile::TypeList* dex_params = dex->GetProtoParameters(dex_proto);
diff --git a/compiler/debug/elf_debug_line_writer.h b/compiler/debug/elf_debug_line_writer.h
index 4e37f4e..44504c1 100644
--- a/compiler/debug/elf_debug_line_writer.h
+++ b/compiler/debug/elf_debug_line_writer.h
@@ -159,7 +159,7 @@
       PositionInfos dex2line_map;
       DCHECK(mi->dex_file != nullptr);
       const DexFile* dex = mi->dex_file;
-      CodeItemDebugInfoAccessor accessor(*dex, mi->code_item);
+      CodeItemDebugInfoAccessor accessor(*dex, mi->code_item, mi->dex_method_index);
       const uint32_t debug_info_offset = accessor.DebugInfoOffset();
       if (!dex->DecodeDebugPositionInfo(debug_info_offset, PositionInfoCallback, &dex2line_map)) {
         continue;
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index b2ad8ec..81a7558 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -1660,7 +1660,7 @@
   const DexFile::CodeItem* code_item = resolved_method->GetCodeItem();
   const DexFile& callee_dex_file = *resolved_method->GetDexFile();
   uint32_t method_index = resolved_method->GetDexMethodIndex();
-  CodeItemDebugInfoAccessor code_item_accessor(callee_dex_file, code_item);
+  CodeItemDebugInfoAccessor code_item_accessor(resolved_method);
   ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
   Handle<mirror::DexCache> dex_cache = NewHandleIfDifferent(resolved_method->GetDexCache(),
                                                             caller_compilation_unit_.GetDexCache(),
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 8966d56..a3b1f0c 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -772,7 +772,7 @@
     return nullptr;
   }
 
-  CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item);
+  CodeItemDebugInfoAccessor code_item_accessor(dex_file, code_item, method_idx);
   HGraph* graph = new (allocator) HGraph(
       allocator,
       arena_stack,
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index df13d1f..1518e1d 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);
+  CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx);
 
   fprintf(gOutFile, "      registers     : %d\n", accessor.RegistersSize());
   fprintf(gOutFile, "      ins           : %d\n", accessor.InsSize());
diff --git a/dexlayout/compact_dex_writer.cc b/dexlayout/compact_dex_writer.cc
index 1c5b16d..d1dc658 100644
--- a/dexlayout/compact_dex_writer.cc
+++ b/dexlayout/compact_dex_writer.cc
@@ -16,10 +16,144 @@
 
 #include "compact_dex_writer.h"
 
+#include "base/logging.h"
+#include "base/time_utils.h"
+#include "dex/compact_dex_debug_info.h"
 #include "dex/compact_dex_file.h"
+#include "dexlayout.h"
 
 namespace art {
 
+uint32_t CompactDexWriter::WriteDebugInfoOffsetTable(uint32_t offset) {
+  const uint32_t start_offset = offset;
+  const dex_ir::Collections& collections = header_->GetCollections();
+  // Debug offsets for method indexes. 0 means no debug info.
+  std::vector<uint32_t> debug_info_offsets(collections.MethodIdsSize(), 0u);
+
+  static constexpr InvokeType invoke_types[] = {
+    kDirect,
+    kVirtual
+  };
+
+  for (InvokeType invoke_type : invoke_types) {
+    for (const std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) {
+      // Skip classes that are not defined in this dex file.
+      dex_ir::ClassData* class_data = class_def->GetClassData();
+      if (class_data == nullptr) {
+        continue;
+      }
+      for (auto& method : *(invoke_type == InvokeType::kDirect
+                                ? class_data->DirectMethods()
+                                : class_data->VirtualMethods())) {
+        const dex_ir::MethodId* method_id = method->GetMethodId();
+        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
+          const uint32_t debug_info_offset = code_item->DebugInfo()->GetOffset();
+          const uint32_t method_idx = method_id->GetIndex();
+          if (debug_info_offsets[method_idx] != 0u) {
+            CHECK_EQ(debug_info_offset, debug_info_offsets[method_idx]);
+          }
+          debug_info_offsets[method_idx] = debug_info_offset;
+        }
+      }
+    }
+  }
+
+  std::vector<uint8_t> data;
+  debug_info_base_ = 0u;
+  debug_info_offsets_table_offset_ = 0u;
+  CompactDexDebugInfoOffsetTable::Build(debug_info_offsets,
+                                        &data,
+                                        &debug_info_base_,
+                                        &debug_info_offsets_table_offset_);
+  // Align the table and write it out.
+  offset = RoundUp(offset, CompactDexDebugInfoOffsetTable::kAlignment);
+  debug_info_offsets_pos_ = offset;
+  offset += Write(data.data(), data.size(), offset);
+
+  // Verify that the whole table decodes as expected and measure average performance.
+  const bool kMeasureAndTestOutput = dex_layout_->GetOptions().verify_output_;
+  if (kMeasureAndTestOutput && !debug_info_offsets.empty()) {
+    uint64_t start_time = NanoTime();
+    CompactDexDebugInfoOffsetTable::Accessor accessor(mem_map_->Begin() + debug_info_offsets_pos_,
+                                                      debug_info_base_,
+                                                      debug_info_offsets_table_offset_);
+
+    for (size_t i = 0; i < debug_info_offsets.size(); ++i) {
+      CHECK_EQ(accessor.GetDebugInfoOffset(i), debug_info_offsets[i]);
+    }
+    uint64_t end_time = NanoTime();
+    VLOG(dex) << "Average lookup time (ns) for debug info offsets: "
+              << (end_time - start_time) / debug_info_offsets.size();
+  }
+
+  return offset - start_offset;
+}
+
+uint32_t CompactDexWriter::WriteCodeItem(dex_ir::CodeItem* code_item,
+                                         uint32_t offset,
+                                         bool reserve_only) {
+  DCHECK(code_item != nullptr);
+  const uint32_t start_offset = offset;
+  offset = RoundUp(offset, CompactDexFile::CodeItem::kAlignment);
+  ProcessOffset(&offset, code_item);
+
+  CompactDexFile::CodeItem disk_code_item;
+  disk_code_item.registers_size_ = code_item->RegistersSize();
+  disk_code_item.ins_size_ = code_item->InsSize();
+  disk_code_item.outs_size_ = code_item->OutsSize();
+  disk_code_item.tries_size_ = code_item->TriesSize();
+  disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize();
+  // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
+  // item.
+  offset += Write(&disk_code_item,
+                  OFFSETOF_MEMBER(CompactDexFile::CodeItem, insns_),
+                  offset);
+  // Write the instructions.
+  offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
+  // Write the post instruction data.
+  offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only);
+  return offset - start_offset;
+}
+
+void CompactDexWriter::SortDebugInfosByMethodIndex() {
+  dex_ir::Collections& collections = header_->GetCollections();
+  static constexpr InvokeType invoke_types[] = {
+    kDirect,
+    kVirtual
+  };
+  std::map<const dex_ir::DebugInfoItem*, uint32_t> method_idx_map;
+  for (InvokeType invoke_type : invoke_types) {
+    for (std::unique_ptr<dex_ir::ClassDef>& class_def : collections.ClassDefs()) {
+      // Skip classes that are not defined in this dex file.
+      dex_ir::ClassData* class_data = class_def->GetClassData();
+      if (class_data == nullptr) {
+        continue;
+      }
+      for (auto& method : *(invoke_type == InvokeType::kDirect
+                                ? class_data->DirectMethods()
+                                : class_data->VirtualMethods())) {
+        const dex_ir::MethodId* method_id = method->GetMethodId();
+        dex_ir::CodeItem* code_item = method->GetCodeItem();
+        if (code_item != nullptr && code_item->DebugInfo() != nullptr) {
+          const dex_ir::DebugInfoItem* debug_item = code_item->DebugInfo();
+          method_idx_map.insert(std::make_pair(debug_item, method_id->GetIndex()));
+        }
+      }
+    }
+  }
+  std::sort(collections.DebugInfoItems().begin(),
+            collections.DebugInfoItems().end(),
+            [&](const std::unique_ptr<dex_ir::DebugInfoItem>& a,
+                const std::unique_ptr<dex_ir::DebugInfoItem>& b) {
+    auto it_a = method_idx_map.find(a.get());
+    auto it_b = method_idx_map.find(b.get());
+    uint32_t idx_a = it_a != method_idx_map.end() ? it_a->second : 0u;
+    uint32_t idx_b = it_b != method_idx_map.end() ? it_b->second : 0u;
+    return idx_a < idx_b;
+  });
+}
+
 void CompactDexWriter::WriteHeader() {
   CompactDexFile::Header header;
   CompactDexFile::WriteMagic(&header.magic_[0]);
@@ -49,6 +183,11 @@
   header.class_defs_off_ = collections.ClassDefsOffset();
   header.data_size_ = header_->DataSize();
   header.data_off_ = header_->DataOffset();
+
+  // Compact dex specific flags.
+  header.debug_info_offsets_pos_ = debug_info_offsets_pos_;
+  header.debug_info_offsets_table_offset_ = debug_info_offsets_table_offset_;
+  header.debug_info_base_ = debug_info_base_;
   header.feature_flags_ = 0u;
   // In cases where apps are converted to cdex during install, maintain feature flags so that
   // the verifier correctly verifies apps that aren't targetting default methods.
@@ -62,4 +201,103 @@
   return sizeof(CompactDexFile::Header);
 }
 
+void CompactDexWriter::WriteMemMap() {
+  // Starting offset is right after the header.
+  uint32_t offset = GetHeaderSize();
+
+  dex_ir::Collections& collection = header_->GetCollections();
+
+  // Based on: https://source.android.com/devices/tech/dalvik/dex-format
+  // Since the offsets may not be calculated already, the writing must be done in the correct order.
+  const uint32_t string_ids_offset = offset;
+  offset += WriteStringIds(offset, /*reserve_only*/ true);
+  offset += WriteTypeIds(offset);
+  const uint32_t proto_ids_offset = offset;
+  offset += WriteProtoIds(offset, /*reserve_only*/ true);
+  offset += WriteFieldIds(offset);
+  offset += WriteMethodIds(offset);
+  const uint32_t class_defs_offset = offset;
+  offset += WriteClassDefs(offset, /*reserve_only*/ true);
+  const uint32_t call_site_ids_offset = offset;
+  offset += WriteCallSiteIds(offset, /*reserve_only*/ true);
+  offset += WriteMethodHandles(offset);
+
+  uint32_t data_offset_ = 0u;
+  if (compute_offsets_) {
+    // Data section.
+    offset = RoundUp(offset, kDataSectionAlignment);
+    data_offset_ = offset;
+  }
+
+  // Write code item first to minimize the space required for encoded methods.
+  // For cdex, the code items don't depend on the debug info.
+  offset += WriteCodeItems(offset, /*reserve_only*/ false);
+
+  // Sort the debug infos by method index order, this reduces size by ~0.1% by reducing the size of
+  // the debug info offset table.
+  SortDebugInfosByMethodIndex();
+  offset += WriteDebugInfoItems(offset);
+
+  offset += WriteEncodedArrays(offset);
+  offset += WriteAnnotations(offset);
+  offset += WriteAnnotationSets(offset);
+  offset += WriteAnnotationSetRefs(offset);
+  offset += WriteAnnotationsDirectories(offset);
+  offset += WriteTypeLists(offset);
+  offset += WriteClassDatas(offset);
+  offset += WriteStringDatas(offset);
+
+  // Write delayed id sections that depend on data sections.
+  WriteStringIds(string_ids_offset, /*reserve_only*/ false);
+  WriteProtoIds(proto_ids_offset, /*reserve_only*/ false);
+  WriteClassDefs(class_defs_offset, /*reserve_only*/ false);
+  WriteCallSiteIds(call_site_ids_offset, /*reserve_only*/ false);
+
+  // Write the map list.
+  if (compute_offsets_) {
+    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeMapList));
+    collection.SetMapListOffset(offset);
+  } else {
+    offset = collection.MapListOffset();
+  }
+  offset += GenerateAndWriteMapItems(offset);
+  offset = RoundUp(offset, kDataSectionAlignment);
+
+  // Map items are included in the data section.
+  if (compute_offsets_) {
+    header_->SetDataSize(offset - data_offset_);
+    if (header_->DataSize() != 0) {
+      // Offset must be zero when the size is zero.
+      header_->SetDataOffset(data_offset_);
+    } else {
+      header_->SetDataOffset(0u);
+    }
+  }
+
+  // Write link data if it exists.
+  const std::vector<uint8_t>& link_data = collection.LinkData();
+  if (link_data.size() > 0) {
+    CHECK_EQ(header_->LinkSize(), static_cast<uint32_t>(link_data.size()));
+    if (compute_offsets_) {
+      header_->SetLinkOffset(offset);
+    }
+    offset += Write(&link_data[0], link_data.size(), header_->LinkOffset());
+  }
+
+  // Write debug info offset table last to make dex file verifier happy.
+  offset += WriteDebugInfoOffsetTable(offset);
+
+  // Write header last.
+  if (compute_offsets_) {
+    header_->SetFileSize(offset);
+  }
+  WriteHeader();
+
+  if (dex_layout_->GetOptions().update_checksum_) {
+    header_->SetChecksum(DexFile::CalculateChecksum(mem_map_->Begin(), offset));
+    // Rewrite the header with the calculated checksum.
+    WriteHeader();
+  }
+}
+
 }  // namespace art
diff --git a/dexlayout/compact_dex_writer.h b/dexlayout/compact_dex_writer.h
index d13333b..37f6ff1 100644
--- a/dexlayout/compact_dex_writer.h
+++ b/dexlayout/compact_dex_writer.h
@@ -33,13 +33,30 @@
         compact_dex_level_(compact_dex_level) {}
 
  protected:
+  void WriteMemMap() OVERRIDE;
+
   void WriteHeader() OVERRIDE;
 
   size_t GetHeaderSize() const OVERRIDE;
 
+  uint32_t WriteDebugInfoOffsetTable(uint32_t offset);
+
   const CompactDexLevel compact_dex_level_;
 
+  uint32_t WriteCodeItem(dex_ir::CodeItem* code_item, uint32_t offset, bool reserve_only) OVERRIDE;
+
+  void SortDebugInfosByMethodIndex();
+
  private:
+  // Position in the compact dex file for the debug info table data starts.
+  uint32_t debug_info_offsets_pos_ = 0u;
+
+  // Offset into the debug info table data where the lookup table is.
+  uint32_t debug_info_offsets_table_offset_ = 0u;
+
+  // Base offset of where debug info starts in the dex file.
+  uint32_t debug_info_base_ = 0u;
+
   DISALLOW_COPY_AND_ASSIGN(CompactDexWriter);
 };
 
diff --git a/dexlayout/dex_ir.cc b/dexlayout/dex_ir.cc
index 2191ea6..0a59cc9 100644
--- a/dexlayout/dex_ir.cc
+++ b/dexlayout/dex_ir.cc
@@ -566,8 +566,10 @@
 }
 
 CodeItem* Collections::CreateCodeItem(const DexFile& dex_file,
-                                      const DexFile::CodeItem& disk_code_item, uint32_t offset) {
-  CodeItemDebugInfoAccessor accessor(dex_file, &disk_code_item);
+                                      const DexFile::CodeItem& disk_code_item,
+                                      uint32_t offset,
+                                      uint32_t dex_method_index) {
+  CodeItemDebugInfoAccessor accessor(dex_file, &disk_code_item, dex_method_index);
   const uint16_t registers_size = accessor.RegistersSize();
   const uint16_t ins_size = accessor.InsSize();
   const uint16_t outs_size = accessor.OutsSize();
@@ -705,7 +707,10 @@
   DebugInfoItem* debug_info = nullptr;
   if (disk_code_item != nullptr) {
     if (code_item == nullptr) {
-      code_item = CreateCodeItem(dex_file, *disk_code_item, cdii.GetMethodCodeItemOffset());
+      code_item = CreateCodeItem(dex_file,
+                                 *disk_code_item,
+                                 cdii.GetMethodCodeItemOffset(),
+                                 cdii.GetMemberIndex());
     }
     debug_info = code_item->DebugInfo();
   }
diff --git a/dexlayout/dex_ir.h b/dexlayout/dex_ir.h
index 6797fa5..ca47b34 100644
--- a/dexlayout/dex_ir.h
+++ b/dexlayout/dex_ir.h
@@ -133,6 +133,7 @@
 
   uint32_t Size() const { return collection_.size(); }
   Vector& Collection() { return collection_; }
+  const Vector& Collection() const { return collection_; }
 
  protected:
   Vector collection_;
@@ -230,6 +231,8 @@
   CollectionVector<CodeItem>::Vector& CodeItems() { return code_items_.Collection(); }
   CollectionVector<ClassData>::Vector& ClassDatas() { return class_datas_.Collection(); }
 
+  const CollectionVector<ClassDef>::Vector& ClassDefs() const { return class_defs_.Collection(); }
+
   void CreateStringId(const DexFile& dex_file, uint32_t i);
   void CreateTypeId(const DexFile& dex_file, uint32_t i);
   void CreateProtoId(const DexFile& dex_file, uint32_t i);
@@ -251,8 +254,10 @@
       const DexFile::AnnotationSetItem* disk_annotations_item, uint32_t offset);
   AnnotationsDirectoryItem* CreateAnnotationsDirectoryItem(const DexFile& dex_file,
       const DexFile::AnnotationsDirectoryItem* disk_annotations_item, uint32_t offset);
-  CodeItem* CreateCodeItem(
-      const DexFile& dex_file, const DexFile::CodeItem& disk_code_item, uint32_t offset);
+  CodeItem* CreateCodeItem(const DexFile& dex_file,
+                           const DexFile::CodeItem& disk_code_item,
+                           uint32_t offset,
+                           uint32_t dex_method_index);
   ClassData* CreateClassData(const DexFile& dex_file, const uint8_t* encoded_data, uint32_t offset);
   void AddAnnotationsFromMapListSection(const DexFile& dex_file,
                                         uint32_t start_offset,
diff --git a/dexlayout/dex_writer.cc b/dexlayout/dex_writer.cc
index 489a6b1..6e1cb62 100644
--- a/dexlayout/dex_writer.cc
+++ b/dexlayout/dex_writer.cc
@@ -30,25 +30,6 @@
 
 namespace art {
 
-static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2;
-static constexpr uint32_t kDexSectionWordAlignment = 4;
-
-static constexpr uint32_t SectionAlignment(DexFile::MapItemType type) {
-  switch (type) {
-    case DexFile::kDexTypeClassDataItem:
-    case DexFile::kDexTypeStringDataItem:
-    case DexFile::kDexTypeDebugInfoItem:
-    case DexFile::kDexTypeAnnotationItem:
-    case DexFile::kDexTypeEncodedArrayItem:
-      return alignof(uint8_t);
-
-    default:
-      // All other sections are kDexAlignedSection.
-      return kDexSectionWordAlignment;
-  }
-}
-
-
 size_t EncodeIntValue(int32_t value, uint8_t* buffer) {
   size_t length = 0;
   if (value >= 0) {
@@ -526,69 +507,96 @@
   return offset - start;
 }
 
+uint32_t DexWriter::WriteCodeItemPostInstructionData(dex_ir::CodeItem* code_item,
+                                                     uint32_t offset,
+                                                     bool reserve_only) {
+  const uint32_t start_offset = offset;
+  if (code_item->TriesSize() != 0) {
+    // Make sure the try items are properly aligned.
+    offset = RoundUp(offset, kDexTryItemAlignment);
+    // Write try items.
+    for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) {
+      DexFile::TryItem disk_try_item;
+      if (!reserve_only) {
+        disk_try_item.start_addr_ = try_item->StartAddr();
+        disk_try_item.insn_count_ = try_item->InsnCount();
+        disk_try_item.handler_off_ = try_item->GetHandlers()->GetListOffset();
+      }
+      offset += Write(&disk_try_item, sizeof(disk_try_item), offset);
+    }
+    size_t max_offset = offset;
+    // Leave offset pointing to the end of the try items.
+    UNUSED(WriteUleb128(code_item->Handlers()->size(), offset));
+    for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) {
+      size_t list_offset = offset + handlers->GetListOffset();
+      uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 :
+          handlers->GetHandlers()->size();
+      list_offset += WriteSleb128(size, list_offset);
+      for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) {
+        if (handler->GetTypeId() != nullptr) {
+          list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset);
+        }
+        list_offset += WriteUleb128(handler->GetAddress(), list_offset);
+      }
+      // TODO: Clean this up to write the handlers in address order.
+      max_offset = std::max(max_offset, list_offset);
+    }
+    offset = max_offset;
+  }
+
+  return offset - start_offset;
+}
+
+uint32_t DexWriter::WriteCodeItem(dex_ir::CodeItem* code_item,
+                                  uint32_t offset,
+                                  bool reserve_only) {
+  DCHECK(code_item != nullptr);
+  const uint32_t start_offset = offset;
+  offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem));
+  ProcessOffset(&offset, code_item);
+
+  StandardDexFile::CodeItem disk_code_item;
+  if (!reserve_only) {
+    disk_code_item.registers_size_ = code_item->RegistersSize();
+    disk_code_item.ins_size_ = code_item->InsSize();
+    disk_code_item.outs_size_ = code_item->OutsSize();
+    disk_code_item.tries_size_ = code_item->TriesSize();
+    disk_code_item.debug_info_off_ = code_item->DebugInfo() == nullptr
+        ? 0
+        : code_item->DebugInfo()->GetOffset();
+    disk_code_item.insns_size_in_code_units_ = code_item->InsnsSize();
+  }
+  // Avoid using sizeof so that we don't write the fake instruction array at the end of the code
+  // item.
+  offset += Write(&disk_code_item,
+                  OFFSETOF_MEMBER(StandardDexFile::CodeItem, insns_),
+                  offset);
+  // Write the instructions.
+  offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
+  // Write the post instruction data.
+  offset += WriteCodeItemPostInstructionData(code_item, offset, reserve_only);
+  return offset - start_offset;
+}
+
 uint32_t DexWriter::WriteCodeItems(uint32_t offset, bool reserve_only) {
   DexLayoutSection* code_section = nullptr;
   if (!reserve_only && dex_layout_ != nullptr) {
     code_section = &dex_layout_->GetSections().sections_[static_cast<size_t>(
         DexLayoutSections::SectionType::kSectionTypeCode)];
   }
-  uint16_t uint16_buffer[4] = {};
-  uint32_t uint32_buffer[2] = {};
   uint32_t start = offset;
   for (auto& code_item : header_->GetCollections().CodeItems()) {
-    offset = RoundUp(offset, SectionAlignment(DexFile::kDexTypeCodeItem));
-    ProcessOffset(&offset, code_item.get());
-    if (!reserve_only) {
-      uint16_buffer[0] = code_item->RegistersSize();
-      uint16_buffer[1] = code_item->InsSize();
-      uint16_buffer[2] = code_item->OutsSize();
-      uint16_buffer[3] = code_item->TriesSize();
-      uint32_buffer[0] = code_item->DebugInfo() == nullptr ? 0 :
-          code_item->DebugInfo()->GetOffset();
-      uint32_buffer[1] = code_item->InsnsSize();
-      // Only add the section hotness info once.
-      if (code_section != nullptr) {
-        auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get());
-        if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) {
-          code_section->parts_[static_cast<size_t>(it->second)].CombineSection(
-              code_item->GetOffset(), code_item->GetOffset() + code_item->GetSize());
-        }
+    const size_t code_item_size = WriteCodeItem(code_item.get(), offset, reserve_only);
+    // Only add the section hotness info once.
+    if (!reserve_only && code_section != nullptr) {
+      auto it = dex_layout_->LayoutHotnessInfo().code_item_layout_.find(code_item.get());
+      if (it != dex_layout_->LayoutHotnessInfo().code_item_layout_.end()) {
+        code_section->parts_[static_cast<size_t>(it->second)].CombineSection(
+            offset,
+            offset + code_item_size);
       }
     }
-    offset += Write(uint16_buffer, 4 * sizeof(uint16_t), offset);
-    offset += Write(uint32_buffer, 2 * sizeof(uint32_t), offset);
-    offset += Write(code_item->Insns(), code_item->InsnsSize() * sizeof(uint16_t), offset);
-    if (code_item->TriesSize() != 0) {
-      if (code_item->InsnsSize() % 2 != 0) {
-        uint16_t padding[1] = { 0 };
-        offset += Write(padding, sizeof(uint16_t), offset);
-      }
-      uint32_t start_addr[1];
-      uint16_t insn_count_and_handler_off[2];
-      for (std::unique_ptr<const dex_ir::TryItem>& try_item : *code_item->Tries()) {
-        start_addr[0] = try_item->StartAddr();
-        insn_count_and_handler_off[0] = try_item->InsnCount();
-        insn_count_and_handler_off[1] = try_item->GetHandlers()->GetListOffset();
-        offset += Write(start_addr, sizeof(uint32_t), offset);
-        offset += Write(insn_count_and_handler_off, 2 * sizeof(uint16_t), offset);
-      }
-      // Leave offset pointing to the end of the try items.
-      UNUSED(WriteUleb128(code_item->Handlers()->size(), offset));
-      for (std::unique_ptr<const dex_ir::CatchHandler>& handlers : *code_item->Handlers()) {
-        size_t list_offset = offset + handlers->GetListOffset();
-        uint32_t size = handlers->HasCatchAll() ? (handlers->GetHandlers()->size() - 1) * -1 :
-            handlers->GetHandlers()->size();
-        list_offset += WriteSleb128(size, list_offset);
-        for (std::unique_ptr<const dex_ir::TypeAddrPair>& handler : *handlers->GetHandlers()) {
-          if (handler->GetTypeId() != nullptr) {
-            list_offset += WriteUleb128(handler->GetTypeId()->GetIndex(), list_offset);
-          }
-          list_offset += WriteUleb128(handler->GetAddress(), list_offset);
-        }
-      }
-    }
-    // TODO: Clean this up to properly calculate the size instead of assuming it doesn't change.
-    offset = code_item->GetOffset() + code_item->GetSize();
+    offset += code_item_size;
   }
 
   if (compute_offsets_ && start != offset) {
diff --git a/dexlayout/dex_writer.h b/dexlayout/dex_writer.h
index 92a002e..fdeb299 100644
--- a/dexlayout/dex_writer.h
+++ b/dexlayout/dex_writer.h
@@ -23,6 +23,7 @@
 
 #include "base/unix_file/fd_file.h"
 #include "dex/compact_dex_level.h"
+#include "dex/dex_file.h"
 #include "dex_ir.h"
 #include "mem_map.h"
 #include "os.h"
@@ -59,6 +60,25 @@
 
 class DexWriter {
  public:
+  static constexpr uint32_t kDataSectionAlignment = sizeof(uint32_t) * 2;
+  static constexpr uint32_t kDexSectionWordAlignment = 4;
+  static constexpr uint32_t kDexTryItemAlignment = sizeof(uint32_t);
+
+  static inline constexpr uint32_t SectionAlignment(DexFile::MapItemType type) {
+    switch (type) {
+      case DexFile::kDexTypeClassDataItem:
+      case DexFile::kDexTypeStringDataItem:
+      case DexFile::kDexTypeDebugInfoItem:
+      case DexFile::kDexTypeAnnotationItem:
+      case DexFile::kDexTypeEncodedArrayItem:
+        return alignof(uint8_t);
+
+      default:
+        // All other sections are kDexAlignedSection.
+        return DexWriter::kDexSectionWordAlignment;
+    }
+  }
+
   DexWriter(dex_ir::Header* header,
             MemMap* mem_map,
             DexLayout* dex_layout,
@@ -77,7 +97,7 @@
   virtual ~DexWriter() {}
 
  protected:
-  void WriteMemMap();
+  virtual void WriteMemMap();
 
   size_t Write(const void* buffer, size_t length, size_t offset) WARN_UNUSED;
   size_t WriteSleb128(uint32_t value, size_t offset) WARN_UNUSED;
@@ -118,6 +138,11 @@
   uint32_t WriteMapItems(uint32_t offset, MapItemQueue* queue);
   uint32_t GenerateAndWriteMapItems(uint32_t offset);
 
+  virtual uint32_t WriteCodeItemPostInstructionData(dex_ir::CodeItem* item,
+                                                    uint32_t offset,
+                                                    bool reserve_only);
+  virtual uint32_t WriteCodeItem(dex_ir::CodeItem* item, uint32_t offset, bool reserve_only);
+
   // Process an offset, if compute_offset is set, write into the dex ir item, otherwise read the
   // existing offset and use that for writing.
   void ProcessOffset(uint32_t* const offset, dex_ir::Item* item) {
diff --git a/dexlist/dexlist.cc b/dexlist/dexlist.cc
index afbd446..1ced8ca 100644
--- a/dexlist/dexlist.cc
+++ b/dexlist/dexlist.cc
@@ -101,7 +101,7 @@
   if (pCode == nullptr || codeOffset == 0) {
     return;
   }
-  CodeItemDebugInfoAccessor accessor(*pDexFile, pCode);
+  CodeItemDebugInfoAccessor accessor(*pDexFile, pCode, idx);
 
   // Method information.
   const DexFile::MethodId& pMethodId = pDexFile->GetMethodId(idx);
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 78cb3b6..2e34baf 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -25,6 +25,7 @@
     defaults: ["art_defaults"],
     host_supported: true,
     srcs: [
+        "dex/compact_dex_debug_info.cc",
         "dex/compact_dex_file.cc",
         "dex/dex_file.cc",
         "dex/dex_file_exception_helpers.cc",
@@ -120,6 +121,7 @@
         "common_throws.cc",
         "compiler_filter.cc",
         "debugger.cc",
+        "dex/compact_dex_debug_info.cc",
         "dex/compact_dex_file.cc",
         "dex/dex_file.cc",
         "dex/dex_file_annotations.cc",
@@ -637,6 +639,7 @@
         "class_table_test.cc",
         "compiler_filter_test.cc",
         "dex/code_item_accessors_test.cc",
+        "dex/compact_dex_debug_info_test.cc",
         "dex/compact_dex_file_test.cc",
         "dex/dex_file_test.cc",
         "dex/dex_file_verifier_test.cc",
diff --git a/runtime/dex/code_item_accessors-inl.h b/runtime/dex/code_item_accessors-inl.h
index 01a2b2f..63fd120 100644
--- a/runtime/dex/code_item_accessors-inl.h
+++ b/runtime/dex/code_item_accessors-inl.h
@@ -34,7 +34,9 @@
     : CodeItemDataAccessor(*method->GetDexFile(), method->GetCodeItem()) {}
 
 inline CodeItemDebugInfoAccessor::CodeItemDebugInfoAccessor(ArtMethod* method)
-    : CodeItemDebugInfoAccessor(*method->GetDexFile(), method->GetCodeItem()) {}
+    : CodeItemDebugInfoAccessor(*method->GetDexFile(),
+                                method->GetCodeItem(),
+                                method->GetDexMethodIndex()) {}
 
 }  // namespace art
 
diff --git a/runtime/dex/code_item_accessors-no_art-inl.h b/runtime/dex/code_item_accessors-no_art-inl.h
index a613559..aaa86d4 100644
--- a/runtime/dex/code_item_accessors-no_art-inl.h
+++ b/runtime/dex/code_item_accessors-no_art-inl.h
@@ -146,22 +146,28 @@
 
 inline void CodeItemDebugInfoAccessor::Init(const DexFile& dex_file,
                                             const DexFile::CodeItem* code_item,
-                                            uint32_t debug_info_offset) {
+                                            uint32_t dex_method_index) {
+  if (code_item == nullptr) {
+    return;
+  }
   dex_file_ = &dex_file;
-  debug_info_offset_ = debug_info_offset;
   if (dex_file.IsCompactDexFile()) {
-    Init(down_cast<const CompactDexFile::CodeItem&>(*code_item));
+    Init(down_cast<const CompactDexFile::CodeItem&>(*code_item), dex_method_index);
   } else {
     DCHECK(dex_file.IsStandardDexFile());
     Init(down_cast<const StandardDexFile::CodeItem&>(*code_item));
   }
 }
 
-inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item) {
+inline void CodeItemDebugInfoAccessor::Init(const CompactDexFile::CodeItem& code_item,
+                                            uint32_t dex_method_index) {
+  debug_info_offset_ = down_cast<const CompactDexFile*>(dex_file_)->GetDebugInfoOffset(
+      dex_method_index);
   CodeItemDataAccessor::Init(code_item);
 }
 
 inline void CodeItemDebugInfoAccessor::Init(const StandardDexFile::CodeItem& code_item) {
+  debug_info_offset_ = code_item.debug_info_off_;
   CodeItemDataAccessor::Init(code_item);
 }
 
@@ -180,14 +186,6 @@
                                          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/code_item_accessors.h b/runtime/dex/code_item_accessors.h
index b5a6957..66531f9 100644
--- a/runtime/dex/code_item_accessors.h
+++ b/runtime/dex/code_item_accessors.h
@@ -131,20 +131,16 @@
  public:
   CodeItemDebugInfoAccessor() = default;
 
-  // Handles null code items, but not null dex files.
-  ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file,
-                                          const DexFile::CodeItem* code_item);
-
   // Initialize with an existing offset.
   ALWAYS_INLINE CodeItemDebugInfoAccessor(const DexFile& dex_file,
                                           const DexFile::CodeItem* code_item,
-                                          uint32_t debug_info_offset) {
-    Init(dex_file, code_item, debug_info_offset);
+                                          uint32_t dex_method_index) {
+    Init(dex_file, code_item, dex_method_index);
   }
 
   ALWAYS_INLINE void Init(const DexFile& dex_file,
                           const DexFile::CodeItem* code_item,
-                          uint32_t debug_info_offset);
+                          uint32_t dex_method_index);
 
   ALWAYS_INLINE explicit CodeItemDebugInfoAccessor(ArtMethod* method);
 
@@ -159,7 +155,7 @@
                             void* context) const;
 
  protected:
-  ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item);
+  ALWAYS_INLINE void Init(const CompactDexFile::CodeItem& code_item, uint32_t dex_method_index);
   ALWAYS_INLINE void Init(const StandardDexFile::CodeItem& code_item);
 
  private:
diff --git a/runtime/dex/compact_dex_debug_info.cc b/runtime/dex/compact_dex_debug_info.cc
new file mode 100644
index 0000000..19495ca
--- /dev/null
+++ b/runtime/dex/compact_dex_debug_info.cc
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "compact_dex_debug_info.h"
+
+#include "compact_dex_utils.h"
+#include "leb128.h"
+
+namespace art {
+
+constexpr size_t CompactDexDebugInfoOffsetTable::kElementsPerIndex;
+
+CompactDexDebugInfoOffsetTable::Accessor::Accessor(const uint8_t* data_begin,
+                                                   uint32_t debug_info_base,
+                                                   uint32_t debug_info_table_offset)
+    : table_(reinterpret_cast<const uint32_t*>(data_begin + debug_info_table_offset)),
+      debug_info_base_(debug_info_base),
+      data_begin_(data_begin) {}
+
+uint32_t CompactDexDebugInfoOffsetTable::Accessor::GetDebugInfoOffset(uint32_t method_idx) const {
+  const uint32_t offset = table_[method_idx / kElementsPerIndex];
+  const size_t bit_index = method_idx % kElementsPerIndex;
+
+  const uint8_t* block = data_begin_ + offset;
+  uint16_t bit_mask = *block;
+  ++block;
+  bit_mask = (bit_mask << kBitsPerByte) | *block;
+  ++block;
+  if ((bit_mask & (1 << bit_index)) == 0) {
+    // Bit is not set means the offset is 0 for the debug info.
+    return 0u;
+  }
+  // Trim off the bits above the index we want and count how many bits are set. This is how many
+  // lebs we need to decode.
+  size_t count = POPCOUNT(static_cast<uintptr_t>(bit_mask) << (kBitsPerIntPtrT - 1 - bit_index));
+  DCHECK_GT(count, 0u);
+  uint32_t current_offset = debug_info_base_;
+  do {
+    current_offset += DecodeUnsignedLeb128(&block);
+    --count;
+  } while (count > 0);
+  return current_offset;
+}
+
+void CompactDexDebugInfoOffsetTable::Build(const std::vector<uint32_t>& debug_info_offsets,
+                                           std::vector<uint8_t>* out_data,
+                                           uint32_t* out_min_offset,
+                                           uint32_t* out_table_offset) {
+  DCHECK(out_data != nullptr);
+  DCHECK(out_data->empty());
+  // Calculate the base offset and return it.
+  *out_min_offset = std::numeric_limits<uint32_t>::max();
+  for (const uint32_t offset : debug_info_offsets) {
+    if (offset != 0u) {
+      *out_min_offset = std::min(*out_min_offset, offset);
+    }
+  }
+  // Write the leb blocks and store the important offsets (each kElementsPerIndex elements).
+  size_t block_start = 0;
+
+  std::vector<uint32_t> offset_table;
+
+  // Write data first then the table.
+  while (block_start < debug_info_offsets.size()) {
+    // Write the offset of the block for each block.
+    offset_table.push_back(out_data->size());
+
+    // Block size of up to kElementsPerIndex
+    const size_t block_size = std::min(debug_info_offsets.size() - block_start, kElementsPerIndex);
+
+    // Calculate bit mask since need to write that first.
+    uint16_t bit_mask = 0u;
+    for (size_t i = 0; i < block_size; ++i) {
+      if (debug_info_offsets[block_start + i] != 0u) {
+        bit_mask |= 1 << i;
+      }
+    }
+    // Write bit mask.
+    out_data->push_back(static_cast<uint8_t>(bit_mask >> kBitsPerByte));
+    out_data->push_back(static_cast<uint8_t>(bit_mask));
+
+    // Write debug info offsets relative to the current offset.
+    uint32_t current_offset = *out_min_offset;
+    for (size_t i = 0; i < block_size; ++i) {
+      const uint32_t debug_info_offset = debug_info_offsets[block_start + i];
+      if (debug_info_offset != 0u) {
+        uint32_t delta = debug_info_offset - current_offset;
+        EncodeUnsignedLeb128(out_data, delta);
+        current_offset = debug_info_offset;
+      }
+    }
+
+    block_start += block_size;
+  }
+
+  // Write the offset table.
+  AlignmentPadVector(out_data, alignof(uint32_t));
+  *out_table_offset = out_data->size();
+  out_data->insert(out_data->end(),
+                   reinterpret_cast<const uint8_t*>(&offset_table[0]),
+                   reinterpret_cast<const uint8_t*>(&offset_table[0] + offset_table.size()));
+}
+
+}  // namespace art
diff --git a/runtime/dex/compact_dex_debug_info.h b/runtime/dex/compact_dex_debug_info.h
new file mode 100644
index 0000000..1aff758
--- /dev/null
+++ b/runtime/dex/compact_dex_debug_info.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_
+#define ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_
+
+#include <cstdint>
+#include <vector>
+
+namespace art {
+
+// Debug offset table for compact dex, aims to minimize size while still providing reasonable
+// speed (10-20ns average time per lookup on host).
+class CompactDexDebugInfoOffsetTable {
+ public:
+  // This value is coupled with the leb chunk bitmask. That logic must also be adjusted when the
+  // integer is modified.
+  static constexpr size_t kElementsPerIndex = 16;
+
+  // Leb block format:
+  // [uint16_t] 16 bit mask for what method ids actually have a debug info offset for the chunk.
+  // [lebs] Up to 16 lebs encoded using leb128, one leb bit. The leb specifies how the offset
+  // changes compared to the previous index.
+
+  class Accessor {
+   public:
+    Accessor(const uint8_t* data_begin,
+             uint32_t debug_info_base,
+             uint32_t debug_info_table_offset);
+
+    // Return the debug info for a method index (or 0 if it doesn't have one).
+    uint32_t GetDebugInfoOffset(uint32_t method_idx) const;
+
+   private:
+    const uint32_t* const table_;
+    const uint32_t debug_info_base_;
+    const uint8_t* const data_begin_;
+  };
+
+  // Returned offsets are all relative to debug_info_offsets.
+  static void Build(const std::vector<uint32_t>& debug_info_offsets,
+                    std::vector<uint8_t>* out_data,
+                    uint32_t* out_min_offset,
+                    uint32_t* out_table_offset);
+
+  // 32 bit aligned for the offset table.
+  static constexpr size_t kAlignment = sizeof(uint32_t);
+};
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEX_COMPACT_DEX_DEBUG_INFO_H_
diff --git a/runtime/dex/compact_dex_debug_info_test.cc b/runtime/dex/compact_dex_debug_info_test.cc
new file mode 100644
index 0000000..02b95e6
--- /dev/null
+++ b/runtime/dex/compact_dex_debug_info_test.cc
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <vector>
+#include <sys/mman.h>
+
+#include "base/logging.h"
+#include "dex/compact_dex_debug_info.h"
+#include "gtest/gtest.h"
+#include "mem_map.h"
+
+namespace art {
+
+TEST(CompactDexDebugInfoTest, TestBuildAndAccess) {
+  MemMap::Init();
+
+  const size_t kDebugInfoMinOffset = 1234567;
+  std::vector<uint32_t> offsets = {
+      0, 17, 2, 3, 11, 0, 0, 0, 0, 1, 0, 1552, 100, 122, 44, 1234567, 0, 0,
+      std::numeric_limits<uint32_t>::max() - kDebugInfoMinOffset, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+      0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12,
+  };
+  // Add some large offset since the debug info section will never be that close to the beginning
+  // of the file.
+  for (uint32_t& offset : offsets) {
+    if (offset != 0u) {
+      offset += kDebugInfoMinOffset;
+    }
+  }
+
+  std::vector<uint8_t> data;
+  uint32_t base_offset = 0;
+  uint32_t table_offset = 0;
+  CompactDexDebugInfoOffsetTable::Build(offsets,
+                                        /*out*/ &data,
+                                        /*out*/ &base_offset,
+                                        /*out*/ &table_offset);
+  EXPECT_GE(base_offset, kDebugInfoMinOffset);
+  EXPECT_LT(table_offset, data.size());
+  ASSERT_GT(data.size(), 0u);
+  const size_t before_size = offsets.size() * sizeof(offsets.front());
+  EXPECT_LT(data.size(), before_size);
+
+  // Note that the accessor requires the data to be aligned. Use memmap to accomplish this.
+  std::string error_msg;
+  // Leave some extra room since we don't copy the table at the start (for testing).
+  constexpr size_t kExtraOffset = 4 * 128;
+  std::unique_ptr<MemMap> fake_dex(MemMap::MapAnonymous("fake dex",
+                                                        nullptr,
+                                                        data.size() + kExtraOffset,
+                                                        PROT_READ | PROT_WRITE,
+                                                        /*low_4gb*/ false,
+                                                        /*reuse*/ false,
+                                                        &error_msg));
+  ASSERT_TRUE(fake_dex != nullptr) << error_msg;
+  std::copy(data.begin(), data.end(), fake_dex->Begin() + kExtraOffset);
+
+  CompactDexDebugInfoOffsetTable::Accessor accessor(fake_dex->Begin() + kExtraOffset,
+                                                    base_offset,
+                                                    table_offset);
+  for (size_t i = 0; i < offsets.size(); ++i) {
+    EXPECT_EQ(offsets[i], accessor.GetDebugInfoOffset(i));
+  }
+
+  // Sort to produce a try and produce a smaller table. This happens because the leb diff is smaller
+  // for sorted increasing order.
+  std::sort(offsets.begin(), offsets.end());
+  std::vector<uint8_t> sorted_data;
+  CompactDexDebugInfoOffsetTable::Build(offsets,
+                                        /*out*/ &sorted_data,
+                                        /*out*/ &base_offset,
+                                        /*out*/ &table_offset);
+  EXPECT_LT(sorted_data.size(), data.size());
+  {
+    ScopedLogSeverity sls(LogSeverity::INFO);
+    LOG(INFO) << "raw size " << before_size
+              << " table size " << data.size()
+              << " sorted table size " << sorted_data.size();
+  }
+}
+
+}  // namespace art
diff --git a/runtime/dex/compact_dex_file.cc b/runtime/dex/compact_dex_file.cc
index 2d1ee04..ff193ff 100644
--- a/runtime/dex/compact_dex_file.cc
+++ b/runtime/dex/compact_dex_file.cc
@@ -63,4 +63,21 @@
       reinterpret_cast<uintptr_t>(&item);
 }
 
+CompactDexFile::CompactDexFile(const uint8_t* base,
+                               size_t size,
+                               const std::string& location,
+                               uint32_t location_checksum,
+                               const OatDexFile* oat_dex_file,
+                               DexFileContainer* container)
+    : DexFile(base,
+              size,
+              location,
+              location_checksum,
+              oat_dex_file,
+              container,
+              /*is_compact_dex*/ true),
+      debug_info_offsets_(Begin() + GetHeader().debug_info_offsets_pos_,
+                          GetHeader().debug_info_base_,
+                          GetHeader().debug_info_offsets_table_offset_) {}
+
 }  // namespace art
diff --git a/runtime/dex/compact_dex_file.h b/runtime/dex/compact_dex_file.h
index 280c6f7..af782a9 100644
--- a/runtime/dex/compact_dex_file.h
+++ b/runtime/dex/compact_dex_file.h
@@ -19,6 +19,7 @@
 
 #include "base/casts.h"
 #include "dex_file.h"
+#include "dex/compact_dex_debug_info.h"
 
 namespace art {
 
@@ -41,13 +42,45 @@
    private:
     uint32_t feature_flags_ = 0u;
 
+    // Position in the compact dex file for the debug info table data starts.
+    uint32_t debug_info_offsets_pos_ = 0u;
+
+    // Offset into the debug info table data where the lookup table is.
+    uint32_t debug_info_offsets_table_offset_ = 0u;
+
+    // Base offset of where debug info starts in the dex file.
+    uint32_t debug_info_base_ = 0u;
+
+    friend class CompactDexFile;
     friend class CompactDexWriter;
   };
 
+  // Like the standard code item except without a debug info offset.
   struct CodeItem : public DexFile::CodeItem {
+    static constexpr size_t kAlignment = sizeof(uint32_t);
+
    private:
-    // TODO: Insert compact dex specific fields here.
+    CodeItem() = default;
+
+    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
+                                         //   that this code is for
+    uint16_t outs_size_;                 // the number of words of outgoing argument space required
+                                         //   by this code for method invocation
+    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 insns_size_in_code_units_;  // size of the insns array, in 2 byte code units
+    uint16_t insns_[1];                  // actual array of bytecode.
+
+    ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
+    friend class CodeItemDataAccessor;
+    friend class CodeItemDebugInfoAccessor;
+    friend class CodeItemInstructionAccessor;
     friend class CompactDexFile;
+    friend class CompactDexWriter;
     DISALLOW_COPY_AND_ASSIGN(CodeItem);
   };
 
@@ -73,25 +106,22 @@
 
   uint32_t GetCodeItemSize(const DexFile::CodeItem& item) const OVERRIDE;
 
+  uint32_t GetDebugInfoOffset(uint32_t dex_method_index) const {
+    return debug_info_offsets_.GetDebugInfoOffset(dex_method_index);
+  }
+
  private:
-  // Not supported yet.
   CompactDexFile(const uint8_t* base,
                  size_t size,
                  const std::string& location,
                  uint32_t location_checksum,
                  const OatDexFile* oat_dex_file,
-                 DexFileContainer* container)
-      : DexFile(base,
-                size,
-                location,
-                location_checksum,
-                oat_dex_file,
-                container,
-                /*is_compact_dex*/ true) {}
+                 DexFileContainer* container);
+
+  CompactDexDebugInfoOffsetTable::Accessor debug_info_offsets_;
 
   friend class DexFile;
   friend class DexFileLoader;
-
   DISALLOW_COPY_AND_ASSIGN(CompactDexFile);
 };
 
diff --git a/runtime/dex/compact_dex_utils.h b/runtime/dex/compact_dex_utils.h
new file mode 100644
index 0000000..1c7e951
--- /dev/null
+++ b/runtime/dex/compact_dex_utils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_
+#define ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_
+
+#include <vector>
+
+#include "base/bit_utils.h"
+
+namespace art {
+
+// Add padding to the end of the array until the size is aligned.
+template <typename T, template<typename> class Allocator>
+static inline void AlignmentPadVector(std::vector<T, Allocator<T>>* dest,
+                                      size_t alignment) {
+  while (!IsAlignedParam(dest->size(), alignment)) {
+    dest->push_back(T());
+  }
+}
+
+}  // namespace art
+
+#endif  // ART_RUNTIME_DEX_COMPACT_DEX_UTILS_H_
diff --git a/runtime/dex/dex_file.h b/runtime/dex/dex_file.h
index 9b31a75..183d84e 100644
--- a/runtime/dex/dex_file.h
+++ b/runtime/dex/dex_file.h
@@ -301,28 +301,12 @@
     DISALLOW_COPY_AND_ASSIGN(CallSiteIdItem);
   };
 
-  // Raw code_item.
+  // Base code_item, compact dex and standard dex have different code item layouts.
   struct CodeItem {
    protected:
-    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
-                                         //   that this code is for
-    uint16_t outs_size_;                 // the number of words of outgoing argument space required
-                                         //   by this code for method invocation
-    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_;            // 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.
+    CodeItem() = default;
 
    private:
-    ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
-    friend class CodeItemDataAccessor;
-    friend class CodeItemDebugInfoAccessor;
-    friend class CodeItemInstructionAccessor;
     DISALLOW_COPY_AND_ASSIGN(CodeItem);
   };
 
@@ -333,6 +317,8 @@
     uint16_t handler_off_;
 
    private:
+    TryItem() = default;
+    friend class DexWriter;
     DISALLOW_COPY_AND_ASSIGN(TryItem);
   };
 
diff --git a/runtime/dex/dex_file_test.cc b/runtime/dex/dex_file_test.cc
index 1c8b3e4..cb721af 100644
--- a/runtime/dex/dex_file_test.cc
+++ b/runtime/dex/dex_file_test.cc
@@ -738,8 +738,10 @@
   std::unique_ptr<const DexFile> raw = OpenDexFileInMemoryBase64(
       kRawDexDebugInfoLocalNullType, tmp.GetFilename().c_str(), 0xf25f2b38U, true);
   const DexFile::ClassDef& class_def = raw->GetClassDef(0);
-  const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def, 1));
-  CodeItemDebugInfoAccessor accessor(*raw, code_item);
+  constexpr uint32_t kMethodIdx = 1;
+  const DexFile::CodeItem* code_item = raw->GetCodeItem(raw->FindCodeItemOffset(class_def,
+                                                                                kMethodIdx));
+  CodeItemDebugInfoAccessor accessor(*raw, code_item, kMethodIdx);
   ASSERT_TRUE(accessor.DecodeDebugLocalInfo(true, 1, Callback, nullptr));
 }
 
diff --git a/runtime/dex/standard_dex_file.h b/runtime/dex/standard_dex_file.h
index 819b721..6437def 100644
--- a/runtime/dex/standard_dex_file.h
+++ b/runtime/dex/standard_dex_file.h
@@ -36,7 +36,27 @@
     static constexpr size_t kAlignment = 4;
 
    private:
-    // TODO: Insert standard dex specific fields here.
+    CodeItem() = default;
+
+    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
+                                         //   that this code is for
+    uint16_t outs_size_;                 // the number of words of outgoing argument space required
+                                         //   by this code for method invocation
+    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_;            // 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.
+
+    ART_FRIEND_TEST(CodeItemAccessorsTest, TestDexInstructionsAccessor);
+    friend class CodeItemDataAccessor;
+    friend class CodeItemDebugInfoAccessor;
+    friend class CodeItemInstructionAccessor;
+    friend class DexWriter;
     friend class StandardDexFile;
     DISALLOW_COPY_AND_ASSIGN(CodeItem);
   };
diff --git a/runtime/vdex_file.h b/runtime/vdex_file.h
index 73190c9..4687a39 100644
--- a/runtime/vdex_file.h
+++ b/runtime/vdex_file.h
@@ -84,8 +84,8 @@
 
    private:
     static constexpr uint8_t kVdexMagic[] = { 'v', 'd', 'e', 'x' };
-    // 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' };
+    // Last update: Side table for debug info offsets in compact dex.
+    static constexpr uint8_t kVdexVersion[] = { '0', '1', '4', '\0' };
 
     uint8_t magic_[4];
     uint8_t version_[4];