Move mapping table and vmap table offsets to OatMethodHeader.

This change has a libcore/ companion CL
  "Remove ArtMethod's quick fields mapping table and vmap table."
  https://android-review.googlesource.com/91254

Bug: 11767815
Change-Id: I46ce2067e1ecd915da3890606498e31ffc332813
diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h
index fdf09a5..8bba84a 100644
--- a/compiler/common_compiler_test.h
+++ b/compiler/common_compiler_test.h
@@ -132,34 +132,24 @@
 
 class CommonCompilerTest : public CommonRuntimeTest {
  public:
-  static void MakeExecutable(const std::vector<uint8_t>& code) {
-    CHECK_NE(code.size(), 0U);
-    MakeExecutable(&code[0], code.size());
-  }
-
   // Create an OatMethod based on pointers (for unit tests).
   OatFile::OatMethod CreateOatMethod(const void* code,
                                      const size_t frame_size_in_bytes,
                                      const uint32_t core_spill_mask,
                                      const uint32_t fp_spill_mask,
-                                     const uint8_t* mapping_table,
-                                     const uint8_t* vmap_table,
                                      const uint8_t* gc_map) {
+    CHECK(code != nullptr);
     const byte* base;
-    uint32_t code_offset, mapping_table_offset, vmap_table_offset, gc_map_offset;
-    if (mapping_table == nullptr && vmap_table == nullptr && gc_map == nullptr) {
+    uint32_t code_offset, gc_map_offset;
+    if (gc_map == nullptr) {
       base = reinterpret_cast<const byte*>(code);  // Base of data points at code.
       base -= kPointerSize;  // Move backward so that code_offset != 0.
       code_offset = kPointerSize;
-      mapping_table_offset = 0;
-      vmap_table_offset = 0;
       gc_map_offset = 0;
     } else {
       // TODO: 64bit support.
       base = nullptr;  // Base of data in oat file, ie 0.
       code_offset = PointerToLowMemUInt32(code);
-      mapping_table_offset = PointerToLowMemUInt32(mapping_table);
-      vmap_table_offset = PointerToLowMemUInt32(vmap_table);
       gc_map_offset = PointerToLowMemUInt32(gc_map);
     }
     return OatFile::OatMethod(base,
@@ -167,8 +157,6 @@
                               frame_size_in_bytes,
                               core_spill_mask,
                               fp_spill_mask,
-                              mapping_table_offset,
-                              vmap_table_offset,
                               gc_map_offset);
   }
 
@@ -185,19 +173,44 @@
     }
     if (compiled_method != nullptr) {
       const std::vector<uint8_t>* code = compiled_method->GetQuickCode();
-      if (code == nullptr) {
+      const void* code_ptr;
+      if (code != nullptr) {
+        uint32_t code_size = code->size();
+        CHECK_NE(0u, code_size);
+        const std::vector<uint8_t>& vmap_table = compiled_method->GetVmapTable();
+        uint32_t vmap_table_offset = vmap_table.empty() ? 0u
+            : sizeof(OatMethodHeader) + vmap_table.size();
+        const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable();
+        uint32_t mapping_table_offset = mapping_table.empty() ? 0u
+            : sizeof(OatMethodHeader) + vmap_table.size() + mapping_table.size();
+        OatMethodHeader method_header(vmap_table_offset, mapping_table_offset, code_size);
+
+        header_code_and_maps_chunks_.push_back(std::vector<uint8_t>());
+        std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back();
+        size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size();
+        size_t code_offset = compiled_method->AlignCode(size - code_size);
+        size_t padding = code_offset - (size - code_size);
+        chunk->reserve(padding + size);
+        chunk->resize(sizeof(method_header));
+        memcpy(&(*chunk)[0], &method_header, sizeof(method_header));
+        chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end());
+        chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end());
+        chunk->insert(chunk->begin(), padding, 0);
+        chunk->insert(chunk->end(), code->begin(), code->end());
+        CHECK_EQ(padding + size, chunk->size());
+        code_ptr = &(*chunk)[code_offset];
+      } else {
         code = compiled_method->GetPortableCode();
+        code_ptr = &(*code)[0];
       }
-      MakeExecutable(*code);
-      const void* method_code = CompiledMethod::CodePointer(&(*code)[0],
+      MakeExecutable(code_ptr, code->size());
+      const void* method_code = CompiledMethod::CodePointer(code_ptr,
                                                             compiled_method->GetInstructionSet());
       LOG(INFO) << "MakeExecutable " << PrettyMethod(method) << " code=" << method_code;
       OatFile::OatMethod oat_method = CreateOatMethod(method_code,
                                                       compiled_method->GetFrameSizeInBytes(),
                                                       compiled_method->GetCoreSpillMask(),
                                                       compiled_method->GetFpSpillMask(),
-                                                      &compiled_method->GetMappingTable()[0],
-                                                      &compiled_method->GetVmapTable()[0],
                                                       nullptr);
       oat_method.LinkMethod(method);
       method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -211,8 +224,6 @@
                                                         kStackAlignment,
                                                         0,
                                                         0,
-                                                        nullptr,
-                                                        nullptr,
                                                         nullptr);
         oat_method.LinkMethod(method);
         method->SetEntryPointFromInterpreter(interpreter::artInterpreterToInterpreterBridge);
@@ -230,8 +241,6 @@
                                                             sirt_size,
                                                         callee_save_method->GetCoreSpillMask(),
                                                         callee_save_method->GetFpSpillMask(),
-                                                        nullptr,
-                                                        nullptr,
                                                         nullptr);
         oat_method.LinkMethod(method);
         method->SetEntryPointFromInterpreter(artInterpreterToCompiledCodeBridge);
@@ -436,6 +445,9 @@
 
  private:
   UniquePtr<MemMap> image_reservation_;
+
+  // Chunks must not move their storage after being created - use the node-based std::list.
+  std::list<std::vector<uint8_t> > header_code_and_maps_chunks_;
 };
 
 }  // namespace art
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index 3400b01..c35d400 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -680,14 +680,6 @@
         copy->SetNativeMethod<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_));
       } else {
         // Normal (non-abstract non-native) methods have various tables to relocate.
-        uint32_t mapping_table_off = orig->GetOatMappingTableOffset();
-        const byte* mapping_table = GetOatAddress(mapping_table_off);
-        copy->SetMappingTable<kVerifyNone>(mapping_table);
-
-        uint32_t vmap_table_offset = orig->GetOatVmapTableOffset();
-        const byte* vmap_table = GetOatAddress(vmap_table_offset);
-        copy->SetVmapTable<kVerifyNone>(vmap_table);
-
         uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset();
         const byte* native_gc_map = GetOatAddress(native_gc_map_offset);
         copy->SetNativeGcMap<kVerifyNone>(reinterpret_cast<const uint8_t*>(native_gc_map));
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 766ef7b..b5d3923 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -176,7 +176,8 @@
   // If this test is failing and you have to update these constants,
   // it is time to update OatHeader::kOatVersion
   EXPECT_EQ(80U, sizeof(OatHeader));
-  EXPECT_EQ(28U, sizeof(OatMethodOffsets));
+  EXPECT_EQ(20U, sizeof(OatMethodOffsets));
+  EXPECT_EQ(12U, sizeof(OatMethodHeader));
 }
 
 TEST_F(OatTest, OatHeaderIsValid) {
diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc
index 2114fe9..bbc9c3e 100644
--- a/compiler/oat_writer.cc
+++ b/compiler/oat_writer.cc
@@ -155,12 +155,15 @@
   }
 
   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_;
+    uint32_t offset = oat_class->method_headers_[method_offsets_index].mapping_table_offset_;
+    return offset == 0u ? 0u :
+        (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - 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;
+    oat_class->method_headers_[method_offsets_index].mapping_table_offset_ =
+        (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
   }
 
   static const char* Name() ALWAYS_INLINE {
@@ -174,12 +177,15 @@
   }
 
   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_;
+    uint32_t offset = oat_class->method_headers_[method_offsets_index].vmap_table_offset_;
+    return offset == 0u ? 0u :
+        (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - 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;
+    oat_class->method_headers_[method_offsets_index].vmap_table_offset_ =
+        (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset;
   }
 
   static const char* Name() ALWAYS_INLINE {
@@ -368,17 +374,22 @@
           }
         }
 
+        DCHECK_LT(method_offsets_index_, oat_class->method_headers_.size());
+        OatMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_];
+        method_header->code_size_ = code_size;
+
         // Deduplicate code arrays.
-        auto code_iter = dedupe_map_.find(quick_code);
+        auto code_iter = dedupe_map_.find(compiled_method);
         if (code_iter != dedupe_map_.end()) {
           quick_code_offset = code_iter->second;
+          FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
         } 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;
+          dedupe_map_.Put(compiled_method, quick_code_offset);
+          FixupMethodHeader(method_header, quick_code_offset - thumb_offset);
+          writer_->oat_header_->UpdateChecksum(method_header, sizeof(*method_header));
+          offset_ += sizeof(*method_header);  // Method header is prepended before code.
           writer_->oat_header_->UpdateChecksum(&(*quick_code)[0], code_size);
+          offset_ += code_size;
         }
       }
       frame_size_in_bytes = compiled_method->GetFrameSizeInBytes();
@@ -420,9 +431,22 @@
   }
 
  private:
+  static void FixupMethodHeader(OatMethodHeader* method_header, uint32_t code_offset) {
+    // The code offset was 0 when the mapping/vmap table offset was set, so it's set
+    // to 0-offset and we need to adjust it by code_offset.
+    if (method_header->mapping_table_offset_ != 0u) {
+      method_header->mapping_table_offset_ += code_offset;
+      DCHECK_LT(method_header->mapping_table_offset_, code_offset);
+    }
+    if (method_header->vmap_table_offset_ != 0u) {
+      method_header->vmap_table_offset_ += code_offset;
+      DCHECK_LT(method_header->vmap_table_offset_, code_offset);
+    }
+  }
+
   // 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_;
+  SafeMap<const CompiledMethod*, uint32_t, CodeOffsetsKeyComparator> dedupe_map_;
 };
 
 template <typename DataAccess>
@@ -477,7 +501,7 @@
     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);
+    OatMethodOffsets offsets(0u, kStackAlignment, 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_];
@@ -511,8 +535,6 @@
       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);
     }
 
@@ -528,10 +550,8 @@
     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;
@@ -584,7 +604,7 @@
                    offset_ + sizeof(OatMethodHeader) + compiled_method->CodeDelta())
             << PrettyMethod(it.GetMemberIndex(), *dex_file_);
         if (method_offsets.code_offset_ >= offset_) {
-          OatMethodHeader method_header(code_size);
+          const OatMethodHeader& method_header = oat_class->method_headers_[method_offsets_index_];
           if (!out->WriteFully(&method_header, sizeof(method_header))) {
             ReportWriteFailure("method header", it);
             return false;
@@ -1153,6 +1173,7 @@
 
   status_ = status;
   method_offsets_.resize(num_non_null_compiled_methods);
+  method_headers_.resize(num_non_null_compiled_methods);
 
   uint32_t oat_method_offsets_offset_from_oat_class = sizeof(type_) + sizeof(status_);
   if (type_ == kOatClassSomeCompiled) {
diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h
index 1abacd8..7cdd532 100644
--- a/compiler/oat_writer.h
+++ b/compiler/oat_writer.h
@@ -225,12 +225,13 @@
     // not is kOatClassBitmap, the bitmap will be NULL.
     BitVector* method_bitmap_;
 
-    // OatMethodOffsets for each CompiledMethod present in the
-    // OatClass. Note that some may be missing if
+    // OatMethodOffsets and OatMethodHeaders for each CompiledMethod
+    // present in the OatClass. Note that some may be missing if
     // OatClass::compiled_methods_ contains NULL values (and
     // oat_method_offsets_offsets_from_oat_class_ should contain 0
     // values in this case).
     std::vector<OatMethodOffsets> method_offsets_;
+    std::vector<OatMethodHeader> method_headers_;
 
    private:
     DISALLOW_COPY_AND_ASSIGN(OatClass);
@@ -299,6 +300,22 @@
   uint32_t size_oat_class_method_bitmaps_;
   uint32_t size_oat_class_method_offsets_;
 
+  struct CodeOffsetsKeyComparator {
+    bool operator()(const CompiledMethod* lhs, const CompiledMethod* rhs) const {
+      if (lhs->GetQuickCode() != rhs->GetQuickCode()) {
+        return lhs->GetQuickCode() < rhs->GetQuickCode();
+      }
+      // If the code is the same, all other fields are likely to be the same as well.
+      if (UNLIKELY(&lhs->GetMappingTable() != &rhs->GetMappingTable())) {
+        return &lhs->GetMappingTable() < &rhs->GetMappingTable();
+      }
+      if (UNLIKELY(&lhs->GetVmapTable() != &rhs->GetVmapTable())) {
+        return &lhs->GetVmapTable() < &rhs->GetVmapTable();
+      }
+      return false;
+    }
+  };
+
   DISALLOW_COPY_AND_ASSIGN(OatWriter);
 };