Undo "Don't include an object header for instructions in the text section."

This undoes most of b2f3e8efe1f32bacad99ddbedbc434a6102d8be2.

This optimization prevents precompiled code from being disabled and re-enabled, because disabling causes the code to lose its only reference to its own instructions. This was okay for precompilated code that ran without a JIT because it is never disabled, but precompiled code that runs in a JIT will become disabled when corresponding optimized code is compiled and re-abled after a deopt.

R=fschneider@google.com

Review URL: https://codereview.chromium.org/1925153003 .
diff --git a/runtime/vm/dart.cc b/runtime/vm/dart.cc
index 3fe6df5..cd7dbde 100644
--- a/runtime/vm/dart.cc
+++ b/runtime/vm/dart.cc
@@ -207,6 +207,9 @@
     if (vm_isolate_snapshot != NULL) {
       NOT_IN_PRODUCT(TimelineDurationScope tds(Timeline::GetVMStream(),
                                                "VMIsolateSnapshot"));
+      if (instructions_snapshot != NULL) {
+        vm_isolate_->SetupInstructionsSnapshotPage(instructions_snapshot);
+      }
       if (data_snapshot != NULL) {
         vm_isolate_->SetupDataSnapshotPage(data_snapshot);
       }
diff --git a/runtime/vm/isolate.cc b/runtime/vm/isolate.cc
index dc52613..44127c5 100644
--- a/runtime/vm/isolate.cc
+++ b/runtime/vm/isolate.cc
@@ -934,6 +934,23 @@
 }
 
 
+void Isolate::SetupInstructionsSnapshotPage(
+    const uint8_t* instructions_snapshot_buffer) {
+  InstructionsSnapshot snapshot(instructions_snapshot_buffer);
+#if defined(DEBUG)
+  if (FLAG_trace_isolates) {
+    OS::Print("Precompiled instructions are at [0x%" Px ", 0x%" Px ")\n",
+              reinterpret_cast<uword>(snapshot.instructions_start()),
+              reinterpret_cast<uword>(snapshot.instructions_start()) +
+              snapshot.instructions_size());
+  }
+#endif
+  heap_->SetupExternalPage(snapshot.instructions_start(),
+                           snapshot.instructions_size(),
+                           /* is_executable = */ true);
+}
+
+
 void Isolate::SetupDataSnapshotPage(const uint8_t* data_snapshot_buffer) {
   DataSnapshot snapshot(data_snapshot_buffer);
 #if defined(DEBUG)
diff --git a/runtime/vm/isolate.h b/runtime/vm/isolate.h
index 8061619..c2fc955 100644
--- a/runtime/vm/isolate.h
+++ b/runtime/vm/isolate.h
@@ -228,6 +228,8 @@
     library_tag_handler_ = value;
   }
 
+  void SetupInstructionsSnapshotPage(
+      const uint8_t* instructions_snapshot_buffer);
   void SetupDataSnapshotPage(
       const uint8_t* instructions_snapshot_buffer);
 
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 7d8acc3..6f48c92 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -13525,26 +13525,6 @@
 }
 
 
-uword Code::EntryPoint() const {
-  RawObject* instr = instructions();
-  if (!instr->IsHeapObject()) {
-    return active_entry_point();
-  } else {
-    return Instructions::EntryPoint(instructions());
-  }
-}
-
-
-intptr_t Code::Size() const {
-  RawObject* instr = instructions();
-  if (!instr->IsHeapObject()) {
-    return Smi::Value(raw_ptr()->precompiled_instructions_size_);
-  } else {
-    return instructions()->ptr()->size_;
-  }
-}
-
-
 bool Code::HasBreakpoint() const {
   if (!FLAG_support_debugger) {
     return false;
@@ -13890,13 +13870,10 @@
     }
 
     // Hook up Code and Instructions objects.
-    code.set_instructions(instrs.raw());
     code.SetActiveInstructions(instrs.raw());
+    code.set_instructions(instrs.raw());
     code.set_is_alive(true);
 
-    ASSERT(code.EntryPoint() == instrs.EntryPoint());
-    ASSERT(code.Size() == instrs.size());
-
     // Set object pool in Instructions object.
     INC_STAT(Thread::Current(),
              total_code_size, object_pool.Length() * sizeof(uintptr_t));
@@ -14099,10 +14076,9 @@
 void Code::DisableDartCode() const {
   DEBUG_ASSERT(IsMutatorOrAtSafepoint());
   ASSERT(IsFunctionCode());
-  ASSERT(!IsDisabled());
+  ASSERT(instructions() == active_instructions());
   const Code& new_code =
       Code::Handle(StubCode::FixCallersTarget_entry()->code());
-  ASSERT(new_code.instructions()->IsVMHeapObject());
   SetActiveInstructions(new_code.instructions());
 }
 
@@ -14111,10 +14087,9 @@
 #if !defined(TARGET_ARCH_DBC)
   ASSERT(Thread::Current()->IsMutatorThread());
   ASSERT(IsAllocationStubCode());
-  ASSERT(!IsDisabled());
+  ASSERT(instructions() == active_instructions());
   const Code& new_code =
       Code::Handle(StubCode::FixAllocationStubTarget_entry()->code());
-  ASSERT(new_code.instructions()->IsVMHeapObject());
   SetActiveInstructions(new_code.instructions());
 #else
   // DBC does not use allocation stubs.
@@ -14127,6 +14102,7 @@
   DEBUG_ASSERT(IsMutatorOrAtSafepoint() || !is_alive());
   // RawInstructions are never allocated in New space and hence a
   // store buffer update is not needed here.
+  StorePointer(&raw_ptr()->active_instructions_, instructions);
   StoreNonPointer(&raw_ptr()->entry_point_,
                   reinterpret_cast<uword>(instructions->ptr()) +
                   Instructions::HeaderSize());
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index 7243d0f..564db72 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -3964,7 +3964,8 @@
   FINAL_HEAP_OBJECT_IMPLEMENTATION(Instructions, Object);
   friend class Class;
   friend class Code;
-  friend class InstructionsWriter;
+  friend class AssemblyInstructionsWriter;
+  friend class BlobInstructionsWriter;
 };
 
 
@@ -4377,7 +4378,9 @@
 
 class Code : public Object {
  public:
-  uword active_entry_point() const { return raw_ptr()->entry_point_; }
+  RawInstructions* active_instructions() const {
+    return raw_ptr()->active_instructions_;
+  }
 
   RawInstructions* instructions() const { return raw_ptr()->instructions_; }
 
@@ -4407,15 +4410,20 @@
   }
   void set_is_alive(bool value) const;
 
-  uword EntryPoint() const;
-  intptr_t Size() const;
-
+  uword EntryPoint() const {
+    return Instructions::Handle(instructions()).EntryPoint();
+  }
+  intptr_t Size() const {
+    const Instructions& instr = Instructions::Handle(instructions());
+    return instr.size();
+  }
   RawObjectPool* GetObjectPool() const {
     return object_pool();
   }
   bool ContainsInstructionAt(uword addr) const {
-    const uword offset = addr - EntryPoint();
-    return offset < static_cast<uword>(Size());
+    const Instructions& instr = Instructions::Handle(instructions());
+    const uword offset = addr - instr.EntryPoint();
+    return offset < static_cast<uword>(instr.size());
   }
 
   // Returns true if there is a debugger breakpoint set in this code object.
@@ -4654,11 +4662,12 @@
   void Enable() const {
     if (!IsDisabled()) return;
     ASSERT(Thread::Current()->IsMutatorThread());
+    ASSERT(instructions() != active_instructions());
     SetActiveInstructions(instructions());
   }
 
   bool IsDisabled() const {
-    return active_entry_point() != EntryPoint();
+    return instructions() != active_instructions();
   }
 
  private:
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 9f1f4fc..12d5234 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -1120,12 +1120,10 @@
   uword entry_point_;
 
   RawObject** from() {
-    return reinterpret_cast<RawObject**>(&ptr()->instructions_);
+    return reinterpret_cast<RawObject**>(&ptr()->active_instructions_);
   }
-  union {
-    RawInstructions* instructions_;
-    RawSmi* precompiled_instructions_size_;
-  };
+  RawInstructions* active_instructions_;
+  RawInstructions* instructions_;
   RawObjectPool* object_pool_;
   // If owner_ is Function::null() the owner is a regular stub.
   // If owner_ is a Class the owner is the allocation stub for that class.
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 1a90c2c..eec2523 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -1361,20 +1361,24 @@
   result.set_lazy_deopt_pc_offset(-1);
 
   int32_t text_offset = reader->Read<int32_t>();
-  int32_t instructions_size = reader->Read<int32_t>();
-  uword entry_point = reader->GetInstructionsAt(text_offset);
+  RawInstructions* instr = reinterpret_cast<RawInstructions*>(
+      reader->GetInstructionsAt(text_offset) + kHeapObjectTag);
+  uword entry_point = Instructions::EntryPoint(instr);
 
 #if defined(DEBUG)
+  ASSERT(instr->IsMarked());
+  ASSERT(instr->IsVMHeapObject());
   uword expected_check = reader->Read<uword>();
+  intptr_t instructions_size = Utils::RoundUp(instr->size_,
+                                              OS::PreferredCodeAlignment());
   uword actual_check = Checksum(entry_point, instructions_size);
   ASSERT(expected_check == actual_check);
 #endif
 
   result.StoreNonPointer(&result.raw_ptr()->entry_point_, entry_point);
 
-  result.StorePointer(reinterpret_cast<RawSmi*const*>(
-                          &result.raw_ptr()->instructions_),
-                      Smi::New(instructions_size));
+  result.StorePointer(&result.raw_ptr()->active_instructions_, instr);
+  result.StorePointer(&result.raw_ptr()->instructions_, instr);
 
   (*reader->PassiveObjectHandle()) ^= reader->ReadObjectImpl(kAsReference);
   result.StorePointer(reinterpret_cast<RawObject*const*>(
@@ -1418,9 +1422,6 @@
   result.StorePointer(&result.raw_ptr()->return_address_metadata_,
                       Object::null());
 
-  ASSERT(result.Size() == instructions_size);
-  ASSERT(result.EntryPoint() == entry_point);
-
   return result.raw();
 }
 
@@ -1449,14 +1450,18 @@
   // Write out all the non object fields.
   writer->Write<int32_t>(ptr()->state_bits_);
 
+  // No disabled code in precompilation.
+  ASSERT(ptr()->instructions_ == ptr()->active_instructions_);
+
   RawInstructions* instr = ptr()->instructions_;
-  intptr_t size = instr->ptr()->size_;
   int32_t text_offset = writer->GetInstructionsId(instr, this);
   writer->Write<int32_t>(text_offset);
-  writer->Write<int32_t>(size);
+
 #if defined(DEBUG)
   uword entry = ptr()->entry_point_;
-  uword check = Checksum(entry, size);
+  intptr_t instructions_size = Utils::RoundUp(instr->size_,
+                                              OS::PreferredCodeAlignment());
+  uword check = Checksum(entry, instructions_size);
   writer->Write<uword>(check);
 #endif
 
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index 6107c53..2f2ca6a 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -1160,13 +1160,9 @@
   }
 #endif
 
-  intptr_t payload_size = instructions->ptr()->size_;
-  payload_size = Utils::RoundUp(payload_size, OS::PreferredCodeAlignment());
-
+  intptr_t heap_size = instructions->Size();
   intptr_t offset = next_offset_;
-  ASSERT(Utils::IsAligned(next_offset_, OS::PreferredCodeAlignment()));
-  next_offset_ += payload_size;
-  ASSERT(Utils::IsAligned(next_offset_, OS::PreferredCodeAlignment()));
+  next_offset_ += heap_size;
   instructions_.Add(InstructionsData(instructions, code, offset));
 
   return offset;
@@ -1235,7 +1231,32 @@
 
     ASSERT(insns.raw()->Size() % sizeof(uint64_t) == 0);
 
-    // 1. Write a label at the entry point.
+    // 1. Write from the header to the entry point.
+    {
+      NoSafepointScope no_safepoint;
+
+      uword beginning = reinterpret_cast<uword>(insns.raw_ptr());
+      uword entry = beginning + Instructions::HeaderSize();
+
+      ASSERT(Utils::IsAligned(beginning, sizeof(uint64_t)));
+      ASSERT(Utils::IsAligned(entry, sizeof(uint64_t)));
+
+      // Write Instructions with the mark and VM heap bits set.
+      uword marked_tags = insns.raw_ptr()->tags_;
+      marked_tags = RawObject::VMHeapObjectTag::update(true, marked_tags);
+      marked_tags = RawObject::MarkBit::update(true, marked_tags);
+
+      WriteWordLiteral(marked_tags);
+      beginning += sizeof(uword);
+
+      for (uword* cursor = reinterpret_cast<uword*>(beginning);
+           cursor < reinterpret_cast<uword*>(entry);
+           cursor++) {
+        WriteWordLiteral(*cursor);
+      }
+    }
+
+    // 2. Write a label at the entry point.
     owner = code.owner();
     if (owner.IsNull()) {
       const char* name = StubCode::NameOfStub(insns.EntryPoint());
@@ -1255,7 +1276,7 @@
     }
 
     {
-      // 2. Write from the entry point to the end.
+      // 3. Write from the entry point to the end.
       NoSafepointScope no_safepoint;
       uword beginning = reinterpret_cast<uword>(insns.raw()) - kHeapObjectTag;
       uword entry = beginning + Instructions::HeaderSize();
@@ -1342,8 +1363,33 @@
   for (intptr_t i = 0; i < instructions_.length(); i++) {
     const Instructions& insns = *instructions_[i].insns_;
 
+    // 1. Write from the header to the entry point.
     {
-      // 2. Write from the entry point to the end.
+      NoSafepointScope no_safepoint;
+
+      uword beginning = reinterpret_cast<uword>(insns.raw_ptr());
+      uword entry = beginning + Instructions::HeaderSize();
+
+      ASSERT(Utils::IsAligned(beginning, sizeof(uint64_t)));
+      ASSERT(Utils::IsAligned(entry, sizeof(uint64_t)));
+
+      // Write Instructions with the mark and VM heap bits set.
+      uword marked_tags = insns.raw_ptr()->tags_;
+      marked_tags = RawObject::VMHeapObjectTag::update(true, marked_tags);
+      marked_tags = RawObject::MarkBit::update(true, marked_tags);
+
+      instructions_blob_stream_.WriteWord(marked_tags);
+      beginning += sizeof(uword);
+
+      for (uword* cursor = reinterpret_cast<uword*>(beginning);
+           cursor < reinterpret_cast<uword*>(entry);
+           cursor++) {
+        instructions_blob_stream_.WriteWord(*cursor);
+      }
+    }
+
+    // 2. Write from the entry point to the end.
+    {
       NoSafepointScope no_safepoint;
       uword beginning = reinterpret_cast<uword>(insns.raw()) - kHeapObjectTag;
       uword entry = beginning + Instructions::HeaderSize();
diff --git a/runtime/vm/stack_frame.cc b/runtime/vm/stack_frame.cc
index a537026..28636b8 100644
--- a/runtime/vm/stack_frame.cc
+++ b/runtime/vm/stack_frame.cc
@@ -109,7 +109,8 @@
     Array maps;
     maps = Array::null();
     Stackmap map;
-    const uword entry = code.EntryPoint();
+    const uword entry = reinterpret_cast<uword>(code.instructions()->ptr()) +
+                        Instructions::HeaderSize();
     map = code.GetStackmap(pc() - entry, &maps, &map);
     if (!map.IsNull()) {
       RawObject** first = reinterpret_cast<RawObject**>(sp());