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(
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index bab1a26..1abacd8 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -50,16 +50,30 @@
 // ...
 // OatClass[C]
 //
+// GcMap             one variable sized blob with GC map.
+// GcMap             GC maps are deduplicated.
+// ...
+// GcMap
+//
+// VmapTable         one variable sized VmapTable blob (quick compiler only).
+// VmapTable         VmapTables are deduplicated.
+// ...
+// VmapTable
+//
+// MappingTable      one variable sized blob with MappingTable (quick compiler only).
+// MappingTable      MappingTables are deduplicated.
+// ...
+// MappingTable
+//
 // padding           if necessary so that the following code will be page aligned
 //
-// CompiledMethod    one variable sized blob with the contents of each CompiledMethod
-// CompiledMethod
-// CompiledMethod
-// CompiledMethod
-// CompiledMethod
-// CompiledMethod
+// OatMethodHeader   fixed size header for a CompiledMethod including the size of the MethodCode.
+// MethodCode        one variable sized blob with the code of a CompiledMethod.
+// OatMethodHeader   (OatMethodHeader, MethodCode) pairs are deduplicated.
+// MethodCode
 // ...
-// CompiledMethod
+// OatMethodHeader
+// MethodCode
 //
 class OatWriter {
  public:
@@ -96,43 +110,47 @@
   }
 
  private:
+  // The DataAccess classes are helper classes that provide access to members related to
+  // a given map, i.e. GC map, mapping table or vmap table. By abstracting these away
+  // we can share a lot of code for processing the maps with template classes below.
+  struct GcMapDataAccess;
+  struct MappingTableDataAccess;
+  struct VmapTableDataAccess;
+
+  // The function VisitDexMethods() below iterates through all the methods in all
+  // the compiled dex files in order of their definitions. The method visitor
+  // classes provide individual bits of processing for each of the passes we need to
+  // first collect the data we want to write to the oat file and then, in later passes,
+  // to actually write it.
+  class DexMethodVisitor;
+  class OatDexMethodVisitor;
+  class InitOatClassesMethodVisitor;
+  class InitCodeMethodVisitor;
+  template <typename DataAccess>
+  class InitMapMethodVisitor;
+  class InitImageMethodVisitor;
+  class WriteCodeMethodVisitor;
+  template <typename DataAccess>
+  class WriteMapMethodVisitor;
+
+  // Visit all the methods in all the compiled dex files in their definition order
+  // with a given DexMethodVisitor.
+  bool VisitDexMethods(DexMethodVisitor* visitor);
+
   size_t InitOatHeader();
   size_t InitOatDexFiles(size_t offset);
   size_t InitDexFiles(size_t offset);
   size_t InitOatClasses(size_t offset);
+  size_t InitOatMaps(size_t offset);
   size_t InitOatCode(size_t offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
   size_t InitOatCodeDexFiles(size_t offset)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  size_t InitOatCodeDexFile(size_t offset,
-                            size_t* oat_class_index,
-                            const DexFile& dex_file)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  size_t InitOatCodeClassDef(size_t offset,
-                             size_t oat_class_index, size_t class_def_index,
-                             const DexFile& dex_file,
-                             const DexFile::ClassDef& class_def)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
-  size_t InitOatCodeMethod(size_t offset, size_t oat_class_index, size_t class_def_index,
-                           size_t class_def_method_index, size_t* method_offsets_index,
-                           bool is_native, InvokeType type, uint32_t method_idx, const DexFile&)
-      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
   bool WriteTables(OutputStream* out, const size_t file_offset);
-  size_t WriteCode(OutputStream* out, const size_t file_offset);
+  size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset);
+  size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset);
   size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset);
-  size_t WriteCodeDexFile(OutputStream* out, const size_t file_offset, size_t relative_offset,
-                          size_t* oat_class_index, const DexFile& dex_file);
-  size_t 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);
-  size_t 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);
-
-  void ReportWriteFailure(const char* what, uint32_t method_idx, const DexFile& dex_file,
-                          const OutputStream& out) const;
 
   class OatDexFile {
    public:
@@ -159,7 +177,7 @@
   class OatClass {
    public:
     explicit 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);
     ~OatClass();
@@ -170,8 +188,8 @@
     bool Write(OatWriter* oat_writer, OutputStream* out, const size_t file_offset) const;
 
     CompiledMethod* GetCompiledMethod(size_t class_def_method_index) const {
-      DCHECK(compiled_methods_ != NULL);
-      return (*compiled_methods_)[class_def_method_index];
+      DCHECK_LT(class_def_method_index, compiled_methods_.size());
+      return compiled_methods_[class_def_method_index];
     }
 
     // Offset of start of OatClass from beginning of OatHeader. It is
@@ -182,7 +200,7 @@
     size_t offset_;
 
     // CompiledMethods for each class_def_method_index, or NULL if no method is available.
-    std::vector<CompiledMethod*>* compiled_methods_;
+    std::vector<CompiledMethod*> compiled_methods_;
 
     // Offset from OatClass::offset_ to the OatMethodOffsets for the
     // class_def_method_index. If 0, it means the corresponding
@@ -265,7 +283,7 @@
   uint32_t size_quick_resolution_trampoline_;
   uint32_t size_quick_to_interpreter_bridge_;
   uint32_t size_trampoline_alignment_;
-  uint32_t size_code_size_;
+  uint32_t size_method_header_;
   uint32_t size_code_;
   uint32_t size_code_alignment_;
   uint32_t size_mapping_table_;
@@ -281,13 +299,6 @@
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
 
-  // Code mappings for deduplication. 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> code_offsets_;
-  SafeMap<const std::vector<uint8_t>*, uint32_t> vmap_table_offsets_;
-  SafeMap<const std::vector<uint8_t>*, uint32_t> mapping_table_offsets_;
-  SafeMap<const std::vector<uint8_t>*, uint32_t> gc_map_offsets_;
-
   DISALLOW_COPY_AND_ASSIGN(OatWriter);
 };
 
diff --git a/runtime/mirror/art_method-inl.h b/runtime/mirror/art_method-inl.h
index d5eccaf..5d62b88 100644
--- a/runtime/mirror/art_method-inl.h
+++ b/runtime/mirror/art_method-inl.h
@@ -22,6 +22,7 @@
 #include "dex_file.h"
 #include "entrypoints/entrypoint_utils.h"
 #include "object_array.h"
+#include "oat.h"
 #include "runtime.h"
 
 namespace art {
@@ -83,7 +84,7 @@
   }
   // TODO: make this Thumb2 specific
   code &= ~0x1;
-  return reinterpret_cast<uint32_t*>(code)[-1];
+  return reinterpret_cast<OatMethodHeader*>(code)[-1].code_size_;
 }
 
 inline bool ArtMethod::CheckIncompatibleClassChange(InvokeType type) {
diff --git a/runtime/mirror/art_method.h b/runtime/mirror/art_method.h
index fd5ac19..ee23c40 100644
--- a/runtime/mirror/art_method.h
+++ b/runtime/mirror/art_method.h
@@ -270,9 +270,11 @@
       return pc == 0;
     }
     /*
-     * During a stack walk, a return PC may point to the end of the code + 1
-     * (in the case that the last instruction is a call that isn't expected to
+     * During a stack walk, a return PC may point past-the-end of the code
+     * in the case that the last instruction is a call that isn't expected to
      * return.  Thus, we check <= code + GetCodeSize().
+     *
+     * NOTE: For Thumb both pc and code are offset by 1 indicating the Thumb state.
      */
     return (code <= pc && pc <= code + GetCodeSize());
   }
diff --git a/runtime/oat.cc b/runtime/oat.cc
index 246e090..d01dc72b 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -22,7 +22,7 @@
 namespace art {
 
 const uint8_t OatHeader::kOatMagic[] = { 'o', 'a', 't', '\n' };
-const uint8_t OatHeader::kOatVersion[] = { '0', '2', '0', '\0' };
+const uint8_t OatHeader::kOatVersion[] = { '0', '2', '1', '\0' };
 
 OatHeader::OatHeader() {
   memset(this, 0, sizeof(*this));
@@ -372,4 +372,14 @@
 
 OatMethodOffsets::~OatMethodOffsets() {}
 
+OatMethodHeader::OatMethodHeader()
+  : code_size_(0)
+{}
+
+OatMethodHeader::OatMethodHeader(uint32_t code_size)
+  : code_size_(code_size)
+{}
+
+OatMethodHeader::~OatMethodHeader() {}
+
 }  // namespace art
diff --git a/runtime/oat.h b/runtime/oat.h
index 2851f5c..035aba1 100644
--- a/runtime/oat.h
+++ b/runtime/oat.h
@@ -155,6 +155,19 @@
   uint32_t gc_map_offset_;
 };
 
+// OatMethodHeader precedes the raw code chunk generated by the Quick compiler.
+class PACKED(4) OatMethodHeader {
+ public:
+  OatMethodHeader();
+
+  explicit OatMethodHeader(uint32_t code_size);
+
+  ~OatMethodHeader();
+
+  // The code size in bytes.
+  uint32_t code_size_;
+};
+
 }  // namespace art
 
 #endif  // ART_RUNTIME_OAT_H_