Separate maps from code in oat file.

Write all GC maps first, then all mapping tables and then
all vmap tables and only then align the offset to page size
and write all method code chunks with headers.

Bug: 11767815
Change-Id: Ic83555c8303c5be119afc43e95e58c0a32ff2a4f
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index dc66e9c..2114fe9 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -38,6 +38,14 @@
 
 namespace art {
 
+#define DCHECK_OFFSET() \
+  DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
+    << "file_offset=" << file_offset << " relative_offset=" << relative_offset
+
+#define DCHECK_OFFSET_() \
+  DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \
+    << "file_offset=" << file_offset << " offset_=" << offset_
+
 OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files,
                      uint32_t image_file_location_oat_checksum,
                      uintptr_t image_file_location_oat_begin,
@@ -66,7 +74,7 @@
     size_quick_resolution_trampoline_(0),
     size_quick_to_interpreter_bridge_(0),
     size_trampoline_alignment_(0),
-    size_code_size_(0),
+    size_method_header_(0),
     size_code_(0),
     size_code_alignment_(0),
     size_mapping_table_(0),
@@ -99,6 +107,10 @@
     offset = InitOatClasses(offset);
   }
   {
+    TimingLogger::ScopedSplit split("InitOatMaps", timings);
+    offset = InitOatMaps(offset);
+  }
+  {
     TimingLogger::ScopedSplit split("InitOatCode", timings);
     offset = InitOatCode(offset);
   }
@@ -118,6 +130,585 @@
   STLDeleteElements(&oat_classes_);
 }
 
+struct OatWriter::GcMapDataAccess {
+  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+    return &compiled_method->GetGcMap();
+  }
+
+  static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
+    return oat_class->method_offsets_[method_offsets_index].gc_map_offset_;
+  }
+
+  static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
+      ALWAYS_INLINE {
+    oat_class->method_offsets_[method_offsets_index].gc_map_offset_ = offset;
+  }
+
+  static const char* Name() ALWAYS_INLINE {
+    return "GC map";
+  }
+};
+
+struct OatWriter::MappingTableDataAccess {
+  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+    return &compiled_method->GetMappingTable();
+  }
+
+  static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
+    return oat_class->method_offsets_[method_offsets_index].mapping_table_offset_;
+  }
+
+  static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
+      ALWAYS_INLINE {
+    oat_class->method_offsets_[method_offsets_index].mapping_table_offset_ = offset;
+  }
+
+  static const char* Name() ALWAYS_INLINE {
+    return "mapping table";
+  }
+};
+
+struct OatWriter::VmapTableDataAccess {
+  static const std::vector<uint8_t>* GetData(const CompiledMethod* compiled_method) ALWAYS_INLINE {
+    return &compiled_method->GetVmapTable();
+  }
+
+  static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE {
+    return oat_class->method_offsets_[method_offsets_index].vmap_table_offset_;
+  }
+
+  static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset)
+      ALWAYS_INLINE {
+    oat_class->method_offsets_[method_offsets_index].vmap_table_offset_ = offset;
+  }
+
+  static const char* Name() ALWAYS_INLINE {
+    return "vmap table";
+  }
+};
+
+class OatWriter::DexMethodVisitor {
+ public:
+  DexMethodVisitor(OatWriter* writer, size_t offset)
+    : writer_(writer),
+      offset_(offset),
+      dex_file_(nullptr),
+      class_def_index_(DexFile::kDexNoIndex) {
+  }
+
+  virtual bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+    DCHECK(dex_file_ == nullptr);
+    DCHECK_EQ(class_def_index_, DexFile::kDexNoIndex);
+    dex_file_ = dex_file;
+    class_def_index_ = class_def_index;
+    return true;
+  }
+
+  virtual bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) = 0;
+
+  virtual bool EndClass() {
+    if (kIsDebugBuild) {
+      dex_file_ = nullptr;
+      class_def_index_ = DexFile::kDexNoIndex;
+    }
+    return true;
+  }
+
+  size_t GetOffset() const {
+    return offset_;
+  }
+
+ protected:
+  virtual ~DexMethodVisitor() { }
+
+  OatWriter* const writer_;
+
+  // The offset is usually advanced for each visited method by the derived class.
+  size_t offset_;
+
+  // The dex file and class def index are set in StartClass().
+  const DexFile* dex_file_;
+  size_t class_def_index_;
+};
+
+class OatWriter::OatDexMethodVisitor : public DexMethodVisitor {
+ public:
+  OatDexMethodVisitor(OatWriter* writer, size_t offset)
+    : DexMethodVisitor(writer, offset),
+      oat_class_index_(0u),
+      method_offsets_index_(0u) {
+  }
+
+  bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+    DexMethodVisitor::StartClass(dex_file, class_def_index);
+    DCHECK_LT(oat_class_index_, writer_->oat_classes_.size());
+    method_offsets_index_ = 0u;
+    return true;
+  }
+
+  bool EndClass() {
+    ++oat_class_index_;
+    return DexMethodVisitor::EndClass();
+  }
+
+ protected:
+  size_t oat_class_index_;
+  size_t method_offsets_index_;
+};
+
+class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor {
+ public:
+  InitOatClassesMethodVisitor(OatWriter* writer, size_t offset)
+    : DexMethodVisitor(writer, offset),
+      compiled_methods_(),
+      num_non_null_compiled_methods_(0u) {
+    compiled_methods_.reserve(256u);
+  }
+
+  bool StartClass(const DexFile* dex_file, size_t class_def_index) {
+    DexMethodVisitor::StartClass(dex_file, class_def_index);
+    compiled_methods_.clear();
+    num_non_null_compiled_methods_ = 0u;
+    return true;
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+    // Fill in the compiled_methods_ array for methods that have a
+    // CompiledMethod. We track the number of non-null entries in
+    // num_non_null_compiled_methods_ since we only want to allocate
+    // OatMethodOffsets for the compiled methods.
+    uint32_t method_idx = it.GetMemberIndex();
+    CompiledMethod* compiled_method =
+        writer_->compiler_driver_->GetCompiledMethod(MethodReference(dex_file_, method_idx));
+    compiled_methods_.push_back(compiled_method);
+    if (compiled_method != nullptr) {
+        ++num_non_null_compiled_methods_;
+    }
+    return true;
+  }
+
+  bool EndClass() {
+    ClassReference class_ref(dex_file_, class_def_index_);
+    CompiledClass* compiled_class = writer_->compiler_driver_->GetCompiledClass(class_ref);
+    mirror::Class::Status status;
+    if (compiled_class != NULL) {
+      status = compiled_class->GetStatus();
+    } else if (writer_->compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
+      status = mirror::Class::kStatusError;
+    } else {
+      status = mirror::Class::kStatusNotReady;
+    }
+
+    OatClass* oat_class = new OatClass(offset_, compiled_methods_,
+                                       num_non_null_compiled_methods_, status);
+    writer_->oat_classes_.push_back(oat_class);
+    offset_ += oat_class->SizeOf();
+    return DexMethodVisitor::EndClass();
+  }
+
+ private:
+  std::vector<CompiledMethod*> compiled_methods_;
+  size_t num_non_null_compiled_methods_;
+};
+
+class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor {
+ public:
+  InitCodeMethodVisitor(OatWriter* writer, size_t offset)
+    : OatDexMethodVisitor(writer, offset) {
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+    if (compiled_method != nullptr) {
+      // Derived from CompiledMethod.
+      uint32_t quick_code_offset = 0;
+      uint32_t frame_size_in_bytes = kStackAlignment;
+      uint32_t core_spill_mask = 0;
+      uint32_t fp_spill_mask = 0;
+
+      const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
+      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      if (portable_code != nullptr) {
+        CHECK(quick_code == nullptr);
+        size_t oat_method_offsets_offset =
+            oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
+        compiled_method->AddOatdataOffsetToCompliledCodeOffset(
+            oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
+      } else {
+        CHECK(quick_code != nullptr);
+        offset_ = compiled_method->AlignCode(offset_);
+        DCHECK_ALIGNED_PARAM(offset_,
+                             GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+        uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+        CHECK_NE(code_size, 0U);
+        uint32_t thumb_offset = compiled_method->CodeDelta();
+        quick_code_offset = offset_ + sizeof(OatMethodHeader) + thumb_offset;
+
+        std::vector<uint8_t>* cfi_info = writer_->compiler_driver_->GetCallFrameInformation();
+        if (cfi_info != nullptr) {
+          // Copy in the FDE, if present
+          const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
+          if (fde != nullptr) {
+            // Copy the information into cfi_info and then fix the address in the new copy.
+            int cur_offset = cfi_info->size();
+            cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
+
+            // Set the 'initial_location' field to address the start of the method.
+            uint32_t new_value = quick_code_offset - writer_->oat_header_->GetExecutableOffset();
+            uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
+            (*cfi_info)[offset_to_update+0] = new_value;
+            (*cfi_info)[offset_to_update+1] = new_value >> 8;
+            (*cfi_info)[offset_to_update+2] = new_value >> 16;
+            (*cfi_info)[offset_to_update+3] = new_value >> 24;
+            std::string name = PrettyMethod(it.GetMemberIndex(), *dex_file_, false);
+            writer_->method_info_.push_back(DebugInfo(name, new_value, new_value + code_size));
+          }
+        }
+
+        // Deduplicate code arrays.
+        auto code_iter = dedupe_map_.find(quick_code);
+        if (code_iter != dedupe_map_.end()) {
+          quick_code_offset = code_iter->second;
+        } else {
+          dedupe_map_.Put(quick_code, quick_code_offset);
+          OatMethodHeader method_header(code_size);
+          offset_ += sizeof(method_header);  // Method header is prepended before code.
+          writer_->oat_header_->UpdateChecksum(&method_header, sizeof(method_header));
+          offset_ += code_size;
+          writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
+        }
+      }
+      frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
+      core_spill_mask = compiled_method->GetCoreSpillMask();
+      fp_spill_mask = compiled_method->GetFpSpillMask();
+
+      if (kIsDebugBuild) {
+        // We expect GC maps except when the class hasn't been verified or the method is native.
+        const CompilerDriver* compiler_driver = writer_->compiler_driver_;
+        ClassReference class_ref(dex_file_, class_def_index_);
+        CompiledClass* compiled_class = compiler_driver->GetCompiledClass(class_ref);
+        mirror::Class::Status status;
+        if (compiled_class != NULL) {
+          status = compiled_class->GetStatus();
+        } else if (compiler_driver->GetVerificationResults()->IsClassRejected(class_ref)) {
+          status = mirror::Class::kStatusError;
+        } else {
+          status = mirror::Class::kStatusNotReady;
+        }
+        const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
+        size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
+        bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
+        CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
+            << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
+            << (status < mirror::Class::kStatusVerified) << " " << status << " "
+            << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+      }
+
+      DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+      OatMethodOffsets* offsets = &oat_class->method_offsets_[method_offsets_index_];
+      offsets->code_offset_ = quick_code_offset;
+      offsets->frame_size_in_bytes_ = frame_size_in_bytes;
+      offsets->core_spill_mask_ = core_spill_mask;
+      offsets->fp_spill_mask_ = fp_spill_mask;
+      ++method_offsets_index_;
+    }
+
+    return true;
+  }
+
+ private:
+  // 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.
+  SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+};
+
+template <typename DataAccess>
+class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor {
+ public:
+  InitMapMethodVisitor(OatWriter* writer, size_t offset)
+    : OatDexMethodVisitor(writer, offset) {
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+    if (compiled_method != nullptr) {
+      DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+      DCHECK_EQ(DataAccess::GetOffset(oat_class, method_offsets_index_), 0u);
+
+      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      uint32_t map_size = map->size() * sizeof((*map)[0]);
+      if (map_size != 0u) {
+        auto it = dedupe_map_.find(map);
+        if (it != dedupe_map_.end()) {
+          DataAccess::SetOffset(oat_class, method_offsets_index_, it->second);
+        } else {
+          DataAccess::SetOffset(oat_class, method_offsets_index_, offset_);
+          dedupe_map_.Put(map, offset_);
+          offset_ += map_size;
+          writer_->oat_header_->UpdateChecksum(&(*map)[0], map_size);
+        }
+      }
+      ++method_offsets_index_;
+    }
+
+    return true;
+  }
+
+ private:
+  // 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.
+  SafeMap<const std::vector<uint8_t>*, uint32_t> dedupe_map_;
+};
+
+class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor {
+ public:
+  InitImageMethodVisitor(OatWriter* writer, size_t offset)
+    : OatDexMethodVisitor(writer, offset) {
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it)
+      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) {
+    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+    OatMethodOffsets offsets(0u, kStackAlignment, 0u, 0u, 0u, 0u, 0u);
+    if (compiled_method != nullptr) {
+      DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size());
+      offsets = oat_class->method_offsets_[method_offsets_index_];
+      ++method_offsets_index_;
+    }
+
+    // Derive frame size and spill masks for native methods without code:
+    // These are generic JNI methods...
+    uint32_t method_idx = it.GetMemberIndex();
+    bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
+    if (is_native && compiled_method == nullptr) {
+      // Compute Sirt size as putting _every_ reference into it, even null ones.
+      uint32_t s_len;
+      const char* shorty = dex_file_->GetMethodShorty(dex_file_->GetMethodId(method_idx),
+                                                      &s_len);
+      DCHECK(shorty != nullptr);
+      uint32_t refs = 1;    // Native method always has "this" or class.
+      for (uint32_t i = 1; i < s_len; ++i) {
+        if (shorty[i] == 'L') {
+          refs++;
+        }
+      }
+      size_t pointer_size = GetInstructionSetPointerSize(
+          writer_->compiler_driver_->GetInstructionSet());
+      size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs);
+
+      // Get the generic spill masks and base frame size.
+      mirror::ArtMethod* callee_save_method =
+          Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
+
+      offsets.frame_size_in_bytes_ = callee_save_method->GetFrameSizeInBytes() + sirt_size;
+      offsets.core_spill_mask_ = callee_save_method->GetCoreSpillMask();
+      offsets.fp_spill_mask_ = callee_save_method->GetFpSpillMask();
+      DCHECK_EQ(offsets.mapping_table_offset_, 0u);
+      DCHECK_EQ(offsets.vmap_table_offset_, 0u);
+      DCHECK_EQ(offsets.gc_map_offset_, 0u);
+    }
+
+    ClassLinker* linker = Runtime::Current()->GetClassLinker();
+    InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_));
+    // Unchecked as we hold mutator_lock_ on entry.
+    ScopedObjectAccessUnchecked soa(Thread::Current());
+    SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(*dex_file_));
+    SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
+    mirror::ArtMethod* method = linker->ResolveMethod(*dex_file_, method_idx, dex_cache,
+                                                      class_loader, nullptr, invoke_type);
+    CHECK(method != NULL);
+    method->SetFrameSizeInBytes(offsets.frame_size_in_bytes_);
+    method->SetCoreSpillMask(offsets.core_spill_mask_);
+    method->SetFpSpillMask(offsets.fp_spill_mask_);
+    method->SetOatMappingTableOffset(offsets.mapping_table_offset_);
+    // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
+    method->SetQuickOatCodeOffset(offsets.code_offset_);
+    method->SetOatVmapTableOffset(offsets.vmap_table_offset_);
+    method->SetOatNativeGcMapOffset(offsets.gc_map_offset_);
+
+    return true;
+  }
+};
+
+class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor {
+ public:
+  WriteCodeMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
+                             size_t relative_offset)
+    : OatDexMethodVisitor(writer, relative_offset),
+      out_(out),
+      file_offset_(file_offset) {
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+    if (compiled_method != NULL) {  // ie. not an abstract method
+      size_t file_offset = file_offset_;
+      OutputStream* out = out_;
+
+      const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
+      if (quick_code != nullptr) {
+        CHECK(compiled_method->GetPortableCode() == nullptr);
+        uint32_t aligned_offset = compiled_method->AlignCode(offset_);
+        uint32_t aligned_code_delta = aligned_offset - offset_;
+        if (aligned_code_delta != 0) {
+          static const uint8_t kPadding[] = {
+              0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u, 0u
+          };
+          DCHECK_LE(aligned_code_delta, sizeof(kPadding));
+          if (UNLIKELY(!out->WriteFully(kPadding, aligned_code_delta))) {
+            ReportWriteFailure("code alignment padding", it);
+            return false;
+          }
+          writer_->size_code_alignment_ += aligned_code_delta;
+          offset_ += aligned_code_delta;
+          DCHECK_OFFSET_();
+        }
+        DCHECK_ALIGNED_PARAM(offset_,
+                             GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
+        uint32_t code_size = quick_code->size() * sizeof(uint8_t);
+        CHECK_NE(code_size, 0U);
+
+        // Deduplicate code arrays.
+        const OatMethodOffsets& method_offsets = oat_class->method_offsets_[method_offsets_index_];
+        DCHECK(method_offsets.code_offset_ < offset_ || method_offsets.code_offset_ ==
+                   offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta())
+            << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+        if (method_offsets.code_offset_ >= offset_) {
+          OatMethodHeader method_header(code_size);
+          if (!out->WriteFully(&method_header, sizeof(method_header))) {
+            ReportWriteFailure("method header", it);
+            return false;
+          }
+          writer_->size_method_header_ += sizeof(method_header);
+          offset_ += sizeof(method_header);
+          DCHECK_OFFSET_();
+          if (!out->WriteFully(&(*quick_code)[0], code_size)) {
+            ReportWriteFailure("method code", it);
+            return false;
+          }
+          writer_->size_code_ += code_size;
+          offset_ += code_size;
+        }
+        DCHECK_OFFSET_();
+      }
+      ++method_offsets_index_;
+    }
+
+    return true;
+  }
+
+ private:
+  OutputStream* const out_;
+  size_t const file_offset_;
+
+  void ReportWriteFailure(const char* what, const ClassDataItemIterator& it) {
+    PLOG(ERROR) << "Failed to write " << what << " for "
+        << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation();
+  }
+};
+
+template <typename DataAccess>
+class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor {
+ public:
+  WriteMapMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset,
+                          size_t relative_offset)
+    : OatDexMethodVisitor(writer, relative_offset),
+      out_(out),
+      file_offset_(file_offset) {
+  }
+
+  bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) {
+    OatClass* oat_class = writer_->oat_classes_[oat_class_index_];
+    const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
+
+    if (compiled_method != NULL) {  // ie. not an abstract method
+      size_t file_offset = file_offset_;
+      OutputStream* out = out_;
+
+      uint32_t map_offset = DataAccess::GetOffset(oat_class, method_offsets_index_);
+      ++method_offsets_index_;
+
+      // Write deduplicated map.
+      const std::vector<uint8_t>* map = DataAccess::GetData(compiled_method);
+      size_t map_size = map->size() * sizeof((*map)[0]);
+      DCHECK((map_size == 0u && map_offset == 0u) ||
+            (map_size != 0u && map_offset != 0u && map_offset <= offset_))
+          << PrettyMethod(it.GetMemberIndex(), *dex_file_);
+      if (map_size != 0u && map_offset == offset_) {
+        if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) {
+          ReportWriteFailure(it);
+          return false;
+        }
+        offset_ += map_size;
+      }
+      DCHECK_OFFSET_();
+    }
+
+    return true;
+  }
+
+ private:
+  OutputStream* const out_;
+  size_t const file_offset_;
+
+  void ReportWriteFailure(const ClassDataItemIterator& it) {
+    PLOG(ERROR) << "Failed to write " << DataAccess::Name() << " for "
+        << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " to " << out_->GetLocation();
+  }
+};
+
+// Visit all methods from all classes in all dex files with the specified visitor.
+bool OatWriter::VisitDexMethods(DexMethodVisitor* visitor) {
+  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) {
+      if (UNLIKELY(!visitor->StartClass(dex_file, class_def_index))) {
+        return false;
+      }
+      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
+      const byte* class_data = dex_file->GetClassData(class_def);
+      if (class_data != NULL) {  // ie not an empty class, such as a marker interface
+        ClassDataItemIterator it(*dex_file, class_data);
+        while (it.HasNextStaticField()) {
+          it.Next();
+        }
+        while (it.HasNextInstanceField()) {
+          it.Next();
+        }
+        size_t class_def_method_index = 0u;
+        while (it.HasNextDirectMethod()) {
+          if (!visitor->VisitMethod(class_def_method_index, it)) {
+            return false;
+          }
+          ++class_def_method_index;
+          it.Next();
+        }
+        while (it.HasNextVirtualMethod()) {
+          if (UNLIKELY(!visitor->VisitMethod(class_def_method_index, it))) {
+            return false;
+          }
+          ++class_def_method_index;
+          it.Next();
+        }
+      }
+      if (UNLIKELY(!visitor->EndClass())) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
 size_t OatWriter::InitOatHeader() {
   // create the OatHeader
   oat_header_ = new OatHeader(compiler_driver_->GetInstructionSet(),
@@ -161,78 +752,42 @@
 }
 
 size_t OatWriter::InitOatClasses(size_t offset) {
-  // create the OatClasses
   // calculate the offsets within OatDexFiles to OatClasses
-  for (size_t i = 0; i != dex_files_->size(); ++i) {
-    const DexFile* dex_file = (*dex_files_)[i];
-    for (size_t class_def_index = 0;
-         class_def_index < dex_file->NumClassDefs();
-         class_def_index++) {
-      oat_dex_files_[i]->methods_offsets_[class_def_index] = offset;
-      const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index);
-      const byte* class_data = dex_file->GetClassData(class_def);
-      uint32_t num_non_null_compiled_methods = 0;
-      UniquePtr<std::vector<CompiledMethod*> > compiled_methods(new std::vector<CompiledMethod*>());
-      if (class_data != NULL) {  // ie not an empty class, such as a marker interface
-        ClassDataItemIterator it(*dex_file, class_data);
-        size_t num_direct_methods = it.NumDirectMethods();
-        size_t num_virtual_methods = it.NumVirtualMethods();
-        size_t num_methods = num_direct_methods + num_virtual_methods;
+  InitOatClassesMethodVisitor visitor(this, offset);
+  bool success = VisitDexMethods(&visitor);
+  CHECK(success);
+  offset = visitor.GetOffset();
 
-        // Fill in the compiled_methods_ array for methods that have a
-        // CompiledMethod. We track the number of non-null entries in
-        // num_non_null_compiled_methods since we only want to allocate
-        // OatMethodOffsets for the compiled methods.
-        compiled_methods->reserve(num_methods);
-        while (it.HasNextStaticField()) {
-          it.Next();
-        }
-        while (it.HasNextInstanceField()) {
-          it.Next();
-        }
-        size_t class_def_method_index = 0;
-        while (it.HasNextDirectMethod()) {
-          uint32_t method_idx = it.GetMemberIndex();
-          CompiledMethod* compiled_method =
-              compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
-          compiled_methods->push_back(compiled_method);
-          if (compiled_method != NULL) {
-              num_non_null_compiled_methods++;
-          }
-          class_def_method_index++;
-          it.Next();
-        }
-        while (it.HasNextVirtualMethod()) {
-          uint32_t method_idx = it.GetMemberIndex();
-          CompiledMethod* compiled_method =
-              compiler_driver_->GetCompiledMethod(MethodReference(dex_file, method_idx));
-          compiled_methods->push_back(compiled_method);
-          if (compiled_method != NULL) {
-              num_non_null_compiled_methods++;
-          }
-          class_def_method_index++;
-          it.Next();
-        }
-      }
-
-      ClassReference class_ref(dex_file, class_def_index);
-      CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref);
-      mirror::Class::Status status;
-      if (compiled_class != NULL) {
-        status = compiled_class->GetStatus();
-      } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
-        status = mirror::Class::kStatusError;
-      } else {
-        status = mirror::Class::kStatusNotReady;
-      }
-
-      OatClass* oat_class = new OatClass(offset, compiled_methods.release(),
-                                         num_non_null_compiled_methods, status);
-      oat_classes_.push_back(oat_class);
-      offset += oat_class->SizeOf();
+  // Update oat_dex_files_.
+  auto oat_class_it = oat_classes_.begin();
+  for (OatDexFile* oat_dex_file : oat_dex_files_) {
+    for (uint32_t& offset : oat_dex_file->methods_offsets_) {
+      DCHECK(oat_class_it != oat_classes_.end());
+      offset = (*oat_class_it)->offset_;
+      ++oat_class_it;
     }
-    oat_dex_files_[i]->UpdateChecksum(oat_header_);
+    oat_dex_file->UpdateChecksum(oat_header_);
   }
+  CHECK(oat_class_it == oat_classes_.end());
+
+  return offset;
+}
+
+size_t OatWriter::InitOatMaps(size_t offset) {
+  #define VISIT(VisitorType)                          \
+    do {                                              \
+      VisitorType visitor(this, offset);              \
+      bool success = VisitDexMethods(&visitor);       \
+      DCHECK(success);                                \
+      offset = visitor.GetOffset();                   \
+    } while (false)
+
+  VISIT(InitMapMethodVisitor<GcMapDataAccess>);
+  VISIT(InitMapMethodVisitor<MappingTableDataAccess>);
+  VISIT(InitMapMethodVisitor<VmapTableDataAccess>);
+
+  #undef VISIT
+
   return offset;
 }
 
@@ -280,280 +835,24 @@
 }
 
 size_t OatWriter::InitOatCodeDexFiles(size_t offset) {
-  size_t oat_class_index = 0;
-  for (size_t i = 0; i != dex_files_->size(); ++i) {
-    const DexFile* dex_file = (*dex_files_)[i];
-    CHECK(dex_file != NULL);
-    offset = InitOatCodeDexFile(offset, &oat_class_index, *dex_file);
-  }
-  return offset;
-}
+  #define VISIT(VisitorType)                          \
+    do {                                              \
+      VisitorType visitor(this, offset);              \
+      bool success = VisitDexMethods(&visitor);       \
+      DCHECK(success);                                \
+      offset = visitor.GetOffset();                   \
+    } while (false)
 
-size_t OatWriter::InitOatCodeDexFile(size_t offset,
-                                     size_t* oat_class_index,
-                                     const DexFile& dex_file) {
-  for (size_t class_def_index = 0;
-       class_def_index < dex_file.NumClassDefs();
-       class_def_index++, (*oat_class_index)++) {
-    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-    offset = InitOatCodeClassDef(offset, *oat_class_index, class_def_index, dex_file, class_def);
-    oat_classes_[*oat_class_index]->UpdateChecksum(oat_header_);
-  }
-  return offset;
-}
-
-size_t OatWriter::InitOatCodeClassDef(size_t offset,
-                                      size_t oat_class_index, size_t class_def_index,
-                                      const DexFile& dex_file,
-                                      const DexFile::ClassDef& class_def) {
-  const byte* class_data = dex_file.GetClassData(class_def);
-  if (class_data == NULL) {
-    // empty class, such as a marker interface
-    return offset;
-  }
-  ClassDataItemIterator it(dex_file, class_data);
-  CHECK_LE(oat_classes_[oat_class_index]->method_offsets_.size(),
-           it.NumDirectMethods() + it.NumVirtualMethods());
-  // Skip fields
-  while (it.HasNextStaticField()) {
-    it.Next();
-  }
-  while (it.HasNextInstanceField()) {
-    it.Next();
-  }
-  // Process methods
-  size_t class_def_method_index = 0;
-  size_t method_offsets_index = 0;
-  while (it.HasNextDirectMethod()) {
-    bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
-    offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
-                               &method_offsets_index, is_native,
-                               it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
-    class_def_method_index++;
-    it.Next();
-  }
-  while (it.HasNextVirtualMethod()) {
-    bool is_native = (it.GetMemberAccessFlags() & kAccNative) != 0;
-    offset = InitOatCodeMethod(offset, oat_class_index, class_def_index, class_def_method_index,
-                               &method_offsets_index, is_native,
-                               it.GetMethodInvokeType(class_def), it.GetMemberIndex(), dex_file);
-    class_def_method_index++;
-    it.Next();
-  }
-  DCHECK(!it.HasNext());
-  CHECK_LE(method_offsets_index, class_def_method_index);
-  return offset;
-}
-
-size_t OatWriter::InitOatCodeMethod(size_t offset, size_t oat_class_index,
-                                    size_t __attribute__((unused)) class_def_index,
-                                    size_t class_def_method_index,
-                                    size_t* method_offsets_index,
-                                    bool __attribute__((unused)) is_native,
-                                    InvokeType invoke_type,
-                                    uint32_t method_idx, const DexFile& dex_file) {
-  // Derived from CompiledMethod if available.
-  uint32_t quick_code_offset = 0;
-  uint32_t frame_size_in_bytes = kStackAlignment;
-  uint32_t core_spill_mask = 0;
-  uint32_t fp_spill_mask = 0;
-  uint32_t mapping_table_offset = 0;
-  uint32_t vmap_table_offset = 0;
-  uint32_t gc_map_offset = 0;
-
-  OatClass* oat_class = oat_classes_[oat_class_index];
-  CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
-
-  if (compiled_method != nullptr) {
-    const std::vector<uint8_t>* portable_code = compiled_method->GetPortableCode();
-    const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
-    if (portable_code != nullptr) {
-      CHECK(quick_code == nullptr);
-      size_t oat_method_offsets_offset =
-          oat_class->GetOatMethodOffsetsOffsetFromOatHeader(class_def_method_index);
-      compiled_method->AddOatdataOffsetToCompliledCodeOffset(
-          oat_method_offsets_offset + OFFSETOF_MEMBER(OatMethodOffsets, code_offset_));
-    } else {
-      CHECK(quick_code != nullptr);
-      offset = compiled_method->AlignCode(offset);
-      DCHECK_ALIGNED_PARAM(offset,
-                           GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
-
-      uint32_t code_size = quick_code->size() * sizeof(uint8_t);
-      CHECK_NE(code_size, 0U);
-      uint32_t thumb_offset = compiled_method->CodeDelta();
-      quick_code_offset = offset + sizeof(code_size) + thumb_offset;
-
-      std::vector<uint8_t>* cfi_info = compiler_driver_->GetCallFrameInformation();
-      if (cfi_info != nullptr) {
-      // Copy in the FDE, if present
-      const std::vector<uint8_t>* fde = compiled_method->GetCFIInfo();
-        if (fde != nullptr) {
-          // Copy the information into cfi_info and then fix the address in the new copy.
-          int cur_offset = cfi_info->size();
-          cfi_info->insert(cfi_info->end(), fde->begin(), fde->end());
-
-          // Set the 'initial_location' field to address the start of the method.
-          uint32_t new_value = quick_code_offset - oat_header_->GetExecutableOffset();
-          uint32_t offset_to_update = cur_offset + 2*sizeof(uint32_t);
-          (*cfi_info)[offset_to_update+0] = new_value;
-          (*cfi_info)[offset_to_update+1] = new_value >> 8;
-          (*cfi_info)[offset_to_update+2] = new_value >> 16;
-          (*cfi_info)[offset_to_update+3] = new_value >> 24;
-          method_info_.push_back(DebugInfo(PrettyMethod(method_idx, dex_file, false),
-                                           new_value, new_value + code_size));
-        }
-      }
-
-      // Deduplicate code arrays
-      SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter =
-          code_offsets_.find(quick_code);
-      if (code_iter != code_offsets_.end()) {
-        quick_code_offset = code_iter->second;
-      } else {
-        code_offsets_.Put(quick_code, quick_code_offset);
-        offset += sizeof(code_size);  // code size is prepended before code
-        offset += code_size;
-        oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
-      }
-    }
-    frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
-    core_spill_mask = compiled_method->GetCoreSpillMask();
-    fp_spill_mask = compiled_method->GetFpSpillMask();
-
-    const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
-    size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]);
-    mapping_table_offset = (mapping_table_size == 0) ? 0 : offset;
-
-    // Deduplicate mapping tables
-    SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter =
-        mapping_table_offsets_.find(&mapping_table);
-    if (mapping_iter != mapping_table_offsets_.end()) {
-      mapping_table_offset = mapping_iter->second;
-    } else {
-      mapping_table_offsets_.Put(&mapping_table, mapping_table_offset);
-      offset += mapping_table_size;
-      oat_header_->UpdateChecksum(&mapping_table[0], mapping_table_size);
-    }
-
-    const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
-    size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]);
-    vmap_table_offset = (vmap_table_size == 0) ? 0 : offset;
-
-    // Deduplicate vmap tables
-    SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter =
-        vmap_table_offsets_.find(&vmap_table);
-    if (vmap_iter != vmap_table_offsets_.end()) {
-      vmap_table_offset = vmap_iter->second;
-    } else {
-      vmap_table_offsets_.Put(&vmap_table, vmap_table_offset);
-      offset += vmap_table_size;
-      oat_header_->UpdateChecksum(&vmap_table[0], vmap_table_size);
-    }
-
-    const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
-    size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
-    gc_map_offset = (gc_map_size == 0) ? 0 : offset;
-
-    if (kIsDebugBuild) {
-      // We expect GC maps except when the class hasn't been verified or the method is native
-      ClassReference class_ref(&dex_file, class_def_index);
-      CompiledClass* compiled_class = compiler_driver_->GetCompiledClass(class_ref);
-      mirror::Class::Status status;
-      if (compiled_class != NULL) {
-        status = compiled_class->GetStatus();
-      } else if (compiler_driver_->GetVerificationResults()->IsClassRejected(class_ref)) {
-        status = mirror::Class::kStatusError;
-      } else {
-        status = mirror::Class::kStatusNotReady;
-      }
-      CHECK(gc_map_size != 0 || is_native || status < mirror::Class::kStatusVerified)
-          << &gc_map << " " << gc_map_size << " " << (is_native ? "true" : "false") << " "
-          << (status < mirror::Class::kStatusVerified) << " " << status << " "
-          << PrettyMethod(method_idx, dex_file);
-    }
-
-    // Deduplicate GC maps
-    SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter =
-        gc_map_offsets_.find(&gc_map);
-    if (gc_map_iter != gc_map_offsets_.end()) {
-      gc_map_offset = gc_map_iter->second;
-    } else {
-      gc_map_offsets_.Put(&gc_map, gc_map_offset);
-      offset += gc_map_size;
-      oat_header_->UpdateChecksum(&gc_map[0], gc_map_size);
-    }
-
-    oat_class->method_offsets_[*method_offsets_index] =
-        OatMethodOffsets(quick_code_offset,
-                         frame_size_in_bytes,
-                         core_spill_mask,
-                         fp_spill_mask,
-                         mapping_table_offset,
-                         vmap_table_offset,
-                         gc_map_offset);
-    (*method_offsets_index)++;
-  }
-
-
+  VISIT(InitCodeMethodVisitor);
   if (compiler_driver_->IsImage()) {
-    // Derive frame size and spill masks for native methods without code:
-    // These are generic JNI methods...
-    if (is_native && compiled_method == nullptr) {
-      // Compute Sirt size as putting _every_ reference into it, even null ones.
-      uint32_t s_len;
-      const char* shorty = dex_file.GetMethodShorty(dex_file.GetMethodId(method_idx), &s_len);
-      DCHECK(shorty != nullptr);
-      uint32_t refs = 1;    // Native method always has "this" or class.
-      for (uint32_t i = 1; i < s_len; ++i) {
-        if (shorty[i] == 'L') {
-          refs++;
-        }
-      }
-      size_t pointer_size = GetInstructionSetPointerSize(compiler_driver_->GetInstructionSet());
-      size_t sirt_size = StackIndirectReferenceTable::GetAlignedSirtSizeTarget(pointer_size, refs);
-
-      // Get the generic spill masks and base frame size.
-      mirror::ArtMethod* callee_save_method =
-          Runtime::Current()->GetCalleeSaveMethod(Runtime::kRefsAndArgs);
-
-      frame_size_in_bytes = callee_save_method->GetFrameSizeInBytes() + sirt_size;
-      core_spill_mask = callee_save_method->GetCoreSpillMask();
-      fp_spill_mask = callee_save_method->GetFpSpillMask();
-      mapping_table_offset = 0;
-      vmap_table_offset = 0;
-      gc_map_offset = 0;
-    }
-
-    ClassLinker* linker = Runtime::Current()->GetClassLinker();
-    // Unchecked as we hold mutator_lock_ on entry.
-    ScopedObjectAccessUnchecked soa(Thread::Current());
-    SirtRef<mirror::DexCache> dex_cache(soa.Self(), linker->FindDexCache(dex_file));
-    SirtRef<mirror::ClassLoader> class_loader(soa.Self(), nullptr);
-    mirror::ArtMethod* method = linker->ResolveMethod(dex_file, method_idx, dex_cache,
-                                                      class_loader, nullptr, invoke_type);
-    CHECK(method != NULL);
-    method->SetFrameSizeInBytes(frame_size_in_bytes);
-    method->SetCoreSpillMask(core_spill_mask);
-    method->SetFpSpillMask(fp_spill_mask);
-    method->SetOatMappingTableOffset(mapping_table_offset);
-    // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking.
-    method->SetQuickOatCodeOffset(quick_code_offset);
-    method->SetOatVmapTableOffset(vmap_table_offset);
-    method->SetOatNativeGcMapOffset(gc_map_offset);
+    VISIT(InitImageMethodVisitor);
   }
 
+  #undef VISIT
+
   return offset;
 }
 
-#define DCHECK_OFFSET() \
-  DCHECK_EQ(static_cast<off_t>(file_offset + relative_offset), out->Seek(0, kSeekCurrent)) \
-    << "file_offset=" << file_offset << " relative_offset=" << relative_offset
-
-#define DCHECK_OFFSET_() \
-  DCHECK_EQ(static_cast<off_t>(file_offset + offset_), out->Seek(0, kSeekCurrent)) \
-    << "file_offset=" << file_offset << " offset_=" << offset_
-
 bool OatWriter::Write(OutputStream* out) {
   const size_t file_offset = out->Seek(0, kSeekCurrent);
 
@@ -574,7 +873,14 @@
     return false;
   }
 
-  size_t relative_offset = WriteCode(out, file_offset);
+  size_t relative_offset = out->Seek(0, kSeekCurrent) - file_offset;
+  relative_offset = WriteMaps(out, file_offset, relative_offset);
+  if (relative_offset == 0) {
+    LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
+    return false;
+  }
+
+  relative_offset = WriteCode(out, file_offset, relative_offset);
   if (relative_offset == 0) {
     LOG(ERROR) << "Failed to write oat code to " << out->GetLocation();
     return false;
@@ -608,7 +914,7 @@
     DO_STAT(size_quick_resolution_trampoline_);
     DO_STAT(size_quick_to_interpreter_bridge_);
     DO_STAT(size_trampoline_alignment_);
-    DO_STAT(size_code_size_);
+    DO_STAT(size_method_header_);
     DO_STAT(size_code_);
     DO_STAT(size_code_alignment_);
     DO_STAT(size_mapping_table_);
@@ -669,9 +975,37 @@
   return true;
 }
 
-size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset) {
-  size_t relative_offset = oat_header_->GetExecutableOffset();
+size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) {
+  #define VISIT(VisitorType)                                              \
+    do {                                                                  \
+      VisitorType visitor(this, out, file_offset, relative_offset);       \
+      if (UNLIKELY(!VisitDexMethods(&visitor))) {                         \
+        return 0;                                                         \
+      }                                                                   \
+      relative_offset = visitor.GetOffset();                              \
+    } while (false)
+
+  size_t gc_maps_offset = relative_offset;
+  VISIT(WriteMapMethodVisitor<GcMapDataAccess>);
+  size_gc_map_ = relative_offset - gc_maps_offset;
+
+  size_t mapping_tables_offset = relative_offset;
+  VISIT(WriteMapMethodVisitor<MappingTableDataAccess>);
+  size_mapping_table_ = relative_offset - mapping_tables_offset;
+
+  size_t vmap_tables_offset = relative_offset;
+  VISIT(WriteMapMethodVisitor<VmapTableDataAccess>);
+  size_vmap_table_ = relative_offset - vmap_tables_offset;
+
+  #undef VISIT
+
+  return relative_offset;
+}
+
+size_t OatWriter::WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset) {
   off_t new_offset = out->Seek(size_executable_offset_alignment_, kSeekCurrent);
+  relative_offset += size_executable_offset_alignment_;
+  DCHECK_EQ(relative_offset, oat_header_->GetExecutableOffset());
   size_t expected_file_offset = file_offset + relative_offset;
   if (static_cast<uint32_t>(new_offset) != expected_file_offset) {
     PLOG(ERROR) << "Failed to seek to oat code section. Actual: " << new_offset
@@ -715,218 +1049,18 @@
 size_t OatWriter::WriteCodeDexFiles(OutputStream* out,
                                     const size_t file_offset,
                                     size_t relative_offset) {
-  size_t oat_class_index = 0;
-  for (size_t i = 0; i != oat_dex_files_.size(); ++i) {
-    const DexFile* dex_file = (*dex_files_)[i];
-    CHECK(dex_file != NULL);
-    relative_offset = WriteCodeDexFile(out, file_offset, relative_offset, &oat_class_index,
-                                       *dex_file);
-    if (relative_offset == 0) {
-      return 0;
-    }
-  }
-  return relative_offset;
-}
+  #define VISIT(VisitorType)                                              \
+    do {                                                                  \
+      VisitorType visitor(this, out, file_offset, relative_offset);       \
+      if (UNLIKELY(!VisitDexMethods(&visitor))) {                         \
+        return 0;                                                         \
+      }                                                                   \
+      relative_offset = visitor.GetOffset();                              \
+    } while (false)
 
-size_t OatWriter::WriteCodeDexFile(OutputStream* out, const size_t file_offset,
-                                   size_t relative_offset, size_t* oat_class_index,
-                                   const DexFile& dex_file) {
-  for (size_t class_def_index = 0; class_def_index < dex_file.NumClassDefs();
-      class_def_index++, (*oat_class_index)++) {
-    const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index);
-    relative_offset = WriteCodeClassDef(out, file_offset, relative_offset, *oat_class_index,
-                                        dex_file, class_def);
-    if (relative_offset == 0) {
-      return 0;
-    }
-  }
-  return relative_offset;
-}
+  VISIT(WriteCodeMethodVisitor);
 
-void OatWriter::ReportWriteFailure(const char* what, uint32_t method_idx,
-                                   const DexFile& dex_file, const OutputStream& out) const {
-  PLOG(ERROR) << "Failed to write " << what << " for " << PrettyMethod(method_idx, dex_file)
-      << " to " << out.GetLocation();
-}
-
-size_t OatWriter::WriteCodeClassDef(OutputStream* out,
-                                    const size_t file_offset,
-                                    size_t relative_offset,
-                                    size_t oat_class_index,
-                                    const DexFile& dex_file,
-                                    const DexFile::ClassDef& class_def) {
-  const byte* class_data = dex_file.GetClassData(class_def);
-  if (class_data == NULL) {
-    // ie. an empty class such as a marker interface
-    return relative_offset;
-  }
-  ClassDataItemIterator it(dex_file, class_data);
-  // Skip fields
-  while (it.HasNextStaticField()) {
-    it.Next();
-  }
-  while (it.HasNextInstanceField()) {
-    it.Next();
-  }
-  // Process methods
-  size_t class_def_method_index = 0;
-  size_t method_offsets_index = 0;
-  while (it.HasNextDirectMethod()) {
-    bool is_static = (it.GetMemberAccessFlags() & kAccStatic) != 0;
-    relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
-                                      class_def_method_index, &method_offsets_index, is_static,
-                                      it.GetMemberIndex(), dex_file);
-    if (relative_offset == 0) {
-      return 0;
-    }
-    class_def_method_index++;
-    it.Next();
-  }
-  while (it.HasNextVirtualMethod()) {
-    relative_offset = WriteCodeMethod(out, file_offset, relative_offset, oat_class_index,
-                                      class_def_method_index, &method_offsets_index, false,
-                                      it.GetMemberIndex(), dex_file);
-    if (relative_offset == 0) {
-      return 0;
-    }
-    class_def_method_index++;
-    it.Next();
-  }
-  DCHECK(!it.HasNext());
-  CHECK_LE(method_offsets_index, class_def_method_index);
-  return relative_offset;
-}
-
-size_t OatWriter::WriteCodeMethod(OutputStream* out, const size_t file_offset,
-                                  size_t relative_offset, size_t oat_class_index,
-                                  size_t class_def_method_index, size_t* method_offsets_index,
-                                  bool is_static, uint32_t method_idx, const DexFile& dex_file) {
-  OatClass* oat_class = oat_classes_[oat_class_index];
-  const CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index);
-
-  if (compiled_method != NULL) {  // ie. not an abstract method
-    const OatMethodOffsets method_offsets = oat_class->method_offsets_[*method_offsets_index];
-    (*method_offsets_index)++;
-    const std::vector<uint8_t>* quick_code = compiled_method->GetQuickCode();
-    if (quick_code != nullptr) {
-      CHECK(compiled_method->GetPortableCode() == nullptr);
-      uint32_t aligned_offset = compiled_method->AlignCode(relative_offset);
-      uint32_t aligned_code_delta = aligned_offset - relative_offset;
-      if (aligned_code_delta != 0) {
-        off_t new_offset = out->Seek(aligned_code_delta, kSeekCurrent);
-        size_code_alignment_ += aligned_code_delta;
-        uint32_t expected_offset = file_offset + aligned_offset;
-        if (static_cast<uint32_t>(new_offset) != expected_offset) {
-          PLOG(ERROR) << "Failed to seek to align oat code. Actual: " << new_offset
-              << " Expected: " << expected_offset << " File: " << out->GetLocation();
-          return 0;
-        }
-        relative_offset += aligned_code_delta;
-        DCHECK_OFFSET();
-      }
-      DCHECK_ALIGNED_PARAM(relative_offset,
-                           GetInstructionSetAlignment(compiled_method->GetInstructionSet()));
-
-      uint32_t code_size = quick_code->size() * sizeof(uint8_t);
-      CHECK_NE(code_size, 0U);
-
-      // Deduplicate code arrays
-      size_t code_offset = relative_offset + sizeof(code_size) + compiled_method->CodeDelta();
-      SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator code_iter =
-          code_offsets_.find(quick_code);
-      if (code_iter != code_offsets_.end() && code_offset != method_offsets.code_offset_) {
-        DCHECK(code_iter->second == method_offsets.code_offset_)
-              << PrettyMethod(method_idx, dex_file);
-      } else {
-        DCHECK(code_offset == method_offsets.code_offset_) << PrettyMethod(method_idx, dex_file);
-        if (!out->WriteFully(&code_size, sizeof(code_size))) {
-          ReportWriteFailure("method code size", method_idx, dex_file, *out);
-          return 0;
-        }
-        size_code_size_ += sizeof(code_size);
-        relative_offset += sizeof(code_size);
-        DCHECK_OFFSET();
-        if (!out->WriteFully(&(*quick_code)[0], code_size)) {
-          ReportWriteFailure("method code", method_idx, dex_file, *out);
-          return 0;
-        }
-        size_code_ += code_size;
-        relative_offset += code_size;
-      }
-      DCHECK_OFFSET();
-    }
-    const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
-    size_t mapping_table_size = mapping_table.size() * sizeof(mapping_table[0]);
-
-    // Deduplicate mapping tables
-    SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator mapping_iter =
-        mapping_table_offsets_.find(&mapping_table);
-    if (mapping_iter != mapping_table_offsets_.end() &&
-        relative_offset != method_offsets.mapping_table_offset_) {
-      DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0)
-          || mapping_iter->second == method_offsets.mapping_table_offset_)
-          << PrettyMethod(method_idx, dex_file);
-    } else {
-      DCHECK((mapping_table_size == 0 && method_offsets.mapping_table_offset_ == 0)
-          || relative_offset == method_offsets.mapping_table_offset_)
-          << PrettyMethod(method_idx, dex_file);
-      if (!out->WriteFully(&mapping_table[0], mapping_table_size)) {
-        ReportWriteFailure("mapping table", method_idx, dex_file, *out);
-        return 0;
-      }
-      size_mapping_table_ += mapping_table_size;
-      relative_offset += mapping_table_size;
-    }
-    DCHECK_OFFSET();
-
-    const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
-    size_t vmap_table_size = vmap_table.size() * sizeof(vmap_table[0]);
-
-    // Deduplicate vmap tables
-    SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator vmap_iter =
-        vmap_table_offsets_.find(&vmap_table);
-    if (vmap_iter != vmap_table_offsets_.end() &&
-        relative_offset != method_offsets.vmap_table_offset_) {
-      DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0)
-          || vmap_iter->second == method_offsets.vmap_table_offset_)
-          << PrettyMethod(method_idx, dex_file);
-    } else {
-      DCHECK((vmap_table_size == 0 && method_offsets.vmap_table_offset_ == 0)
-          || relative_offset == method_offsets.vmap_table_offset_)
-          << PrettyMethod(method_idx, dex_file);
-      if (!out->WriteFully(&vmap_table[0], vmap_table_size)) {
-        ReportWriteFailure("vmap table", method_idx, dex_file, *out);
-        return 0;
-      }
-      size_vmap_table_ += vmap_table_size;
-      relative_offset += vmap_table_size;
-    }
-    DCHECK_OFFSET();
-
-    const std::vector<uint8_t>& gc_map = compiled_method->GetGcMap();
-    size_t gc_map_size = gc_map.size() * sizeof(gc_map[0]);
-
-    // Deduplicate GC maps
-    SafeMap<const std::vector<uint8_t>*, uint32_t>::iterator gc_map_iter =
-        gc_map_offsets_.find(&gc_map);
-    if (gc_map_iter != gc_map_offsets_.end() &&
-        relative_offset != method_offsets.gc_map_offset_) {
-      DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0)
-          || gc_map_iter->second == method_offsets.gc_map_offset_)
-          << PrettyMethod(method_idx, dex_file);
-    } else {
-      DCHECK((gc_map_size == 0 && method_offsets.gc_map_offset_ == 0)
-          || relative_offset == method_offsets.gc_map_offset_)
-          << PrettyMethod(method_idx, dex_file);
-      if (!out->WriteFully(&gc_map[0], gc_map_size)) {
-        ReportWriteFailure("GC map", method_idx, dex_file, *out);
-        return 0;
-      }
-      size_gc_map_ += gc_map_size;
-      relative_offset += gc_map_size;
-    }
-    DCHECK_OFFSET();
-  }
+  #undef VISIT
 
   return relative_offset;
 }
@@ -993,15 +1127,14 @@
 }
 
 OatWriter::OatClass::OatClass(size_t offset,
-                              std::vector<CompiledMethod*>* compiled_methods,
+                              const std::vector<CompiledMethod*>& compiled_methods,
                               uint32_t num_non_null_compiled_methods,
-                              mirror::Class::Status status) {
-  CHECK(compiled_methods !=  NULL);
-  uint32_t num_methods = compiled_methods->size();
+                              mirror::Class::Status status)
+    : compiled_methods_(compiled_methods) {
+  uint32_t num_methods = compiled_methods.size();
   CHECK_LE(num_non_null_compiled_methods, num_methods);
 
   offset_ = offset;
-  compiled_methods_ = compiled_methods;
   oat_method_offsets_offsets_from_oat_class_.resize(num_methods);
 
   // Since both kOatClassNoneCompiled and kOatClassAllCompiled could
@@ -1033,7 +1166,7 @@
   }
 
   for (size_t i = 0; i < num_methods; i++) {
-    CompiledMethod* compiled_method = (*compiled_methods_)[i];
+    CompiledMethod* compiled_method = compiled_methods_[i];
     if (compiled_method == NULL) {
       oat_method_offsets_offsets_from_oat_class_[i] = 0;
     } else {
@@ -1048,7 +1181,6 @@
 
 OatWriter::OatClass::~OatClass() {
   delete method_bitmap_;
-  delete compiled_methods_;
 }
 
 size_t OatWriter::OatClass::GetOatMethodOffsetsOffsetFromOatHeader(