Merge "Separate maps from code in oat file."
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 38e44be..d684266 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..d01dc72 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_