[vm] Reference type test stubs through Code; use the type as the owner of a type test stub.

Directly referencing VM isolate instructions from an isolate snapshot causes the instructions to be copied into the isolate's image page because instructions do not use the indirection of the ref array. Duplicating the instructions caused TypeTestingStubFinder::LookupByAddresss to be unable to identity the duplicate copies of the VM isolate type testing stubs.

Fixes identity of StubCode::*TypeTest broken by duplication in snapshot.
Fixes generating AppJIT snapshots after loading from AppJIT snapshots.
Fixes attribution of ticks for type test stubs in the AOT profiler.

Change-Id: I9879a85bd548e694ee36e14f795898582ccdddb0
Reviewed-on: https://dart-review.googlesource.com/c/90501
Commit-Queue: Ryan Macnak <rmacnak@google.com>
Reviewed-by: Martin Kustermann <kustermann@google.com>
Reviewed-by: Zach Anderson <zra@google.com>
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 1684f25..ef183f9 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -69,10 +69,6 @@
   return RawObject::FromAddr(address);
 }
 
-static bool SnapshotContainsTypeTestingStubs(Snapshot::Kind kind) {
-  return kind == Snapshot::kFullAOT || kind == Snapshot::kFullJIT;
-}
-
 void Deserializer::InitializeHeader(RawObject* raw,
                                     intptr_t class_id,
                                     intptr_t size,
@@ -597,7 +593,7 @@
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    TIMELINE_DURATION(Thread::Current(), Isolate, "PostLoadFunction");
+    TIMELINE_DURATION(Thread::Current(), Isolate, "Function");
 
     if (kind == Snapshot::kFullAOT) {
       Function& func = Function::Handle(zone);
@@ -1010,7 +1006,7 @@
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    TIMELINE_DURATION(Thread::Current(), Isolate, "PostLoadField");
+    TIMELINE_DURATION(Thread::Current(), Isolate, "Field");
 
     Field& field = Field::Handle(zone);
     if (!Isolate::Current()->use_field_guards()) {
@@ -1323,14 +1319,16 @@
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    Array& array_ = Array::Handle(zone);
-    KernelProgramInfo& info_ = KernelProgramInfo::Handle(zone);
+    TIMELINE_DURATION(Thread::Current(), Isolate, "KernelProgramInfo");
+
+    Array& array = Array::Handle(zone);
+    KernelProgramInfo& info = KernelProgramInfo::Handle(zone);
     for (intptr_t id = start_index_; id < stop_index_; id++) {
-      info_ ^= refs.At(id);
-      array_ = HashTables::New<UnorderedHashMap<SmiTraits>>(16, Heap::kOld);
-      info_.set_libraries_cache(array_);
-      array_ = HashTables::New<UnorderedHashMap<SmiTraits>>(16, Heap::kOld);
-      info_.set_classes_cache(array_);
+      info ^= refs.At(id);
+      array = HashTables::New<UnorderedHashMap<SmiTraits>>(16, Heap::kOld);
+      info.set_libraries_cache(array);
+      array = HashTables::New<UnorderedHashMap<SmiTraits>>(16, Heap::kOld);
+      info.set_classes_cache(array);
     }
   }
 };
@@ -2334,6 +2332,8 @@
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
+    TIMELINE_DURATION(Thread::Current(), Isolate, "MegamorphicCache");
+
 #if defined(DART_PRECOMPILED_RUNTIME)
     if (FLAG_use_bare_instructions) {
       // By default, every megamorphic call site will load the target
@@ -2758,8 +2758,7 @@
 #if !defined(DART_PRECOMPILED_RUNTIME)
 class TypeSerializationCluster : public SerializationCluster {
  public:
-  explicit TypeSerializationCluster(const TypeTestingStubFinder& ttsf)
-      : SerializationCluster("Type"), type_testing_stubs_(ttsf) {}
+  TypeSerializationCluster() : SerializationCluster("Type") {}
   ~TypeSerializationCluster() {}
 
   void Trace(Serializer* s, RawObject* object) {
@@ -2800,10 +2799,6 @@
   }
 
   void WriteFill(Serializer* s) {
-    const bool is_vm_isolate = s->isolate() == Dart::vm_isolate();
-    const bool should_write_type_testing_stub =
-        SnapshotContainsTypeTestingStubs(s->kind());
-
     intptr_t count = canonical_objects_.length();
     for (intptr_t i = 0; i < count; i++) {
       RawType* type = canonical_objects_[i];
@@ -2811,11 +2806,6 @@
       WriteFromTo(type);
       s->WriteTokenPosition(type->ptr()->token_pos_);
       s->Write<int8_t>(type->ptr()->type_state_);
-      if (should_write_type_testing_stub) {
-        RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
-            type->ptr()->type_test_stub_entry_point_);
-        s->WriteInstructions(instr, Code::null());
-      }
     }
     count = objects_.length();
     for (intptr_t i = 0; i < count; i++) {
@@ -2824,37 +2814,18 @@
       WriteFromTo(type);
       s->WriteTokenPosition(type->ptr()->token_pos_);
       s->Write<int8_t>(type->ptr()->type_state_);
-      if (should_write_type_testing_stub) {
-        RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
-            type->ptr()->type_test_stub_entry_point_);
-        s->WriteInstructions(instr, Code::null());
-      }
-    }
-
-    // The dynamic/void objects are not serialized, so we manually send
-    // the type testing stub for it.
-    if (should_write_type_testing_stub && is_vm_isolate) {
-      RawInstructions* dynamic_instr = type_testing_stubs_.LookupByAddresss(
-          Type::dynamic_type().type_test_stub_entry_point());
-      s->WriteInstructions(dynamic_instr, Code::null());
-
-      RawInstructions* void_instr = type_testing_stubs_.LookupByAddresss(
-          Type::void_type().type_test_stub_entry_point());
-      s->WriteInstructions(void_instr, Code::null());
     }
   }
 
  private:
   GrowableArray<RawType*> canonical_objects_;
   GrowableArray<RawType*> objects_;
-  const TypeTestingStubFinder& type_testing_stubs_;
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
 class TypeDeserializationCluster : public DeserializationCluster {
  public:
-  TypeDeserializationCluster()
-      : type_(AbstractType::Handle()), instr_(Instructions::Handle()) {}
+  TypeDeserializationCluster() {}
   ~TypeDeserializationCluster() {}
 
   void ReadAlloc(Deserializer* d) {
@@ -2876,8 +2847,6 @@
 
   void ReadFill(Deserializer* d) {
     const bool is_vm_isolate = d->isolate() == Dart::vm_isolate();
-    const bool should_read_type_testing_stub =
-        SnapshotContainsTypeTestingStubs(d->kind());
 
     for (intptr_t id = canonical_start_index_; id < canonical_stop_index_;
          id++) {
@@ -2887,11 +2856,6 @@
       ReadFromTo(type);
       type->ptr()->token_pos_ = d->ReadTokenPosition();
       type->ptr()->type_state_ = d->Read<int8_t>();
-      if (should_read_type_testing_stub) {
-        instr_ = d->ReadInstructions();
-        type_ = type;
-        type_.SetTypeTestingStub(instr_);
-      }
     }
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
@@ -2901,35 +2865,38 @@
       ReadFromTo(type);
       type->ptr()->token_pos_ = d->ReadTokenPosition();
       type->ptr()->type_state_ = d->Read<int8_t>();
-      if (should_read_type_testing_stub) {
-        instr_ = d->ReadInstructions();
-        type_ = type;
-        type_.SetTypeTestingStub(instr_);
-      }
-    }
-
-    // The dynamic/void objects are not serialized, so we manually send
-    // the type testing stub for it.
-    if (should_read_type_testing_stub && is_vm_isolate) {
-      instr_ = d->ReadInstructions();
-      Type::dynamic_type().SetTypeTestingStub(instr_);
-      instr_ = d->ReadInstructions();
-      Type::void_type().SetTypeTestingStub(instr_);
     }
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    if (!SnapshotContainsTypeTestingStubs(kind)) {
+    TIMELINE_DURATION(Thread::Current(), Isolate, "Type");
+
+    Type& type = Type::Handle(zone);
+    Code& stub = Code::Handle(zone);
+
+    if (Snapshot::IncludesCode(kind)) {
       for (intptr_t id = canonical_start_index_; id < canonical_stop_index_;
            id++) {
-        type_ ^= refs.At(id);
-        instr_ = TypeTestingStubGenerator::DefaultCodeForType(type_);
-        type_.SetTypeTestingStub(instr_);
+        type ^= refs.At(id);
+        stub = type.type_test_stub();
+        type.SetTypeTestingStub(stub);  // Update type_test_stub_entry_point_
       }
       for (intptr_t id = start_index_; id < stop_index_; id++) {
-        type_ ^= refs.At(id);
-        instr_ = TypeTestingStubGenerator::DefaultCodeForType(type_);
-        type_.SetTypeTestingStub(instr_);
+        type ^= refs.At(id);
+        stub = type.type_test_stub();
+        type.SetTypeTestingStub(stub);  // Update type_test_stub_entry_point_
+      }
+    } else {
+      for (intptr_t id = canonical_start_index_; id < canonical_stop_index_;
+           id++) {
+        type ^= refs.At(id);
+        stub = TypeTestingStubGenerator::DefaultCodeForType(type);
+        type.SetTypeTestingStub(stub);
+      }
+      for (intptr_t id = start_index_; id < stop_index_; id++) {
+        type ^= refs.At(id);
+        stub = TypeTestingStubGenerator::DefaultCodeForType(type);
+        type.SetTypeTestingStub(stub);
       }
     }
   }
@@ -2937,15 +2904,12 @@
  private:
   intptr_t canonical_start_index_;
   intptr_t canonical_stop_index_;
-  AbstractType& type_;
-  Instructions& instr_;
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 class TypeRefSerializationCluster : public SerializationCluster {
  public:
-  explicit TypeRefSerializationCluster(const TypeTestingStubFinder& ttsf)
-      : SerializationCluster("TypeRef"), type_testing_stubs_(ttsf) {}
+  TypeRefSerializationCluster() : SerializationCluster("TypeRef") {}
   ~TypeRefSerializationCluster() {}
 
   void Trace(Serializer* s, RawObject* object) {
@@ -2965,32 +2929,22 @@
   }
 
   void WriteFill(Serializer* s) {
-    const bool should_write_type_testing_stub =
-        SnapshotContainsTypeTestingStubs(s->kind());
-
     intptr_t count = objects_.length();
     for (intptr_t i = 0; i < count; i++) {
       RawTypeRef* type = objects_[i];
       AutoTraceObject(type);
       WriteFromTo(type);
-      if (should_write_type_testing_stub) {
-        RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
-            type->ptr()->type_test_stub_entry_point_);
-        s->WriteInstructions(instr, Code::null());
-      }
     }
   }
 
  private:
   GrowableArray<RawTypeRef*> objects_;
-  const TypeTestingStubFinder& type_testing_stubs_;
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
 class TypeRefDeserializationCluster : public DeserializationCluster {
  public:
-  TypeRefDeserializationCluster()
-      : type_(AbstractType::Handle()), instr_(Instructions::Handle()) {}
+  TypeRefDeserializationCluster() {}
   ~TypeRefDeserializationCluster() {}
 
   void ReadAlloc(Deserializer* d) {
@@ -3005,42 +2959,42 @@
 
   void ReadFill(Deserializer* d) {
     const bool is_vm_object = d->isolate() == Dart::vm_isolate();
-    const bool should_read_type_testing_stub =
-        SnapshotContainsTypeTestingStubs(d->kind());
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       RawTypeRef* type = reinterpret_cast<RawTypeRef*>(d->Ref(id));
       Deserializer::InitializeHeader(type, kTypeRefCid, TypeRef::InstanceSize(),
                                      is_vm_object);
       ReadFromTo(type);
-      if (should_read_type_testing_stub) {
-        instr_ = d->ReadInstructions();
-        type_ = type;
-        type_.SetTypeTestingStub(instr_);
-      }
     }
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    if (!SnapshotContainsTypeTestingStubs(kind)) {
+    TIMELINE_DURATION(Thread::Current(), Isolate, "TypeRef");
+
+    TypeRef& type_ref = TypeRef::Handle(zone);
+    Code& stub = Code::Handle(zone);
+
+    if (Snapshot::IncludesCode(kind)) {
       for (intptr_t id = start_index_; id < stop_index_; id++) {
-        type_ ^= refs.At(id);
-        instr_ = TypeTestingStubGenerator::DefaultCodeForType(type_);
-        type_.SetTypeTestingStub(instr_);
+        type_ref ^= refs.At(id);
+        stub = type_ref.type_test_stub();
+        type_ref.SetTypeTestingStub(
+            stub);  // Update type_test_stub_entry_point_
+      }
+    } else {
+      for (intptr_t id = start_index_; id < stop_index_; id++) {
+        type_ref ^= refs.At(id);
+        stub = TypeTestingStubGenerator::DefaultCodeForType(type_ref);
+        type_ref.SetTypeTestingStub(stub);
       }
     }
   }
-
- private:
-  AbstractType& type_;
-  Instructions& instr_;
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
 class TypeParameterSerializationCluster : public SerializationCluster {
  public:
-  explicit TypeParameterSerializationCluster(const TypeTestingStubFinder& ttsf)
-      : SerializationCluster("TypeParameter"), type_testing_stubs_(ttsf) {}
+  TypeParameterSerializationCluster() : SerializationCluster("TypeParameter") {}
 
   ~TypeParameterSerializationCluster() {}
 
@@ -3062,9 +3016,6 @@
   }
 
   void WriteFill(Serializer* s) {
-    const bool should_write_type_testing_stub =
-        SnapshotContainsTypeTestingStubs(s->kind());
-
     intptr_t count = objects_.length();
     for (intptr_t i = 0; i < count; i++) {
       RawTypeParameter* type = objects_[i];
@@ -3074,24 +3025,17 @@
       s->WriteTokenPosition(type->ptr()->token_pos_);
       s->Write<int16_t>(type->ptr()->index_);
       s->Write<int8_t>(type->ptr()->type_state_);
-      if (should_write_type_testing_stub) {
-        RawInstructions* instr = type_testing_stubs_.LookupByAddresss(
-            type->ptr()->type_test_stub_entry_point_);
-        s->WriteInstructions(instr, Code::null());
-      }
     }
   }
 
  private:
   GrowableArray<RawTypeParameter*> objects_;
-  const TypeTestingStubFinder& type_testing_stubs_;
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
 class TypeParameterDeserializationCluster : public DeserializationCluster {
  public:
-  TypeParameterDeserializationCluster()
-      : type_(AbstractType::Handle()), instr_(Instructions::Handle()) {}
+  TypeParameterDeserializationCluster() {}
   ~TypeParameterDeserializationCluster() {}
 
   void ReadAlloc(Deserializer* d) {
@@ -3107,8 +3051,6 @@
 
   void ReadFill(Deserializer* d) {
     bool is_vm_object = d->isolate() == Dart::vm_isolate();
-    const bool should_read_type_testing_stub =
-        SnapshotContainsTypeTestingStubs(d->kind());
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       RawTypeParameter* type = reinterpret_cast<RawTypeParameter*>(d->Ref(id));
@@ -3119,27 +3061,30 @@
       type->ptr()->token_pos_ = d->ReadTokenPosition();
       type->ptr()->index_ = d->Read<int16_t>();
       type->ptr()->type_state_ = d->Read<int8_t>();
-      if (should_read_type_testing_stub) {
-        instr_ = d->ReadInstructions();
-        type_ = type;
-        type_.SetTypeTestingStub(instr_);
-      }
     }
   }
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    if (!SnapshotContainsTypeTestingStubs(kind)) {
+    TIMELINE_DURATION(Thread::Current(), Isolate, "TypeParameter");
+
+    TypeParameter& type_param = TypeParameter::Handle(zone);
+    Code& stub = Code::Handle(zone);
+
+    if (Snapshot::IncludesCode(kind)) {
       for (intptr_t id = start_index_; id < stop_index_; id++) {
-        type_ ^= refs.At(id);
-        instr_ = TypeTestingStubGenerator::DefaultCodeForType(type_);
-        type_.SetTypeTestingStub(instr_);
+        type_param ^= refs.At(id);
+        stub = type_param.type_test_stub();
+        type_param.SetTypeTestingStub(
+            stub);  // Update type_test_stub_entry_point_
+      }
+    } else {
+      for (intptr_t id = start_index_; id < stop_index_; id++) {
+        type_param ^= refs.At(id);
+        stub = TypeTestingStubGenerator::DefaultCodeForType(type_param);
+        type_param.SetTypeTestingStub(stub);
       }
     }
   }
-
- private:
-  AbstractType& type_;
-  Instructions& instr_;
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -3283,7 +3228,7 @@
   void ReadFill(Deserializer* d) {}
 
   void PostLoad(const Array& refs, Snapshot::Kind kind, Zone* zone) {
-    TIMELINE_DURATION(Thread::Current(), Isolate, "PostLoadMint");
+    TIMELINE_DURATION(Thread::Current(), Isolate, "Mint");
 
     const Class& mint_cls =
         Class::Handle(zone, Isolate::Current()->object_store()->mint_class());
@@ -4348,11 +4293,11 @@
     case kLibraryPrefixCid:
       return new (Z) LibraryPrefixSerializationCluster();
     case kTypeCid:
-      return new (Z) TypeSerializationCluster(type_testing_stubs_);
+      return new (Z) TypeSerializationCluster();
     case kTypeRefCid:
-      return new (Z) TypeRefSerializationCluster(type_testing_stubs_);
+      return new (Z) TypeRefSerializationCluster();
     case kTypeParameterCid:
-      return new (Z) TypeParameterSerializationCluster(type_testing_stubs_);
+      return new (Z) TypeParameterSerializationCluster();
     case kClosureCid:
       return new (Z) ClosureSerializationCluster();
     case kMintCid:
@@ -4393,13 +4338,15 @@
       break;
   }
 
-  FATAL2("No cluster defined for cid %" Pd ", kind %s", cid,
-         Snapshot::KindToCString(kind_));
+  // The caller will check for NULL and provide an error with more context than
+  // is available here.
   return NULL;
 #endif  // !DART_PRECOMPILED_RUNTIME
 }
 
 void Serializer::WriteInstructions(RawInstructions* instr, RawCode* code) {
+  ASSERT(code != Code::null());
+
   const intptr_t offset = image_writer_->GetTextOffsetFor(instr, code);
   ASSERT(offset != 0);
   Write<int32_t>(offset);
@@ -4497,7 +4444,10 @@
     // roots we do not trace references, e.g. inside [RawCode], to
     // [RawInstructions], since [RawInstructions] doesn't contain any references
     // and the serialization code uses an [ImageWriter] for those.
-    ASSERT(object->GetClassId() != kInstructionsCid);
+    if (object->IsInstructions()) {
+      UnexpectedObject(object,
+                       "Instructions should only be reachable from Code");
+    }
 
     heap_->SetObjectId(object, 1);
     ASSERT(heap_->GetObjectId(object) != 0);
@@ -4523,6 +4473,9 @@
   SerializationCluster* cluster = clusters_by_cid_[cid];
   if (cluster == NULL) {
     cluster = NewClusterForClass(cid);
+    if (cluster == NULL) {
+      UnexpectedObject(object, "No serialization cluster defined");
+    }
     clusters_by_cid_[cid] = cluster;
   }
   ASSERT(cluster != NULL);
@@ -4544,7 +4497,8 @@
     thread()->DecrementNoSafepointScopeDepth();
   }
   Object& object = Object::Handle(raw_object);
-  OS::PrintErr("Unexpected object (%s): 0x%" Px " %s\n", message,
+  OS::PrintErr("Unexpected object (%s, %s): 0x%" Px " %s\n", message,
+               Snapshot::KindToCString(kind_),
                reinterpret_cast<uword>(object.raw()), object.ToCString());
 #if defined(SNAPSHOT_BACKTRACE)
   while (!object.IsNull()) {
@@ -5263,6 +5217,13 @@
 #if defined(DEBUG)
   isolate()->ValidateClassTable();
 #endif
+
+  {
+    TIMELINE_DURATION(thread(), Isolate, "PostLoad");
+    for (intptr_t i = 0; i < num_clusters_; i++) {
+      clusters_[i]->PostLoad(refs, kind_, zone_);
+    }
+  }
 }
 
 void Deserializer::ReadIsolateSnapshot(ObjectStore* object_store) {
@@ -5306,8 +5267,11 @@
   isolate->heap()->Verify();
 #endif
 
-  for (intptr_t i = 0; i < num_clusters_; i++) {
-    clusters_[i]->PostLoad(refs, kind_, zone_);
+  {
+    TIMELINE_DURATION(thread(), Isolate, "PostLoad");
+    for (intptr_t i = 0; i < num_clusters_; i++) {
+      clusters_[i]->PostLoad(refs, kind_, zone_);
+    }
   }
 
   // Setup native resolver for bootstrap impl.
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index d4a17c8..f3c574e 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -17,7 +17,6 @@
 #include "vm/object.h"
 #include "vm/raw_object_fields.h"
 #include "vm/snapshot.h"
-#include "vm/type_testing_stubs.h"
 #include "vm/v8_snapshot_writer.h"
 #include "vm/version.h"
 
@@ -368,7 +367,6 @@
   void DumpCombinedCodeStatistics();
 
  private:
-  TypeTestingStubFinder type_testing_stubs_;
   Heap* heap_;
   Zone* zone_;
   Snapshot::Kind kind_;
diff --git a/runtime/vm/compiler/aot/precompiler.cc b/runtime/vm/compiler/aot/precompiler.cc
index e0ad99c..3784b1b 100644
--- a/runtime/vm/compiler/aot/precompiler.cc
+++ b/runtime/vm/compiler/aot/precompiler.cc
@@ -1527,7 +1527,7 @@
   type_usage_info->BuildTypeUsageInformation();
 
   TypeTestingStubGenerator type_testing_stubs;
-  Instructions& instr = Instructions::Handle();
+  Code& code = Code::Handle();
   for (intptr_t i = 0; i < types.length(); i++) {
     const AbstractType& type = types.At(i);
 
@@ -1538,8 +1538,8 @@
     }
 
     if (type_usage_info->IsUsedInTypeTest(type)) {
-      instr = type_testing_stubs.OptimizedCodeForType(type);
-      type.SetTypeTestingStub(instr);
+      code = type_testing_stubs.OptimizedCodeForType(type);
+      type.SetTypeTestingStub(code);
 
       // Ensure we retain the type.
       AddType(type);
diff --git a/runtime/vm/image_snapshot.cc b/runtime/vm/image_snapshot.cc
index ae115c3..70adef8 100644
--- a/runtime/vm/image_snapshot.cc
+++ b/runtime/vm/image_snapshot.cc
@@ -375,7 +375,7 @@
 #endif
 }
 
-static void EnsureIdentifier(char* label) {
+static void EnsureAssemblerIdentifier(char* label) {
   for (char c = *label; c != '\0'; c = *++label) {
     if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) ||
         ((c >= '0') && (c <= '9'))) {
@@ -437,7 +437,7 @@
 
   ObjectStore* object_store = Isolate::Current()->object_store();
 
-  TypeTestingStubFinder tts;
+  TypeTestingStubNamer tts;
   intptr_t text_offset = 0;
 
   ASSERT(offset_space_ != V8SnapshotProfileWriter::kSnapshot);
@@ -511,37 +511,34 @@
 
     // 2. Write a label at the entry point.
     // Linux's perf uses these labels.
-    if (code.IsNull()) {
-      const char* name = tts.StubNameFromAddresss(insns.EntryPoint());
-      assembly_stream_.Print("Precompiled_%s:\n", name);
-    } else {
-      owner = code.owner();
-      if (owner.IsNull()) {
-        const char* name = StubCode::NameOfStub(insns.EntryPoint());
-        if (name != nullptr) {
-          assembly_stream_.Print("Precompiled_Stub_%s:\n", name);
-        } else {
-          if (name == nullptr) {
-            name = NameOfStubIsolateSpecificStub(object_store, code);
-          }
-          if (name == nullptr) {
-            name = tts.StubNameFromAddresss(insns.EntryPoint());
-          }
-          assembly_stream_.Print("Precompiled__%s:\n", name);
-        }
-      } else if (owner.IsClass()) {
-        str = Class::Cast(owner).Name();
-        const char* name = str.ToCString();
-        EnsureIdentifier(const_cast<char*>(name));
-        assembly_stream_.Print("Precompiled_AllocationStub_%s_%" Pd ":\n", name,
-                               i);
-      } else if (owner.IsFunction()) {
-        const char* name = Function::Cast(owner).ToQualifiedCString();
-        EnsureIdentifier(const_cast<char*>(name));
-        assembly_stream_.Print("Precompiled_%s_%" Pd ":\n", name, i);
+    ASSERT(!code.IsNull());
+    owner = code.owner();
+    if (owner.IsNull()) {
+      const char* name = StubCode::NameOfStub(insns.EntryPoint());
+      if (name != nullptr) {
+        assembly_stream_.Print("Precompiled_Stub_%s:\n", name);
       } else {
-        UNREACHABLE();
+        if (name == nullptr) {
+          name = NameOfStubIsolateSpecificStub(object_store, code);
+        }
+        ASSERT(name != nullptr);
+        assembly_stream_.Print("Precompiled__%s:\n", name);
       }
+    } else if (owner.IsClass()) {
+      str = Class::Cast(owner).Name();
+      const char* name = str.ToCString();
+      EnsureAssemblerIdentifier(const_cast<char*>(name));
+      assembly_stream_.Print("Precompiled_AllocationStub_%s_%" Pd ":\n", name,
+                             i);
+    } else if (owner.IsAbstractType()) {
+      const char* name = tts.StubNameForType(AbstractType::Cast(owner));
+      assembly_stream_.Print("Precompiled_%s:\n", name);
+    } else if (owner.IsFunction()) {
+      const char* name = Function::Cast(owner).ToQualifiedCString();
+      EnsureAssemblerIdentifier(const_cast<char*>(name));
+      assembly_stream_.Print("Precompiled_%s_%" Pd ":\n", name, i);
+    } else {
+      UNREACHABLE();
     }
 
 #ifdef DART_PRECOMPILER
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 43c0a3c..581dbc6 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -929,13 +929,13 @@
   // The type testing stubs we initialize in AbstractType objects for the
   // canonical type of kDynamicCid/kVoidCid need to be set in this
   // method, which is called after StubCode::InitOnce().
-  Instructions& instr = Instructions::Handle();
+  Code& code = Code::Handle();
 
-  instr = TypeTestingStubGenerator::DefaultCodeForType(*dynamic_type_);
-  dynamic_type_->SetTypeTestingStub(instr);
+  code = TypeTestingStubGenerator::DefaultCodeForType(*dynamic_type_);
+  dynamic_type_->SetTypeTestingStub(code);
 
-  instr = TypeTestingStubGenerator::DefaultCodeForType(*void_type_);
-  void_type_->SetTypeTestingStub(instr);
+  code = TypeTestingStubGenerator::DefaultCodeForType(*void_type_);
+  void_type_->SetTypeTestingStub(code);
 }
 
 void Object::Cleanup() {
@@ -14454,6 +14454,10 @@
     String& cls_name = String::Handle(zone, Class::Cast(obj).ScrubbedName());
     ASSERT(!cls_name.IsNull());
     return zone->PrintToString("[Stub] Allocate %s", cls_name.ToCString());
+  } else if (obj.IsAbstractType()) {
+    // Type test stub.
+    return zone->PrintToString("[Stub] Type Test %s",
+                               AbstractType::Cast(obj).ToCString());
   } else {
     ASSERT(obj.IsFunction());
     // Dart function.
@@ -14477,14 +14481,18 @@
   return Name();
 }
 
+bool Code::IsStubCode() const {
+  return owner() == Object::null();
+}
+
 bool Code::IsAllocationStubCode() const {
   const Object& obj = Object::Handle(owner());
   return obj.IsClass();
 }
 
-bool Code::IsStubCode() const {
+bool Code::IsTypeTestStubCode() const {
   const Object& obj = Object::Handle(owner());
-  return obj.IsNull();
+  return obj.IsAbstractType();
 }
 
 bool Code::IsFunctionCode() const {
@@ -16669,16 +16677,16 @@
   return "AbstractType";
 }
 
-void AbstractType::SetTypeTestingStub(const Instructions& instr) const {
-  if (instr.IsNull()) {
+void AbstractType::SetTypeTestingStub(const Code& stub) const {
+  if (stub.IsNull()) {
     // This only happens during bootstrapping when creating Type objects before
     // we have the instructions.
     ASSERT(type_class_id() == kDynamicCid || type_class_id() == kVoidCid);
     StoreNonPointer(&raw_ptr()->type_test_stub_entry_point_, 0);
   } else {
-    StoreNonPointer(&raw_ptr()->type_test_stub_entry_point_,
-                    instr.EntryPoint());
+    StoreNonPointer(&raw_ptr()->type_test_stub_entry_point_, stub.EntryPoint());
   }
+  StorePointer(&raw_ptr()->type_test_stub_, stub.raw());
 }
 
 RawType* Type::NullType() {
@@ -17321,8 +17329,8 @@
   result.set_token_pos(token_pos);
   result.StoreNonPointer(&result.raw_ptr()->type_state_, RawType::kAllocated);
 
-  result.SetTypeTestingStub(Instructions::Handle(
-      Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
+  result.SetTypeTestingStub(
+      Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
   return result.raw();
 }
 
@@ -17423,7 +17431,7 @@
   ASSERT(!instantiated_ref_type.IsTypeRef());
   instantiated_type_ref.set_type(instantiated_ref_type);
 
-  instantiated_type_ref.SetTypeTestingStub(Instructions::Handle(
+  instantiated_type_ref.SetTypeTestingStub(Code::Handle(
       TypeTestingStubGenerator::DefaultCodeForType(instantiated_type_ref)));
   return instantiated_type_ref.raw();
 }
@@ -17491,8 +17499,8 @@
   const TypeRef& result = TypeRef::Handle(Z, TypeRef::New());
   result.set_type(type);
 
-  result.SetTypeTestingStub(Instructions::Handle(
-      Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
+  result.SetTypeTestingStub(
+      Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
   return result.raw();
 }
 
@@ -17697,8 +17705,8 @@
   result.StoreNonPointer(&result.raw_ptr()->type_state_,
                          RawTypeParameter::kAllocated);
 
-  result.SetTypeTestingStub(Instructions::Handle(
-      Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
+  result.SetTypeTestingStub(
+      Code::Handle(Z, TypeTestingStubGenerator::DefaultCodeForType(result)));
   return result.raw();
 }
 
diff --git a/runtime/vm/object.h b/runtime/vm/object.h
index b35e8ff..cea3fda 100644
--- a/runtime/vm/object.h
+++ b/runtime/vm/object.h
@@ -5019,16 +5019,9 @@
   }
 
   RawObject* owner() const { return raw_ptr()->owner_; }
-
-  void set_owner(const Function& function) const {
-    ASSERT(function.IsOld());
-    StorePointer(&raw_ptr()->owner_,
-                 reinterpret_cast<RawObject*>(function.raw()));
-  }
-
-  void set_owner(const Class& cls) {
-    ASSERT(cls.IsOld());
-    StorePointer(&raw_ptr()->owner_, reinterpret_cast<RawObject*>(cls.raw()));
+  void set_owner(const Object& owner) const {
+    ASSERT(owner.IsFunction() || owner.IsClass() || owner.IsAbstractType());
+    StorePointer(&raw_ptr()->owner_, owner.raw());
   }
 
   // We would have a VisitPointers function here to traverse all the
@@ -5093,8 +5086,9 @@
 #endif
   }
 
-  bool IsAllocationStubCode() const;
   bool IsStubCode() const;
+  bool IsAllocationStubCode() const;
+  bool IsTypeTestStubCode() const;
   bool IsFunctionCode() const;
 
   void DisableDartCode() const;
@@ -6319,8 +6313,9 @@
   uword type_test_stub_entry_point() const {
     return raw_ptr()->type_test_stub_entry_point_;
   }
+  RawCode* type_test_stub() const { return raw_ptr()->type_test_stub_; }
 
-  void SetTypeTestingStub(const Instructions& instr) const;
+  void SetTypeTestingStub(const Code& stub) const;
 
  private:
   // Returns true if this type is a subtype of FutureOr<T> specified by 'other'.
diff --git a/runtime/vm/object_service.cc b/runtime/vm/object_service.cc
index 5299079..f83dd85 100644
--- a/runtime/vm/object_service.cc
+++ b/runtime/vm/object_service.cc
@@ -798,7 +798,8 @@
   const char* qualified_name = QualifiedName();
   const char* vm_name = Name();
   AddNameProperties(&jsobj, qualified_name, vm_name);
-  const bool is_stub = IsStubCode() || IsAllocationStubCode();
+  const bool is_stub =
+      IsStubCode() || IsAllocationStubCode() || IsTypeTestStubCode();
   if (is_stub) {
     jsobj.AddProperty("kind", "Stub");
   } else {
diff --git a/runtime/vm/object_store.h b/runtime/vm/object_store.h
index 89abdee..ea2a191 100644
--- a/runtime/vm/object_store.h
+++ b/runtime/vm/object_store.h
@@ -135,7 +135,6 @@
   R_(Function, megamorphic_miss_function)                                      \
   RW(Array, code_order_table)                                                  \
   RW(Array, obfuscation_map)                                                   \
-  RW(GrowableObjectArray, type_testing_stubs)                                  \
   RW(GrowableObjectArray, changed_in_last_reload)                              \
 // Please remember the last entry must be referred in the 'to' function below.
 
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 25a420b..145bf46 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -2020,13 +2020,9 @@
     kFinalizedUninstantiated,  // Uninstantiated type ready for use.
   };
 
-  // Note: we don't handle this field in GC in any special way.
-  // Instead we rely on two things:
-  //   (1) GC not moving code objects and
-  //   (2) lifetime of optimized stubs exceeding that of types;
-  // Practically (2) means that optimized stubs never die because
-  // canonical types to which they are attached never die.
   uword type_test_stub_entry_point_;  // Accessed from generated code.
+  RawCode* type_test_stub_;  // Must be the last field, since subclasses use it
+                             // in their VISIT_FROM.
 
  private:
   RAW_HEAP_OBJECT_IMPLEMENTATION(AbstractType);
@@ -2039,7 +2035,7 @@
  private:
   RAW_HEAP_OBJECT_IMPLEMENTATION(Type);
 
-  VISIT_FROM(RawObject*, type_class_id_)
+  VISIT_FROM(RawObject*, type_test_stub_)
   RawSmi* type_class_id_;
   RawTypeArguments* arguments_;
   RawSmi* hash_;
@@ -2060,7 +2056,7 @@
  private:
   RAW_HEAP_OBJECT_IMPLEMENTATION(TypeRef);
 
-  VISIT_FROM(RawObject*, type_)
+  VISIT_FROM(RawObject*, type_test_stub_)
   RawAbstractType* type_;  // The referenced type.
   VISIT_TO(RawObject*, type_)
   RawObject** to_snapshot(Snapshot::Kind kind) { return to(); }
@@ -2070,7 +2066,7 @@
  private:
   RAW_HEAP_OBJECT_IMPLEMENTATION(TypeParameter);
 
-  VISIT_FROM(RawObject*, name_)
+  VISIT_FROM(RawObject*, type_test_stub_)
   RawString* name_;
   RawSmi* hash_;
   RawAbstractType* bound_;  // ObjectType if no explicit bound specified.
diff --git a/runtime/vm/raw_object_fields.cc b/runtime/vm/raw_object_fields.cc
index 03cbffb..e76edeb 100644
--- a/runtime/vm/raw_object_fields.cc
+++ b/runtime/vm/raw_object_fields.cc
@@ -137,6 +137,7 @@
   F(TypeArguments, instantiations_)                                            \
   F(TypeArguments, length_)                                                    \
   F(TypeArguments, hash_)                                                      \
+  F(AbstractType, type_test_stub_)                                             \
   F(Type, type_class_id_)                                                      \
   F(Type, arguments_)                                                          \
   F(Type, hash_)                                                               \
diff --git a/runtime/vm/raw_object_snapshot.cc b/runtime/vm/raw_object_snapshot.cc
index 286beb6..0424d5c 100644
--- a/runtime/vm/raw_object_snapshot.cc
+++ b/runtime/vm/raw_object_snapshot.cc
@@ -128,9 +128,9 @@
   }
 
   // Fill in the type testing stub.
-  Instructions& instr = *reader->InstructionsHandle();
-  instr = TypeTestingStubGenerator::DefaultCodeForType(type);
-  type.SetTypeTestingStub(instr);
+  Code& code = *reader->CodeHandle();
+  code = TypeTestingStubGenerator::DefaultCodeForType(type);
+  type.SetTypeTestingStub(code);
 
   return type.raw();
 }
@@ -203,9 +203,9 @@
                      kAsReference);
 
   // Fill in the type testing stub.
-  Instructions& instr = *reader->InstructionsHandle();
-  instr = TypeTestingStubGenerator::DefaultCodeForType(type_ref);
-  type_ref.SetTypeTestingStub(instr);
+  Code& code = *reader->CodeHandle();
+  code = TypeTestingStubGenerator::DefaultCodeForType(type_ref);
+  type_ref.SetTypeTestingStub(code);
 
   return type_ref.raw();
 }
@@ -259,9 +259,9 @@
   type_parameter.set_parameterized_class(*reader->ClassHandle());
 
   // Fill in the type testing stub.
-  Instructions& instr = *reader->InstructionsHandle();
-  instr = TypeTestingStubGenerator::DefaultCodeForType(type_parameter);
-  type_parameter.SetTypeTestingStub(instr);
+  Code& code = *reader->CodeHandle();
+  code = TypeTestingStubGenerator::DefaultCodeForType(type_parameter);
+  type_parameter.SetTypeTestingStub(code);
 
   return type_parameter.raw();
 }
diff --git a/runtime/vm/snapshot.cc b/runtime/vm/snapshot.cc
index ea491de..ea839f3 100644
--- a/runtime/vm/snapshot.cc
+++ b/runtime/vm/snapshot.cc
@@ -271,14 +271,16 @@
 }
 
 void SnapshotReader::RunDelayedTypePostprocessing() {
-  if (types_to_postprocess_.Length() > 0) {
-    AbstractType& type = AbstractType::Handle();
-    Instructions& instr = Instructions::Handle();
-    for (intptr_t i = 0; i < types_to_postprocess_.Length(); ++i) {
-      type ^= types_to_postprocess_.At(i);
-      instr = TypeTestingStubGenerator::DefaultCodeForType(type);
-      type.SetTypeTestingStub(instr);
-    }
+  if (types_to_postprocess_.Length() == 0) {
+    return;
+  }
+
+  AbstractType& type = AbstractType::Handle();
+  Code& code = Code::Handle();
+  for (intptr_t i = 0; i < types_to_postprocess_.Length(); ++i) {
+    type ^= types_to_postprocess_.At(i);
+    code = TypeTestingStubGenerator::DefaultCodeForType(type);
+    type.SetTypeTestingStub(code);
   }
 }
 
@@ -1069,12 +1071,6 @@
     return true;
   }
 
-  // Now check if it is an object from the VM isolate. These objects are shared
-  // by all isolates.
-  if (rawobj->IsVMHeapObject() && HandleVMIsolateObject(rawobj)) {
-    return true;
-  }
-
   // Check if it is a code object in that case just write a Null object
   // as we do not want code objects in the snapshot.
   if ((cid == kCodeCid) || (cid == kBytecodeCid)) {
@@ -1082,6 +1078,12 @@
     return true;
   }
 
+  // Now check if it is an object from the VM isolate. These objects are shared
+  // by all isolates.
+  if (rawobj->IsVMHeapObject() && HandleVMIsolateObject(rawobj)) {
+    return true;
+  }
+
   // Check if classes are not being serialized and it is preinitialized type
   // or a predefined internal VM class in the object store.
   // Check if it is an internal VM class which is in the object store.
diff --git a/runtime/vm/type_testing_stubs.cc b/runtime/vm/type_testing_stubs.cc
index bca4938..4b37b7f 100644
--- a/runtime/vm/type_testing_stubs.cc
+++ b/runtime/vm/type_testing_stubs.cc
@@ -7,6 +7,7 @@
 #include "vm/compiler/backend/flow_graph_compiler.h"
 #include "vm/compiler/backend/il_printer.h"
 #include "vm/object_store.h"
+#include "vm/timeline.h"
 
 #define __ assembler->
 
@@ -91,7 +92,7 @@
   return cname;
 }
 
-RawInstructions* TypeTestingStubGenerator::DefaultCodeForType(
+RawCode* TypeTestingStubGenerator::DefaultCodeForType(
     const AbstractType& type,
     bool lazy_specialize /* = true */) {
   // During bootstrapping we have no access to stubs yet, so we'll just return
@@ -100,25 +101,25 @@
     ASSERT(type.IsType());
     const intptr_t cid = Type::Cast(type).type_class_id();
     ASSERT(cid == kDynamicCid || cid == kVoidCid);
-    return Instructions::null();
+    return Code::null();
   }
 
   if (type.raw() == Type::ObjectType() || type.raw() == Type::DynamicType() ||
       type.raw() == Type::VoidType()) {
-    return StubCode::TopTypeTypeTest().instructions();
+    return StubCode::TopTypeTypeTest().raw();
   }
 
   if (type.IsTypeRef()) {
-    return StubCode::TypeRefTypeTest().instructions();
+    return StubCode::TypeRefTypeTest().raw();
   }
 
   if (type.IsType() || type.IsTypeParameter()) {
     const bool should_specialize = !FLAG_precompiled_mode && lazy_specialize;
-    return should_specialize ? StubCode::LazySpecializeTypeTest().instructions()
-                             : StubCode::DefaultTypeTest().instructions();
-  } else {
-    return StubCode::UnreachableTypeTest().instructions();
+    return should_specialize ? StubCode::LazySpecializeTypeTest().raw()
+                             : StubCode::DefaultTypeTest().raw();
   }
+
+  return StubCode::UnreachableTypeTest().raw();
 }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -126,215 +127,61 @@
                                                  const AbstractType& type) {
   HierarchyInfo hi(thread);
   TypeTestingStubGenerator generator;
-  const Instructions& instr = Instructions::Handle(
-      thread->zone(), generator.OptimizedCodeForType(type));
-  type.SetTypeTestingStub(instr);
+  const Code& code =
+      Code::Handle(thread->zone(), generator.OptimizedCodeForType(type));
+  type.SetTypeTestingStub(code);
 }
 #endif
 
 TypeTestingStubGenerator::TypeTestingStubGenerator()
-    : object_store_(Isolate::Current()->object_store()),
-      array_(GrowableObjectArray::Handle()),
-      instr_(Instructions::Handle()) {}
+    : object_store_(Isolate::Current()->object_store()) {}
 
-RawInstructions* TypeTestingStubGenerator::OptimizedCodeForType(
+RawCode* TypeTestingStubGenerator::OptimizedCodeForType(
     const AbstractType& type) {
 #if !defined(TARGET_ARCH_DBC) && !defined(TARGET_ARCH_IA32)
   ASSERT(StubCode::HasBeenInitialized());
 
   if (type.IsTypeRef()) {
-    return StubCode::TypeRefTypeTest().instructions();
+    return StubCode::TypeRefTypeTest().raw();
   }
 
   if (type.raw() == Type::ObjectType() || type.raw() == Type::DynamicType()) {
-    return StubCode::TopTypeTypeTest().instructions();
+    return StubCode::TopTypeTypeTest().raw();
   }
 
   if (type.IsCanonical()) {
     if (type.IsType()) {
 #if !defined(DART_PRECOMPILED_RUNTIME)
-      // Lazily create the type testing stubs array.
-      array_ = object_store_->type_testing_stubs();
-      if (array_.IsNull()) {
-        array_ = GrowableObjectArray::New(Heap::kOld);
-        object_store_->set_type_testing_stubs(array_);
+      const Code& code = Code::Handle(
+          TypeTestingStubGenerator::BuildCodeForType(Type::Cast(type)));
+      if (!code.IsNull()) {
+        return code.raw();
       }
 
-      instr_ = TypeTestingStubGenerator::BuildCodeForType(Type::Cast(type));
-      if (!instr_.IsNull()) {
-        array_.Add(type);
-        array_.Add(instr_);
-      } else {
-        // Fall back to default.
-        instr_ = StubCode::DefaultTypeTest().instructions();
-      }
+      // Fall back to default.
+      return StubCode::DefaultTypeTest().raw();
 #else
       // In the precompiled runtime we cannot lazily create new optimized type
       // testing stubs, so if we cannot find one, we'll just return the default
       // one.
-      instr_ = StubCode::DefaultTypeTest().instructions();
+      return StubCode::DefaultTypeTest().raw();
 #endif  // !defined(DART_PRECOMPILED_RUNTIME)
-      return instr_.raw();
     }
   }
 #endif  // !defined(TARGET_ARCH_DBC) && !defined(TARGET_ARCH_IA32)
   return TypeTestingStubGenerator::DefaultCodeForType(type, false);
 }
 
-TypeTestingStubFinder::TypeTestingStubFinder()
-    : array_(GrowableObjectArray::Handle()),
-      type_(Type::Handle()),
-      code_(Code::Handle()),
-      instr_(Instructions::Handle()) {
-  array_ = Isolate::Current()->object_store()->type_testing_stubs();
-  if (!array_.IsNull()) {
-    SortTableForFastLookup();
-  }
-}
-
-// TODO(kustermann): Use sorting/hashtables to speed this up.
-RawInstructions* TypeTestingStubFinder::LookupByAddresss(
-    uword entry_point) const {
-  // First test the 4 common ones:
-  code_ = StubCode::DefaultTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return code_.instructions();
-  }
-  code_ = StubCode::LazySpecializeTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return code_.instructions();
-  }
-  code_ = StubCode::TopTypeTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return code_.instructions();
-  }
-  code_ = StubCode::TypeRefTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return code_.instructions();
-  }
-  code_ = StubCode::UnreachableTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return code_.instructions();
-  }
-
-  const intptr_t tuple_idx = LookupInSortedArray(entry_point);
-  return Instructions::RawCast(array_.At(2 * tuple_idx + 1));
-}
-
-const char* TypeTestingStubFinder::StubNameFromAddresss(
-    uword entry_point) const {
-  // First test the 4 common ones:
-  code_ = StubCode::DefaultTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return "TypeTestingStub_Default";
-  }
-  code_ = StubCode::LazySpecializeTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return "TypeTestingStub_LazySpecialize";
-  }
-  code_ = StubCode::TopTypeTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return "TypeTestingStub_Top";
-  }
-  code_ = StubCode::TypeRefTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return "TypeTestingStub_Ref";
-  }
-  code_ = StubCode::UnreachableTypeTest().raw();
-  if (entry_point == code_.EntryPoint()) {
-    return "TypeTestingStub_Unreachable";
-  }
-
-  const intptr_t tuple_idx = LookupInSortedArray(entry_point);
-  type_ = AbstractType::RawCast(array_.At(2 * tuple_idx));
-  return namer_.StubNameForType(type_);
-}
-
-void TypeTestingStubFinder::SortTableForFastLookup() {
-  struct Sorter {
-    explicit Sorter(const GrowableObjectArray& array)
-        : array_(array),
-          object_(AbstractType::Handle()),
-          object2_(AbstractType::Handle()) {}
-
-    void Sort() {
-      const intptr_t tuples = array_.Length() / 2;
-      InsertionSort(0, tuples - 1);
-    }
-
-    void InsertionSort(intptr_t start, intptr_t end) {
-      for (intptr_t i = start + 1; i <= end; ++i) {
-        intptr_t j = i;
-        while (j > start && Value(j - 1) > Value(j)) {
-          Swap(j - 1, j);
-          j--;
-        }
-      }
-    }
-
-    void Swap(intptr_t i, intptr_t j) {
-      // Swap type.
-      object_ = array_.At(2 * i);
-      object2_ = array_.At(2 * j);
-      array_.SetAt(2 * i, object2_);
-      array_.SetAt(2 * j, object_);
-
-      // Swap instructions.
-      object_ = array_.At(2 * i + 1);
-      object2_ = array_.At(2 * j + 1);
-      array_.SetAt(2 * i + 1, object2_);
-      array_.SetAt(2 * j + 1, object_);
-    }
-
-    uword Value(intptr_t i) {
-      return Instructions::EntryPoint(
-          Instructions::RawCast(array_.At(2 * i + 1)));
-    }
-
-    const GrowableObjectArray& array_;
-    Object& object_;
-    Object& object2_;
-  };
-
-  Sorter sorter(array_);
-  sorter.Sort();
-}
-
-intptr_t TypeTestingStubFinder::LookupInSortedArray(uword entry_point) const {
-  intptr_t left = 0;
-  intptr_t right = array_.Length() / 2 - 1;
-
-  while (left <= right) {
-    const intptr_t mid = left + (right - left) / 2;
-    RawInstructions* instr = Instructions::RawCast(array_.At(2 * mid + 1));
-    const uword mid_value = Instructions::EntryPoint(instr);
-
-    if (entry_point < mid_value) {
-      right = mid - 1;
-    } else if (mid_value == entry_point) {
-      return mid;
-    } else {
-      left = mid + 1;
-    }
-  }
-
-  // The caller should only call this function if [entry_point] is a real type
-  // testing entrypoint, in which case it must be guaranteed to find it.
-  UNREACHABLE();
-  return NULL;
-}
-
 #if !defined(TARGET_ARCH_DBC) && !defined(TARGET_ARCH_IA32)
 #if !defined(DART_PRECOMPILED_RUNTIME)
 
-RawInstructions* TypeTestingStubGenerator::BuildCodeForType(const Type& type) {
+RawCode* TypeTestingStubGenerator::BuildCodeForType(const Type& type) {
   HierarchyInfo* hi = Thread::Current()->hierarchy_info();
   ASSERT(hi != NULL);
 
-  if (!hi->CanUseSubtypeRangeCheckFor(type)) {
-    if (!hi->CanUseGenericSubtypeRangeCheckFor(type)) {
-      return Instructions::null();
-    }
+  if (!hi->CanUseSubtypeRangeCheckFor(type) &&
+      !hi->CanUseGenericSubtypeRangeCheckFor(type)) {
+    return Code::null();
   }
 
   const Class& type_class = Class::Handle(type.type_class());
@@ -350,6 +197,7 @@
                                    : Code::PoolAttachment::kAttachPool;
   const Code& code = Code::Handle(Code::FinalizeCode(
       name, nullptr, &assembler, pool_attachment, false /* optimized */));
+  code.set_owner(type);
 #ifndef PRODUCT
   if (FLAG_support_disassembler && FLAG_disassemble_stubs) {
     LogBlock lb;
@@ -364,7 +212,7 @@
   }
 #endif  // !PRODUCT
 
-  return code.instructions();
+  return code.raw();
 }
 
 void TypeTestingStubGenerator::BuildOptimizedTypeTestStubFastCases(
@@ -1003,19 +851,45 @@
 #if !defined(PRODUCT) && !defined(DART_PRECOMPILED_RUNTIME)
 
 void DeoptimizeTypeTestingStubs() {
-  auto isolate = Isolate::Current();
-  auto& tts_array = GrowableObjectArray::Handle(
-      isolate->object_store()->type_testing_stubs());
-  auto& type = AbstractType::Handle();
-  auto& instr = Instructions::Handle();
+  class CollectTypes : public ObjectVisitor {
+   public:
+    CollectTypes(GrowableArray<AbstractType*>* types, Zone* zone)
+        : types_(types), object_(Object::Handle(zone)), zone_(zone) {}
+
+    void VisitObject(RawObject* object) {
+      if (object->IsPseudoObject()) {
+        // Cannot even be wrapped in handles.
+        return;
+      }
+      object_ = object;
+      if (object_.IsAbstractType()) {
+        types_->Add(
+            &AbstractType::Handle(zone_, AbstractType::RawCast(object)));
+      }
+    }
+
+   private:
+    GrowableArray<AbstractType*>* types_;
+    Object& object_;
+    Zone* zone_;
+  };
+
+  Thread* thread = Thread::Current();
+  TIMELINE_DURATION(thread, Isolate, "DeoptimizeTypeTestingStubs");
+  HANDLESCOPE(thread);
+  Zone* zone = thread->zone();
+  GrowableArray<AbstractType*> types;
+  {
+    HeapIterationScope iter(thread);
+    CollectTypes visitor(&types, zone);
+    iter.IterateObjects(&visitor);
+  }
 
   TypeTestingStubGenerator generator;
-  if (!tts_array.IsNull()) {
-    for (intptr_t i = 0; i < tts_array.Length(); i += 2) {
-      type ^= tts_array.At(i);
-      instr = generator.DefaultCodeForType(type);
-      type.SetTypeTestingStub(instr);
-    }
+  Code& code = Code::Handle(zone);
+  for (intptr_t i = 0; i < types.length(); i++) {
+    code = generator.DefaultCodeForType(*types[i]);
+    types[i]->SetTypeTestingStub(code);
   }
 }
 
diff --git a/runtime/vm/type_testing_stubs.h b/runtime/vm/type_testing_stubs.h
index e8c7044..2f091d1 100644
--- a/runtime/vm/type_testing_stubs.h
+++ b/runtime/vm/type_testing_stubs.h
@@ -38,8 +38,8 @@
   // During bootstrapping it will return `null` for a whitelisted set of types,
   // otherwise it will return a default stub which tail-calls
   // subtypingtest/runtime code.
-  static RawInstructions* DefaultCodeForType(const AbstractType& type,
-                                             bool lazy_specialize = true);
+  static RawCode* DefaultCodeForType(const AbstractType& type,
+                                     bool lazy_specialize = true);
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
   static void SpecializeStubFor(Thread* thread, const AbstractType& type);
@@ -49,12 +49,12 @@
 
   // Creates new stub for [type] (and registers the tuple in object store
   // array) or returns default stub.
-  RawInstructions* OptimizedCodeForType(const AbstractType& type);
+  RawCode* OptimizedCodeForType(const AbstractType& type);
 
  private:
 #if !defined(TARGET_ARCH_DBC) && !defined(TARGET_ARCH_IA32)
 #if !defined(DART_PRECOMPILED_RUNTIME)
-  RawInstructions* BuildCodeForType(const Type& type);
+  RawCode* BuildCodeForType(const Type& type);
   static void BuildOptimizedTypeTestStub(Assembler* assembler,
                                          HierarchyInfo* hi,
                                          const Type& type,
@@ -120,42 +120,6 @@
 
   TypeTestingStubNamer namer_;
   ObjectStore* object_store_;
-  GrowableObjectArray& array_;
-  Instructions& instr_;
-};
-
-// It is assumed that the caller ensures, while this object lives there is no
-// other access to [Isolate::Current()->object_store()->type_testing_stubs()].
-class TypeTestingStubFinder {
- public:
-  TypeTestingStubFinder();
-
-  // When serializing an AOT snapshot via our clustered snapshot writer, we
-  // write out references to the [Instructions] object for all the
-  // [AbstractType] objects we encounter.
-  //
-  // This method is used for this mapping of stub entrypoint addresses to the
-  // corresponding [Instructions] object.
-  RawInstructions* LookupByAddresss(uword entry_point) const;
-
-  // When generating an AOT snapshot as an assembly file (i.e. ".S" file) we
-  // need to generate labels for the type testing stubs.
-  //
-  // This method maps stub entrypoint addresses to meaningful names.
-  const char* StubNameFromAddresss(uword entry_point) const;
-
- private:
-  // Sorts the tuples in [array_] according to entrypoint.
-  void SortTableForFastLookup();
-
-  // Returns the tuple index where [entry_point] was found.
-  intptr_t LookupInSortedArray(uword entry_point) const;
-
-  TypeTestingStubNamer namer_;
-  GrowableObjectArray& array_;
-  AbstractType& type_;
-  Code& code_;
-  Instructions& instr_;
 };
 
 template <typename T>