Version 2.12.0-274.0.dev

Merge commit '27deca1457ad6b79eb3d22f16ab45b30b754e2eb' into 'dev'
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index 2579d39..b960315 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -441,6 +441,11 @@
             CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY,
             errorNode,
           );
+        } else if (errorNode is BlockFunctionBody) {
+          errorReporter.reportErrorForToken(
+            CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY,
+            errorNode.block.leftBracket,
+          );
         } else {
           errorReporter.reportErrorForNode(
             CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY,
diff --git a/pkg/analyzer/test/src/diagnostics/body_might_complete_normally_test.dart b/pkg/analyzer/test/src/diagnostics/body_might_complete_normally_test.dart
index b366a0d..b46c519 100644
--- a/pkg/analyzer/test/src/diagnostics/body_might_complete_normally_test.dart
+++ b/pkg/analyzer/test/src/diagnostics/body_might_complete_normally_test.dart
@@ -171,23 +171,23 @@
 
   test_functionExpression_future_int_blockBody_async() async {
     await assertErrorsInCode(r'''
-main() {
+void f() {
   Future<int> Function() foo = () async {};
   foo;
 }
 ''', [
-      error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 43, 8),
+      error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 51, 1),
     ]);
   }
 
   test_functionExpression_future_void_blockBody() async {
     await assertErrorsInCode(r'''
-main() {
+void f() {
   Future<void> Function() foo = () {};
   foo;
 }
 ''', [
-      error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 44, 2),
+      error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 46, 1),
     ]);
   }
 
@@ -202,13 +202,13 @@
 
   test_functionExpression_notNullable_blockBody() async {
     await assertErrorsInCode(r'''
-main() {
+void f() {
   int Function() foo = () {
   };
   foo;
 }
 ''', [
-      error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 35, 5),
+      error(CompileTimeErrorCode.BODY_MIGHT_COMPLETE_NORMALLY, 37, 1),
     ]);
   }
 
diff --git a/runtime/tests/vm/dart/split_literals.dart b/runtime/tests/vm/dart/split_literals.dart
deleted file mode 100644
index b71e9bd..0000000
--- a/runtime/tests/vm/dart/split_literals.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "split_literals_deferred.dart" deferred as lib;
-
-class Box {
-  final contents;
-  const Box(this.contents);
-  String toString() => "Box($contents)";
-}
-
-main() async {
-  print("Root literal!");
-  print(const <String>["Root literal in a list!"]);
-  print(const <String, String>{"key": "Root literal in a map!"});
-  print(const Box("Root literal in a box!"));
-
-  await lib.loadLibrary();
-  lib.foo();
-}
diff --git a/runtime/tests/vm/dart/split_literals_deferred.dart b/runtime/tests/vm/dart/split_literals_deferred.dart
deleted file mode 100644
index 015ae1d..0000000
--- a/runtime/tests/vm/dart/split_literals_deferred.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "split_literals.dart";
-
-void foo() {
-  print("Deferred literal!");
-  print(const <String>["Deferred literal in a list!"]);
-  print(const <String, String>{"key": "Deferred literal in a map!"});
-  print(const Box("Deferred literal in a box!"));
-}
diff --git a/runtime/tests/vm/dart/split_literals_test.dart b/runtime/tests/vm/dart/split_literals_test.dart
deleted file mode 100644
index 6cbc01b..0000000
--- a/runtime/tests/vm/dart/split_literals_test.dart
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "dart:convert";
-import "dart:io";
-
-import "package:expect/expect.dart";
-import "package:path/path.dart" as path;
-
-import "use_flag_test_helper.dart";
-
-main(List<String> args) async {
-  if (!isAOTRuntime) {
-    return; // Running in JIT: AOT binaries not available.
-  }
-
-  if (Platform.isAndroid) {
-    return; // SDK tree not available on the test device.
-  }
-
-  // These are the tools we need to be available to run on a given platform:
-  if (!File(platformDill).existsSync()) {
-    throw "Cannot run test as $platformDill does not exist";
-  }
-  if (!await testExecutable(genSnapshot)) {
-    throw "Cannot run test as $genSnapshot not available";
-  }
-
-  sanitizedPartitioning(manifest) {
-    // Filter core libraries, relativize URIs, and sort to make the results less
-    // sensitive to compiler or test harness changes.
-    print(manifest);
-    var units = <List<String>>[];
-    for (var unit in manifest['loadingUnits']) {
-      var uris = <String>[];
-      for (var uri in unit['libraries']) {
-        if (uri.startsWith("dart:")) continue;
-        uris.add(Uri.parse(uri).pathSegments.last);
-      }
-      uris.sort((a, b) => a.compareTo(b));
-      units.add(uris);
-    }
-    units.sort((a, b) => a.first.compareTo(b.first));
-    print(units);
-    return units;
-  }
-
-  await withTempDir("split-literals-test", (String tempDir) async {
-    final source =
-        path.join(sdkDir, "runtime/tests/vm/dart_2/split_literals.dart");
-    final dill = path.join(tempDir, "split_literals.dart.dill");
-    final snapshot = path.join(tempDir, "split_literals.so");
-    final manifest = path.join(tempDir, "split_literals.txt");
-    final deferredSnapshot = snapshot + "-2.part.so";
-
-    // Compile source to kernel.
-    await run(genKernel, <String>[
-      "--aot",
-      "--platform=$platformDill",
-      "-o",
-      dill,
-      source,
-    ]);
-
-    // Compile kernel to ELF.
-    await run(genSnapshot, <String>[
-      "--use_bare_instructions=false", //# object: ok
-      "--use_bare_instructions=true", //# bare: ok
-      "--snapshot-kind=app-aot-elf",
-      "--elf=$snapshot",
-      "--loading-unit-manifest=$manifest",
-      dill,
-    ]);
-    var manifestContent = jsonDecode(await new File(manifest).readAsString());
-    Expect.equals(2, manifestContent["loadingUnits"].length);
-    // Note package:expect doesn't do deep equals on collections.
-    Expect.equals(
-        "[[split_literals.dart],"
-        " [split_literals_deferred.dart]]",
-        sanitizedPartitioning(manifestContent).toString());
-    Expect.isTrue(await new File(deferredSnapshot).exists());
-
-    bool containsSubsequence(haystack, needle) {
-      outer:
-      for (var i = 0, n = haystack.length - needle.length; i < n; i++) {
-        for (var j = 0; j < needle.length; j++) {
-          if (haystack[i + j] != needle.codeUnitAt(j)) continue outer;
-        }
-        return true;
-      }
-      return false;
-    }
-
-    var unit_1 = await new File(snapshot).readAsBytes();
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal!"));
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal in a list!"));
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal in a map!"));
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal in a box!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a list!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a map!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a box!"));
-
-    var unit_2 = await new File(deferredSnapshot).readAsBytes();
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal!"));
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a list!"));
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a map!"));
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a box!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a list!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a map!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a box!"));
-  });
-}
diff --git a/runtime/tests/vm/dart_2/split_literals.dart b/runtime/tests/vm/dart_2/split_literals.dart
deleted file mode 100644
index b71e9bd..0000000
--- a/runtime/tests/vm/dart_2/split_literals.dart
+++ /dev/null
@@ -1,21 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "split_literals_deferred.dart" deferred as lib;
-
-class Box {
-  final contents;
-  const Box(this.contents);
-  String toString() => "Box($contents)";
-}
-
-main() async {
-  print("Root literal!");
-  print(const <String>["Root literal in a list!"]);
-  print(const <String, String>{"key": "Root literal in a map!"});
-  print(const Box("Root literal in a box!"));
-
-  await lib.loadLibrary();
-  lib.foo();
-}
diff --git a/runtime/tests/vm/dart_2/split_literals_deferred.dart b/runtime/tests/vm/dart_2/split_literals_deferred.dart
deleted file mode 100644
index 015ae1d..0000000
--- a/runtime/tests/vm/dart_2/split_literals_deferred.dart
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "split_literals.dart";
-
-void foo() {
-  print("Deferred literal!");
-  print(const <String>["Deferred literal in a list!"]);
-  print(const <String, String>{"key": "Deferred literal in a map!"});
-  print(const Box("Deferred literal in a box!"));
-}
diff --git a/runtime/tests/vm/dart_2/split_literals_test.dart b/runtime/tests/vm/dart_2/split_literals_test.dart
deleted file mode 100644
index 6cbc01b..0000000
--- a/runtime/tests/vm/dart_2/split_literals_test.dart
+++ /dev/null
@@ -1,115 +0,0 @@
-// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import "dart:convert";
-import "dart:io";
-
-import "package:expect/expect.dart";
-import "package:path/path.dart" as path;
-
-import "use_flag_test_helper.dart";
-
-main(List<String> args) async {
-  if (!isAOTRuntime) {
-    return; // Running in JIT: AOT binaries not available.
-  }
-
-  if (Platform.isAndroid) {
-    return; // SDK tree not available on the test device.
-  }
-
-  // These are the tools we need to be available to run on a given platform:
-  if (!File(platformDill).existsSync()) {
-    throw "Cannot run test as $platformDill does not exist";
-  }
-  if (!await testExecutable(genSnapshot)) {
-    throw "Cannot run test as $genSnapshot not available";
-  }
-
-  sanitizedPartitioning(manifest) {
-    // Filter core libraries, relativize URIs, and sort to make the results less
-    // sensitive to compiler or test harness changes.
-    print(manifest);
-    var units = <List<String>>[];
-    for (var unit in manifest['loadingUnits']) {
-      var uris = <String>[];
-      for (var uri in unit['libraries']) {
-        if (uri.startsWith("dart:")) continue;
-        uris.add(Uri.parse(uri).pathSegments.last);
-      }
-      uris.sort((a, b) => a.compareTo(b));
-      units.add(uris);
-    }
-    units.sort((a, b) => a.first.compareTo(b.first));
-    print(units);
-    return units;
-  }
-
-  await withTempDir("split-literals-test", (String tempDir) async {
-    final source =
-        path.join(sdkDir, "runtime/tests/vm/dart_2/split_literals.dart");
-    final dill = path.join(tempDir, "split_literals.dart.dill");
-    final snapshot = path.join(tempDir, "split_literals.so");
-    final manifest = path.join(tempDir, "split_literals.txt");
-    final deferredSnapshot = snapshot + "-2.part.so";
-
-    // Compile source to kernel.
-    await run(genKernel, <String>[
-      "--aot",
-      "--platform=$platformDill",
-      "-o",
-      dill,
-      source,
-    ]);
-
-    // Compile kernel to ELF.
-    await run(genSnapshot, <String>[
-      "--use_bare_instructions=false", //# object: ok
-      "--use_bare_instructions=true", //# bare: ok
-      "--snapshot-kind=app-aot-elf",
-      "--elf=$snapshot",
-      "--loading-unit-manifest=$manifest",
-      dill,
-    ]);
-    var manifestContent = jsonDecode(await new File(manifest).readAsString());
-    Expect.equals(2, manifestContent["loadingUnits"].length);
-    // Note package:expect doesn't do deep equals on collections.
-    Expect.equals(
-        "[[split_literals.dart],"
-        " [split_literals_deferred.dart]]",
-        sanitizedPartitioning(manifestContent).toString());
-    Expect.isTrue(await new File(deferredSnapshot).exists());
-
-    bool containsSubsequence(haystack, needle) {
-      outer:
-      for (var i = 0, n = haystack.length - needle.length; i < n; i++) {
-        for (var j = 0; j < needle.length; j++) {
-          if (haystack[i + j] != needle.codeUnitAt(j)) continue outer;
-        }
-        return true;
-      }
-      return false;
-    }
-
-    var unit_1 = await new File(snapshot).readAsBytes();
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal!"));
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal in a list!"));
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal in a map!"));
-    Expect.isTrue(containsSubsequence(unit_1, "Root literal in a box!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a list!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a map!"));
-    Expect.isTrue(!containsSubsequence(unit_1, "Deferred literal in a box!"));
-
-    var unit_2 = await new File(deferredSnapshot).readAsBytes();
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal!"));
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a list!"));
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a map!"));
-    Expect.isTrue(!containsSubsequence(unit_2, "Root literal in a box!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a list!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a map!"));
-    Expect.isTrue(containsSubsequence(unit_2, "Deferred literal in a box!"));
-  });
-}
diff --git a/runtime/vm/clustered_snapshot.cc b/runtime/vm/clustered_snapshot.cc
index 12ec76e..ef22f90 100644
--- a/runtime/vm/clustered_snapshot.cc
+++ b/runtime/vm/clustered_snapshot.cc
@@ -289,7 +289,7 @@
   ClassDeserializationCluster() : DeserializationCluster("Class") {}
   ~ClassDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     predefined_start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     intptr_t count = d->ReadUnsigned();
@@ -311,7 +311,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     ClassTable* table = d->isolate_group()->class_table();
 
     for (intptr_t id = predefined_start_index_; id < predefined_stop_index_;
@@ -464,7 +464,7 @@
       : DeserializationCluster("TypeArguments") {}
   ~TypeArgumentsDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -476,13 +476,13 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TypeArgumentsPtr type_args = static_cast<TypeArgumentsPtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
       Deserializer::InitializeHeader(type_args, kTypeArgumentsCid,
                                      TypeArguments::InstanceSize(length),
-                                     stamp_canonical);
+                                     is_canonical);
       type_args->untag()->length_ = Smi::New(length);
       type_args->untag()->hash_ = Smi::New(d->Read<int32_t>());
       type_args->untag()->nullability_ = Smi::New(d->ReadUnsigned());
@@ -494,15 +494,22 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      Thread* thread = Thread::Current();
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
+      CanonicalTypeArgumentsSet table(
+          d->zone(),
+          d->isolate_group()->object_store()->canonical_type_arguments());
       TypeArguments& type_arg = TypeArguments::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type_arg ^= refs.At(i);
-        type_arg = type_arg.Canonicalize(thread, nullptr);
-        refs.SetAt(i, type_arg);
+        ASSERT(type_arg.IsCanonical());
+        bool present = table.Insert(type_arg);
+        // Two recursive types with different topology (and hashes) may be
+        // equal.
+        ASSERT(!present || type_arg.IsRecursive());
       }
+      d->isolate_group()->object_store()->set_canonical_type_arguments(
+          table.Release());
     }
   }
 };
@@ -551,7 +558,7 @@
   PatchClassDeserializationCluster() : DeserializationCluster("PatchClass") {}
   ~PatchClassDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -562,8 +569,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       PatchClassPtr cls = static_cast<PatchClassPtr>(d->Ref(id));
       Deserializer::InitializeHeader(cls, kPatchClassCid,
@@ -661,7 +667,7 @@
   FunctionDeserializationCluster() : DeserializationCluster("Function") {}
   ~FunctionDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -671,8 +677,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     Snapshot::Kind kind = d->kind();
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
@@ -720,7 +725,7 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (d->kind() == Snapshot::kFullAOT) {
       Function& func = Function::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
@@ -811,7 +816,7 @@
   ClosureDataDeserializationCluster() : DeserializationCluster("ClosureData") {}
   ~ClosureDataDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -822,8 +827,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ClosureDataPtr data = static_cast<ClosureDataPtr>(d->Ref(id));
       Deserializer::InitializeHeader(data, kClosureDataCid,
@@ -893,7 +897,7 @@
       : DeserializationCluster("FfiTrampolineData") {}
   ~FfiTrampolineDataDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -904,8 +908,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       FfiTrampolineDataPtr data = static_cast<FfiTrampolineDataPtr>(d->Ref(id));
       Deserializer::InitializeHeader(data, kFfiTrampolineDataCid,
@@ -1012,7 +1015,7 @@
   FieldDeserializationCluster() : DeserializationCluster("Field") {}
   ~FieldDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -1022,8 +1025,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     Snapshot::Kind kind = d->kind();
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
@@ -1079,7 +1081,7 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     Field& field = Field::Handle(d->zone());
     if (!IsolateGroup::Current()->use_field_guards()) {
       for (intptr_t i = start_index_; i < stop_index_; i++) {
@@ -1155,7 +1157,7 @@
   ScriptDeserializationCluster() : DeserializationCluster("Script") {}
   ~ScriptDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -1165,8 +1167,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ScriptPtr script = static_cast<ScriptPtr>(d->Ref(id));
       Deserializer::InitializeHeader(script, kScriptCid,
@@ -1231,7 +1232,7 @@
   LibraryDeserializationCluster() : DeserializationCluster("Library") {}
   ~LibraryDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -1241,8 +1242,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       LibraryPtr lib = static_cast<LibraryPtr>(d->Ref(id));
       Deserializer::InitializeHeader(lib, kLibraryCid, Library::InstanceSize());
@@ -1304,7 +1304,7 @@
   NamespaceDeserializationCluster() : DeserializationCluster("Namespace") {}
   ~NamespaceDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -1314,8 +1314,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       NamespacePtr ns = static_cast<NamespacePtr>(d->Ref(id));
       Deserializer::InitializeHeader(ns, kNamespaceCid,
@@ -1371,7 +1370,7 @@
       : DeserializationCluster("KernelProgramInfo") {}
   ~KernelProgramInfoDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -1382,8 +1381,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       KernelProgramInfoPtr info = static_cast<KernelProgramInfoPtr>(d->Ref(id));
       Deserializer::InitializeHeader(info, kKernelProgramInfoCid,
@@ -1393,7 +1391,7 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     Array& array = Array::Handle(d->zone());
     KernelProgramInfo& info = KernelProgramInfo::Handle(d->zone());
     for (intptr_t id = start_index_; id < stop_index_; id++) {
@@ -1419,26 +1417,9 @@
       objects_.Add(code);
     }
 
-    if (s->kind() == Snapshot::kFullAOT && FLAG_use_bare_instructions) {
-      if (FLAG_retain_function_objects) {
-        ObjectPoolPtr pool = code->untag()->object_pool_;
-        if ((pool != ObjectPool::null()) && s->InCurrentLoadingUnit(code)) {
-          const intptr_t length = pool->untag()->length_;
-          uint8_t* entry_bits = pool->untag()->entry_bits();
-          for (intptr_t i = 0; i < length; i++) {
-            auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
-            if (entry_type == ObjectPool::EntryType::kTaggedObject) {
-              s->Push(pool->untag()->data()[i].raw_obj_);
-            }
-          }
-        }
-      }
-    } else {
-      if (s->InCurrentLoadingUnit(code->untag()->object_pool_)) {
-        s->Push(code->untag()->object_pool_);
-      }
+    if (!(s->kind() == Snapshot::kFullAOT && FLAG_use_bare_instructions)) {
+      s->Push(code->untag()->object_pool_);
     }
-
     s->Push(code->untag()->owner_);
     s->Push(code->untag()->exception_handlers_);
     s->Push(code->untag()->pc_descriptors_);
@@ -1550,6 +1531,20 @@
   }
 
   void WriteAlloc(Serializer* s) {
+    Sort(&objects_);
+    auto loading_units = s->loading_units();
+    if (loading_units != nullptr) {
+      for (intptr_t i = LoadingUnit::kRootId + 1; i < loading_units->length();
+           i++) {
+        auto unit_objects = loading_units->At(i)->deferred_objects();
+        Sort(unit_objects);
+        for (intptr_t j = 0; j < unit_objects->length(); j++) {
+          deferred_objects_.Add(unit_objects->At(j)->ptr());
+        }
+      }
+    }
+    s->PrepareInstructions(&objects_);
+
     s->WriteCid(kCodeCid);
     const intptr_t count = objects_.length();
     s->WriteUnsigned(count);
@@ -1610,11 +1605,7 @@
     // No need to write object pool out if we are producing full AOT
     // snapshot with bare instructions.
     if (!(kind == Snapshot::kFullAOT && FLAG_use_bare_instructions)) {
-      if (s->InCurrentLoadingUnit(code->untag()->object_pool_)) {
-        WriteField(code, object_pool_);
-      } else {
-        WriteFieldValue(object_pool_, ObjectPool::null());
-      }
+      WriteField(code, object_pool_);
 #if defined(DART_PRECOMPILER)
     } else if (FLAG_write_v8_snapshot_profile_to != nullptr &&
                code->untag()->object_pool_ != ObjectPool::null()) {
@@ -1695,8 +1686,7 @@
     s->Write<int32_t>(code->untag()->state_bits_);
   }
 
-  GrowableArray<CodePtr>* objects() { return &objects_; }
-  GrowableArray<CodePtr>* deferred_objects() { return &deferred_objects_; }
+  GrowableArray<CodePtr>* discovered_objects() { return &objects_; }
 
   // Some code objects would have their owners dropped from the snapshot,
   // which makes it is impossible to recover program structure when
@@ -1742,7 +1732,7 @@
   CodeDeserializationCluster() : DeserializationCluster("Code") {}
   ~CodeDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     PageSpace* old_space = d->heap()->old_space();
     start_index_ = d->next_index();
     const intptr_t count = d->ReadUnsigned();
@@ -1760,8 +1750,7 @@
     deferred_stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ReadFill(d, id, false);
     }
@@ -1816,7 +1805,7 @@
     code->untag()->state_bits_ = d->Read<int32_t>();
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     d->EndInstructions(refs, start_index_, stop_index_);
 
 #if !defined(PRODUCT)
@@ -1864,17 +1853,12 @@
     ObjectPoolPtr pool = ObjectPool::RawCast(object);
     objects_.Add(pool);
 
-    if (s->kind() == Snapshot::kFullAOT && FLAG_use_bare_instructions &&
-        FLAG_retain_function_objects) {
-      // Treat pool as weak.
-    } else {
-      const intptr_t length = pool->untag()->length_;
-      uint8_t* entry_bits = pool->untag()->entry_bits();
-      for (intptr_t i = 0; i < length; i++) {
-        auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
-        if (entry_type == ObjectPool::EntryType::kTaggedObject) {
-          s->Push(pool->untag()->data()[i].raw_obj_);
-        }
+    const intptr_t length = pool->untag()->length_;
+    uint8_t* entry_bits = pool->untag()->entry_bits();
+    for (intptr_t i = 0; i < length; i++) {
+      auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
+      if (entry_type == ObjectPool::EntryType::kTaggedObject) {
+        s->Push(pool->untag()->data()[i].raw_obj_);
       }
     }
   }
@@ -1893,9 +1877,6 @@
   }
 
   void WriteFill(Serializer* s) {
-    bool weak = s->kind() == Snapshot::kFullAOT && FLAG_use_bare_instructions &&
-                FLAG_retain_function_objects;
-
     const intptr_t count = objects_.length();
     for (intptr_t i = 0; i < count; i++) {
       ObjectPoolPtr pool = objects_[i];
@@ -1916,12 +1897,7 @@
               s->WriteElementRef(StubCode::CallBootstrapNative().ptr(), j);
               break;
             }
-            if (weak && !s->HasRef(entry.raw_obj_)) {
-              // Any value will do, but null has the shortest id.
-              s->WriteElementRef(Object::null(), j);
-            } else {
-              s->WriteElementRef(entry.raw_obj_, j);
-            }
+            s->WriteElementRef(entry.raw_obj_, j);
             break;
           }
           case ObjectPool::EntryType::kImmediate: {
@@ -1950,7 +1926,7 @@
   ObjectPoolDeserializationCluster() : DeserializationCluster("ObjectPool") {}
   ~ObjectPoolDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -1962,10 +1938,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
-    fill_position_ = d->position();
-
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id += 1) {
       const intptr_t length = d->ReadUnsigned();
       ObjectPoolPtr pool = static_cast<ObjectPoolPtr>(d->Ref(id + 0));
@@ -1995,41 +1968,6 @@
       }
     }
   }
-
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    intptr_t restore_position = d->position();
-    d->set_position(fill_position_);
-
-    ObjectPool& pool = ObjectPool::Handle();
-    Object& entry = Object::Handle();
-    for (intptr_t id = start_index_; id < stop_index_; id += 1) {
-      pool ^= refs.At(id);
-      const intptr_t length = d->ReadUnsigned();
-      for (intptr_t j = 0; j < length; j++) {
-        const uint8_t entry_bits = d->Read<uint8_t>();
-        switch (ObjectPool::TypeBits::decode(entry_bits)) {
-          case ObjectPool::EntryType::kTaggedObject:
-            entry = refs.At(d->ReadUnsigned());
-            pool.SetObjectAt(j, entry);
-            break;
-          case ObjectPool::EntryType::kImmediate:
-            d->Read<intptr_t>();
-            break;
-          case ObjectPool::EntryType::kNativeFunction: {
-            // Read nothing.
-            break;
-          }
-          default:
-            UNREACHABLE();
-        }
-      }
-    }
-
-    d->set_position(restore_position);
-  }
-
- private:
-  intptr_t fill_position_ = 0;
 };
 
 #if defined(DART_PRECOMPILER)
@@ -2152,7 +2090,7 @@
       : DeserializationCluster("WeakSerializationReference") {}
   ~WeakSerializationReferenceDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2166,8 +2104,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       auto const ref = static_cast<WeakSerializationReferencePtr>(d->Ref(id));
       Deserializer::InitializeHeader(
@@ -2226,7 +2163,7 @@
       : DeserializationCluster("PcDescriptors") {}
   ~PcDescriptorsDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2238,8 +2175,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id += 1) {
       const intptr_t length = d->ReadUnsigned();
       PcDescriptorsPtr desc = static_cast<PcDescriptorsPtr>(d->Ref(id));
@@ -2321,7 +2257,7 @@
       : DeserializationCluster("ROData"), cid_(cid) {}
   ~RODataDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     intptr_t count = d->ReadUnsigned();
     uint32_t running_offset = 0;
@@ -2332,25 +2268,21 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     // No-op.
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize && IsStringClassId(cid_)) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && IsStringClassId(cid_) &&
+        (d->isolate() != Dart::vm_isolate())) {
       CanonicalStringSet table(
           d->zone(), d->isolate_group()->object_store()->symbol_table());
       String& str = String::Handle(d->zone());
-      String& str2 = String::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         str ^= refs.At(i);
-        str2 ^= table.InsertOrGet(str);
-        if (str.ptr() == str2.ptr()) {
-          // str.SetCanonical();
-        } else {
-          FATAL("Lost canonicalization race");
-          refs.SetAt(i, str2);
-        }
+        ASSERT(str.IsCanonical());
+        bool present = table.Insert(str);
+        ASSERT(!present);
       }
       d->isolate_group()->object_store()->set_symbol_table(table.Release());
     }
@@ -2417,7 +2349,7 @@
       : DeserializationCluster("ExceptionHandlers") {}
   ~ExceptionHandlersDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2429,8 +2361,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ExceptionHandlersPtr handlers =
           static_cast<ExceptionHandlersPtr>(d->Ref(id));
@@ -2506,7 +2437,7 @@
   ContextDeserializationCluster() : DeserializationCluster("Context") {}
   ~ContextDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2518,8 +2449,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ContextPtr context = static_cast<ContextPtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
@@ -2584,7 +2514,7 @@
       : DeserializationCluster("ContextScope") {}
   ~ContextScopeDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2596,8 +2526,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ContextScopePtr scope = static_cast<ContextScopePtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
@@ -2653,7 +2582,7 @@
       : DeserializationCluster("UnlinkedCall") {}
   ~UnlinkedCallDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2664,8 +2593,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       UnlinkedCallPtr unlinked = static_cast<UnlinkedCallPtr>(d->Ref(id));
       Deserializer::InitializeHeader(unlinked, kUnlinkedCallCid,
@@ -2722,7 +2650,7 @@
   ICDataDeserializationCluster() : DeserializationCluster("ICData") {}
   ~ICDataDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2732,8 +2660,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ICDataPtr ic = static_cast<ICDataPtr>(d->Ref(id));
       Deserializer::InitializeHeader(ic, kICDataCid, ICData::InstanceSize());
@@ -2788,7 +2715,7 @@
       : DeserializationCluster("MegamorphicCache") {}
   ~MegamorphicCacheDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2799,8 +2726,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       MegamorphicCachePtr cache = static_cast<MegamorphicCachePtr>(d->Ref(id));
       Deserializer::InitializeHeader(cache, kMegamorphicCacheCid,
@@ -2811,7 +2737,7 @@
   }
 
 #if defined(DART_PRECOMPILED_RUNTIME)
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     if (FLAG_use_bare_instructions) {
       // By default, every megamorphic call site will load the target
       // [Function] from the hash table and call indirectly via loading the
@@ -2880,7 +2806,7 @@
       : DeserializationCluster("SubtypeTestCache") {}
   ~SubtypeTestCacheDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2891,8 +2817,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       SubtypeTestCachePtr cache = static_cast<SubtypeTestCachePtr>(d->Ref(id));
       Deserializer::InitializeHeader(cache, kSubtypeTestCacheCid,
@@ -2944,7 +2869,7 @@
   LoadingUnitDeserializationCluster() : DeserializationCluster("LoadingUnit") {}
   ~LoadingUnitDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -2955,8 +2880,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       LoadingUnitPtr unit = static_cast<LoadingUnitPtr>(d->Ref(id));
       Deserializer::InitializeHeader(unit, kLoadingUnitCid,
@@ -3015,7 +2939,7 @@
       : DeserializationCluster("LanguageError") {}
   ~LanguageErrorDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3026,8 +2950,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       LanguageErrorPtr error = static_cast<LanguageErrorPtr>(d->Ref(id));
       Deserializer::InitializeHeader(error, kLanguageErrorCid,
@@ -3083,7 +3006,7 @@
       : DeserializationCluster("UnhandledException") {}
   ~UnhandledExceptionDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3094,8 +3017,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       UnhandledExceptionPtr exception =
           static_cast<UnhandledExceptionPtr>(d->Ref(id));
@@ -3204,35 +3126,13 @@
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
-class AbstractInstanceDeserializationCluster : public DeserializationCluster {
- protected:
-  explicit AbstractInstanceDeserializationCluster(const char* name)
-      : DeserializationCluster(name) {}
-
- public:
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      Thread* thread = Thread::Current();
-      SafepointMutexLocker ml(
-          thread->isolate_group()->constant_canonicalization_mutex());
-      Instance& instance = Instance::Handle(d->zone());
-      for (intptr_t i = start_index_; i < stop_index_; i++) {
-        instance ^= refs.At(i);
-        instance = instance.CanonicalizeLocked(thread);
-        refs.SetAt(i, instance);
-      }
-    }
-  }
-};
-
-class InstanceDeserializationCluster
-    : public AbstractInstanceDeserializationCluster {
+class InstanceDeserializationCluster : public DeserializationCluster {
  public:
   explicit InstanceDeserializationCluster(intptr_t cid)
-      : AbstractInstanceDeserializationCluster("Instance"), cid_(cid) {}
+      : DeserializationCluster("Instance"), cid_(cid) {}
   ~InstanceDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3246,7 +3146,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     intptr_t next_field_offset = next_field_offset_in_words_ << kWordSizeLog2;
     intptr_t instance_size =
         Object::RoundedAllocationSize(instance_size_in_words_ * kWordSize);
@@ -3255,7 +3155,7 @@
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       InstancePtr instance = static_cast<InstancePtr>(d->Ref(id));
       Deserializer::InitializeHeader(instance, cid_, instance_size,
-                                     stamp_canonical);
+                                     is_canonical);
       intptr_t offset = Instance::NextFieldOffset();
       while (offset < next_field_offset) {
         if (unboxed_fields_bitmap.Get(offset / kWordSize)) {
@@ -3330,7 +3230,7 @@
       : DeserializationCluster("LibraryPrefix") {}
   ~LibraryPrefixDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3341,8 +3241,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       LibraryPrefixPtr prefix = static_cast<LibraryPrefixPtr>(d->Ref(id));
       Deserializer::InitializeHeader(prefix, kLibraryPrefixCid,
@@ -3425,7 +3324,7 @@
   TypeDeserializationCluster() : DeserializationCluster("Type") {}
   ~TypeDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3435,11 +3334,11 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TypePtr type = static_cast<TypePtr>(d->Ref(id));
       Deserializer::InitializeHeader(type, kTypeCid, Type::InstanceSize(),
-                                     stamp_canonical);
+                                     is_canonical);
       ReadFromTo(type);
       const uint8_t combined = d->Read<uint8_t>();
       type->untag()->type_state_ = combined >> kNullabilityBitSize;
@@ -3447,15 +3346,20 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      Thread* thread = Thread::Current();
-      AbstractType& type = AbstractType::Handle(d->zone());
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
+      CanonicalTypeSet table(
+          d->zone(), d->isolate_group()->object_store()->canonical_types());
+      Type& type = Type::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type ^= refs.At(i);
-        type = type.Canonicalize(thread, nullptr);
-        refs.SetAt(i, type);
+        ASSERT(type.IsCanonical());
+        bool present = table.Insert(type);
+        // Two recursive types with different topology (and hashes) may be
+        // equal.
+        ASSERT(!present || type.IsRecursive());
       }
+      d->isolate_group()->object_store()->set_canonical_types(table.Release());
     }
 
     Type& type = Type::Handle(d->zone());
@@ -3536,7 +3440,7 @@
       : DeserializationCluster("FunctionType") {}
   ~FunctionTypeDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3547,12 +3451,11 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       FunctionTypePtr type = static_cast<FunctionTypePtr>(d->Ref(id));
-      Deserializer::InitializeHeader(type, kFunctionTypeCid,
-                                     FunctionType::InstanceSize(),
-                                     stamp_canonical);
+      Deserializer::InitializeHeader(
+          type, kFunctionTypeCid, FunctionType::InstanceSize(), is_canonical);
       ReadFromTo(type);
       const uint8_t combined = d->Read<uint8_t>();
       type->untag()->type_state_ = combined >> kNullabilityBitSize;
@@ -3561,15 +3464,22 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      Thread* thread = Thread::Current();
-      AbstractType& type = AbstractType::Handle(d->zone());
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
+      CanonicalFunctionTypeSet table(
+          d->zone(),
+          d->isolate_group()->object_store()->canonical_function_types());
+      FunctionType& type = FunctionType::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type ^= refs.At(i);
-        type = type.Canonicalize(thread, nullptr);
-        refs.SetAt(i, type);
+        ASSERT(type.IsCanonical());
+        bool present = table.Insert(type);
+        // Two recursive types with different topology (and hashes) may be
+        // equal.
+        ASSERT(!present || type.IsRecursive());
       }
+      d->isolate_group()->object_store()->set_canonical_function_types(
+          table.Release());
     }
 
     FunctionType& type = FunctionType::Handle(d->zone());
@@ -3632,7 +3542,7 @@
   TypeRefDeserializationCluster() : DeserializationCluster("TypeRef") {}
   ~TypeRefDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3642,16 +3552,16 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TypeRefPtr type = static_cast<TypeRefPtr>(d->Ref(id));
-      Deserializer::InitializeHeader(type, kTypeRefCid, TypeRef::InstanceSize(),
-                                     stamp_canonical);
+      Deserializer::InitializeHeader(type, kTypeRefCid,
+                                     TypeRef::InstanceSize());
       ReadFromTo(type);
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     TypeRef& type_ref = TypeRef::Handle(d->zone());
     Code& stub = Code::Handle(d->zone());
 
@@ -3731,7 +3641,7 @@
       : DeserializationCluster("TypeParameter") {}
   ~TypeParameterDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3742,12 +3652,11 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TypeParameterPtr type = static_cast<TypeParameterPtr>(d->Ref(id));
-      Deserializer::InitializeHeader(type, kTypeParameterCid,
-                                     TypeParameter::InstanceSize(),
-                                     stamp_canonical);
+      Deserializer::InitializeHeader(
+          type, kTypeParameterCid, TypeParameter::InstanceSize(), is_canonical);
       ReadFromTo(type);
       type->untag()->parameterized_class_id_ = d->Read<int32_t>();
       type->untag()->base_ = d->Read<uint16_t>();
@@ -3758,15 +3667,20 @@
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      Thread* thread = Thread::Current();
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
+      CanonicalTypeParameterSet table(
+          d->zone(),
+          d->isolate_group()->object_store()->canonical_type_parameters());
       TypeParameter& type_param = TypeParameter::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         type_param ^= refs.At(i);
-        type_param ^= type_param.Canonicalize(thread, nullptr);
-        refs.SetAt(i, type_param);
+        ASSERT(type_param.IsCanonical());
+        bool present = table.Insert(type_param);
+        ASSERT(!present);
       }
+      d->isolate_group()->object_store()->set_canonical_type_parameters(
+          table.Release());
     }
 
     TypeParameter& type_param = TypeParameter::Handle(d->zone());
@@ -3825,14 +3739,12 @@
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
-class ClosureDeserializationCluster
-    : public AbstractInstanceDeserializationCluster {
+class ClosureDeserializationCluster : public DeserializationCluster {
  public:
-  ClosureDeserializationCluster()
-      : AbstractInstanceDeserializationCluster("Closure") {}
+  ClosureDeserializationCluster() : DeserializationCluster("Closure") {}
   ~ClosureDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3842,11 +3754,11 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ClosurePtr closure = static_cast<ClosurePtr>(d->Ref(id));
       Deserializer::InitializeHeader(closure, kClosureCid,
-                                     Closure::InstanceSize(), stamp_canonical);
+                                     Closure::InstanceSize(), is_canonical);
       ReadFromTo(closure);
     }
   }
@@ -3899,7 +3811,7 @@
   MintDeserializationCluster() : DeserializationCluster("int") {}
   ~MintDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     PageSpace* old_space = d->heap()->old_space();
 
     start_index_ = d->next_index();
@@ -3912,7 +3824,7 @@
         MintPtr mint = static_cast<MintPtr>(
             AllocateUninitialized(old_space, Mint::InstanceSize()));
         Deserializer::InitializeHeader(mint, kMintCid, Mint::InstanceSize(),
-                                       stamp_canonical);
+                                       is_canonical);
         mint->untag()->value_ = value;
         d->AssignRef(mint);
       }
@@ -3920,24 +3832,19 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {}
+  void ReadFill(Deserializer* d, bool is_canonical) {}
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
       const Class& mint_cls = Class::Handle(
-          d->zone(), d->isolate_group()->object_store()->mint_class());
+          d->zone(), IsolateGroup::Current()->object_store()->mint_class());
+      mint_cls.set_constants(Object::null_array());
       Object& number = Object::Handle(d->zone());
-      Mint& number2 = Mint::Handle(d->zone());
       for (intptr_t i = start_index_; i < stop_index_; i++) {
         number = refs.At(i);
-        if (!number.IsMint()) continue;
-        number2 =
-            mint_cls.LookupCanonicalMint(d->zone(), Mint::Cast(number).value());
-        if (number2.IsNull()) {
-          number.SetCanonical();
+        if (number.IsMint()) {
+          ASSERT(number.IsCanonical());
           mint_cls.InsertCanonicalMint(d->zone(), Mint::Cast(number));
-        } else {
-          refs.SetAt(i, number2);
         }
       }
     }
@@ -3984,7 +3891,7 @@
   DoubleDeserializationCluster() : DeserializationCluster("double") {}
   ~DoubleDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -3994,35 +3901,14 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       DoublePtr dbl = static_cast<DoublePtr>(d->Ref(id));
       Deserializer::InitializeHeader(dbl, kDoubleCid, Double::InstanceSize(),
-                                     stamp_canonical);
+                                     is_canonical);
       dbl->untag()->value_ = d->Read<double>();
     }
   }
-
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      const Class& cls = Class::Handle(
-          d->zone(), d->isolate_group()->object_store()->double_class());
-      SafepointMutexLocker ml(
-          d->isolate_group()->constant_canonicalization_mutex());
-      Double& dbl = Double::Handle(d->zone());
-      Double& dbl2 = Double::Handle(d->zone());
-      for (intptr_t i = start_index_; i < stop_index_; i++) {
-        dbl ^= refs.At(i);
-        dbl2 = cls.LookupCanonicalDouble(d->zone(), dbl.value());
-        if (dbl2.IsNull()) {
-          dbl.SetCanonical();
-          cls.InsertCanonicalDouble(d->zone(), dbl);
-        } else {
-          refs.SetAt(i, dbl2);
-        }
-      }
-    }
-  }
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -4069,7 +3955,7 @@
       : DeserializationCluster("GrowableObjectArray") {}
   ~GrowableObjectArrayDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4080,13 +3966,13 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       GrowableObjectArrayPtr list =
           static_cast<GrowableObjectArrayPtr>(d->Ref(id));
       Deserializer::InitializeHeader(list, kGrowableObjectArrayCid,
                                      GrowableObjectArray::InstanceSize(),
-                                     stamp_canonical);
+                                     is_canonical);
       ReadFromTo(list);
     }
   }
@@ -4142,7 +4028,7 @@
       : DeserializationCluster("TypedData"), cid_(cid) {}
   ~TypedDataDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4155,17 +4041,15 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     intptr_t element_size = TypedData::ElementSizeInBytes(cid_);
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TypedDataPtr data = static_cast<TypedDataPtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
       const intptr_t length_in_bytes = length * element_size;
-      Deserializer::InitializeHeader(data, cid_,
-                                     TypedData::InstanceSize(length_in_bytes),
-                                     stamp_canonical);
+      Deserializer::InitializeHeader(
+          data, cid_, TypedData::InstanceSize(length_in_bytes), is_canonical);
       data->untag()->length_ = Smi::New(length);
       data->untag()->RecomputeDataField();
       uint8_t* cdata = reinterpret_cast<uint8_t*>(data->untag()->data());
@@ -4222,7 +4106,7 @@
       : DeserializationCluster("TypedDataView"), cid_(cid) {}
   ~TypedDataViewDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4233,17 +4117,16 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TypedDataViewPtr view = static_cast<TypedDataViewPtr>(d->Ref(id));
-      Deserializer::InitializeHeader(view, cid_, TypedDataView::InstanceSize());
+      Deserializer::InitializeHeader(view, cid_, TypedDataView::InstanceSize(),
+                                     is_canonical);
       ReadFromTo(view);
     }
   }
 
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    ASSERT(!canonicalize);
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
     auto& view = TypedDataView::Handle(d->zone());
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       view ^= refs.At(id);
@@ -4303,7 +4186,7 @@
       : DeserializationCluster("ExternalTypedData"), cid_(cid) {}
   ~ExternalTypedDataDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4314,8 +4197,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     intptr_t element_size = ExternalTypedData::ElementSizeInBytes(cid_);
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
@@ -4376,7 +4258,7 @@
   StackTraceDeserializationCluster() : DeserializationCluster("StackTrace") {}
   ~StackTraceDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4387,8 +4269,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       StackTracePtr trace = static_cast<StackTracePtr>(d->Ref(id));
       Deserializer::InitializeHeader(trace, kStackTraceCid,
@@ -4442,7 +4323,7 @@
   RegExpDeserializationCluster() : DeserializationCluster("RegExp") {}
   ~RegExpDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4452,8 +4333,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       RegExpPtr regexp = static_cast<RegExpPtr>(d->Ref(id));
       Deserializer::InitializeHeader(regexp, kRegExpCid,
@@ -4508,7 +4388,7 @@
       : DeserializationCluster("WeakProperty") {}
   ~WeakPropertyDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4519,8 +4399,7 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
-    ASSERT(!stamp_canonical);  // Never canonical.
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       WeakPropertyPtr property = static_cast<WeakPropertyPtr>(d->Ref(id));
       Deserializer::InitializeHeader(property, kWeakPropertyCid,
@@ -4599,14 +4478,13 @@
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
-class LinkedHashMapDeserializationCluster
-    : public AbstractInstanceDeserializationCluster {
+class LinkedHashMapDeserializationCluster : public DeserializationCluster {
  public:
   LinkedHashMapDeserializationCluster()
-      : AbstractInstanceDeserializationCluster("LinkedHashMap") {}
+      : DeserializationCluster("LinkedHashMap") {}
   ~LinkedHashMapDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4617,14 +4495,13 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     PageSpace* old_space = d->heap()->old_space();
 
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       LinkedHashMapPtr map = static_cast<LinkedHashMapPtr>(d->Ref(id));
-      Deserializer::InitializeHeader(map, kLinkedHashMapCid,
-                                     LinkedHashMap::InstanceSize(),
-                                     stamp_canonical);
+      Deserializer::InitializeHeader(
+          map, kLinkedHashMapCid, LinkedHashMap::InstanceSize(), is_canonical);
 
       map->untag()->type_arguments_ =
           static_cast<TypeArgumentsPtr>(d->ReadRef());
@@ -4708,14 +4585,13 @@
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
-class ArrayDeserializationCluster
-    : public AbstractInstanceDeserializationCluster {
+class ArrayDeserializationCluster : public DeserializationCluster {
  public:
   explicit ArrayDeserializationCluster(intptr_t cid)
-      : AbstractInstanceDeserializationCluster("Array"), cid_(cid) {}
+      : DeserializationCluster("Array"), cid_(cid) {}
   ~ArrayDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4727,12 +4603,12 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       ArrayPtr array = static_cast<ArrayPtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
       Deserializer::InitializeHeader(array, cid_, Array::InstanceSize(length),
-                                     stamp_canonical);
+                                     is_canonical);
       array->untag()->type_arguments_ =
           static_cast<TypeArgumentsPtr>(d->ReadRef());
       array->untag()->length_ = Smi::New(length);
@@ -4787,40 +4663,13 @@
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
-class StringDeserializationCluster : public DeserializationCluster {
- protected:
-  explicit StringDeserializationCluster(const char* name)
-      : DeserializationCluster(name) {}
-
- public:
-  void PostLoad(Deserializer* d, const Array& refs, bool canonicalize) {
-    if (canonicalize) {
-      CanonicalStringSet table(
-          d->zone(), d->isolate_group()->object_store()->symbol_table());
-      String& str = String::Handle(d->zone());
-      String& str2 = String::Handle(d->zone());
-      for (intptr_t i = start_index_; i < stop_index_; i++) {
-        str ^= refs.At(i);
-        str2 ^= table.InsertOrGet(str);
-        if (str.ptr() == str2.ptr()) {
-          str.SetCanonical();
-        } else {
-          refs.SetAt(i, str2);
-        }
-      }
-      d->isolate_group()->object_store()->set_symbol_table(table.Release());
-    }
-  }
-};
-
-class OneByteStringDeserializationCluster
-    : public StringDeserializationCluster {
+class OneByteStringDeserializationCluster : public DeserializationCluster {
  public:
   OneByteStringDeserializationCluster()
-      : StringDeserializationCluster("OneByteString") {}
+      : DeserializationCluster("OneByteString") {}
   ~OneByteStringDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4832,13 +4681,13 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       OneByteStringPtr str = static_cast<OneByteStringPtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
       Deserializer::InitializeHeader(str, kOneByteStringCid,
                                      OneByteString::InstanceSize(length),
-                                     stamp_canonical);
+                                     is_canonical);
       str->untag()->length_ = Smi::New(length);
       StringHasher hasher;
       for (intptr_t j = 0; j < length; j++) {
@@ -4849,6 +4698,21 @@
       String::SetCachedHash(str, hasher.Finalize());
     }
   }
+
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
+      CanonicalStringSet table(
+          d->zone(), d->isolate_group()->object_store()->symbol_table());
+      String& str = String::Handle(d->zone());
+      for (intptr_t i = start_index_; i < stop_index_; i++) {
+        str ^= refs.At(i);
+        ASSERT(str.IsCanonical());
+        bool present = table.Insert(str);
+        ASSERT(!present);
+      }
+      d->isolate_group()->object_store()->set_symbol_table(table.Release());
+    }
+  }
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -4893,14 +4757,13 @@
 };
 #endif  // !DART_PRECOMPILED_RUNTIME
 
-class TwoByteStringDeserializationCluster
-    : public StringDeserializationCluster {
+class TwoByteStringDeserializationCluster : public DeserializationCluster {
  public:
   TwoByteStringDeserializationCluster()
-      : StringDeserializationCluster("TwoByteString") {}
+      : DeserializationCluster("TwoByteString") {}
   ~TwoByteStringDeserializationCluster() {}
 
-  void ReadAlloc(Deserializer* d, bool stamp_canonical) {
+  void ReadAlloc(Deserializer* d, bool is_canonical) {
     start_index_ = d->next_index();
     PageSpace* old_space = d->heap()->old_space();
     const intptr_t count = d->ReadUnsigned();
@@ -4912,13 +4775,13 @@
     stop_index_ = d->next_index();
   }
 
-  void ReadFill(Deserializer* d, bool stamp_canonical) {
+  void ReadFill(Deserializer* d, bool is_canonical) {
     for (intptr_t id = start_index_; id < stop_index_; id++) {
       TwoByteStringPtr str = static_cast<TwoByteStringPtr>(d->Ref(id));
       const intptr_t length = d->ReadUnsigned();
       Deserializer::InitializeHeader(str, kTwoByteStringCid,
                                      TwoByteString::InstanceSize(length),
-                                     stamp_canonical);
+                                     is_canonical);
       str->untag()->length_ = Smi::New(length);
       StringHasher hasher;
       for (intptr_t j = 0; j < length; j++) {
@@ -4930,6 +4793,21 @@
       String::SetCachedHash(str, hasher.Finalize());
     }
   }
+
+  void PostLoad(Deserializer* d, const Array& refs, bool is_canonical) {
+    if (is_canonical && (d->isolate() != Dart::vm_isolate())) {
+      CanonicalStringSet table(
+          d->zone(), d->isolate_group()->object_store()->symbol_table());
+      String& str = String::Handle(d->zone());
+      for (intptr_t i = start_index_; i < stop_index_; i++) {
+        str ^= refs.At(i);
+        ASSERT(str.IsCanonical());
+        bool present = table.Insert(str);
+        ASSERT(!present);
+      }
+      d->isolate_group()->object_store()->set_symbol_table(table.Release());
+    }
+  }
 };
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
@@ -5309,26 +5187,16 @@
       const Object* deferred_object = (*unit_->deferred_objects())[i];
       ASSERT(deferred_object->IsCode());
       CodePtr code = static_cast<CodePtr>(deferred_object->ptr());
-      if (FLAG_use_bare_instructions) {
-        if (FLAG_retain_function_objects) {
-          ObjectPoolPtr pool = code->untag()->object_pool_;
-          if (pool != ObjectPool::null()) {
-            const intptr_t length = pool->untag()->length_;
-            uint8_t* entry_bits = pool->untag()->entry_bits();
-            for (intptr_t i = 0; i < length; i++) {
-              auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
-              if (entry_type == ObjectPool::EntryType::kTaggedObject) {
-                s->Push(pool->untag()->data()[i].raw_obj_);
-              }
-            }
-          }
-        }
-      } else {
-        s->Push(code->untag()->object_pool_);
-      }
       s->Push(code->untag()->compressed_stackmaps_);
       s->Push(code->untag()->code_source_map_);
     }
+    {
+      GrowableArray<CodePtr> raw_codes(num_deferred_objects);
+      for (intptr_t i = 0; i < num_deferred_objects; i++) {
+        raw_codes.Add((*unit_->deferred_objects())[i]->ptr());
+      }
+      s->PrepareInstructions(&raw_codes);
+    }
   }
 
   void WriteRoots(Serializer* s) {
@@ -5348,33 +5216,9 @@
       ASSERT(s->RefId(code) == (start_index + i));
       s->WriteInstructions(code->untag()->instructions_,
                            code->untag()->unchecked_offset_, code, false);
-      if (!FLAG_use_bare_instructions) {
-        s->WriteRootRef(code->untag()->object_pool_, "deferred-code");
-      }
       s->WriteRootRef(code->untag()->compressed_stackmaps_, "deferred-code");
       s->WriteRootRef(code->untag()->code_source_map_, "deferred-code");
     }
-
-    if (FLAG_use_bare_instructions && FLAG_retain_function_objects) {
-      ObjectPoolPtr pool =
-          s->isolate_group()->object_store()->global_object_pool();
-      const intptr_t length = pool->untag()->length_;
-      uint8_t* entry_bits = pool->untag()->entry_bits();
-      intptr_t last_write = 0;
-      for (intptr_t i = 0; i < length; i++) {
-        auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
-        if (entry_type == ObjectPool::EntryType::kTaggedObject) {
-          if (s->IsWritten(pool->untag()->data()[i].raw_obj_)) {
-            intptr_t skip = i - last_write;
-            s->WriteUnsigned(skip);
-            s->WriteRootRef(pool->untag()->data()[i].raw_obj_,
-                            "deferred-literal");
-            last_write = i;
-          }
-        }
-      }
-      s->WriteUnsigned(length - last_write);
-    }
 #endif
   }
 
@@ -5410,29 +5254,12 @@
         ASSERT(unchecked_entry_point != 0);
         func->untag()->unchecked_entry_point_ = unchecked_entry_point;
       }
-      if (!FLAG_use_bare_instructions) {
-        code->untag()->object_pool_ = static_cast<ObjectPoolPtr>(d->ReadRef());
-      }
       code->untag()->compressed_stackmaps_ =
           static_cast<CompressedStackMapsPtr>(d->ReadRef());
       code->untag()->code_source_map_ =
           static_cast<CodeSourceMapPtr>(d->ReadRef());
     }
 
-    if (FLAG_use_bare_instructions && FLAG_retain_function_objects) {
-      ObjectPoolPtr pool =
-          d->isolate_group()->object_store()->global_object_pool();
-      const intptr_t length = pool->untag()->length_;
-      uint8_t* entry_bits = pool->untag()->entry_bits();
-      for (intptr_t i = d->ReadUnsigned(); i < length; i += d->ReadUnsigned()) {
-        auto entry_type = ObjectPool::TypeBits::decode(entry_bits[i]);
-        ASSERT(entry_type == ObjectPool::EntryType::kTaggedObject);
-        // The existing entry will usually be null, but it might also be an
-        // equivalent object that was duplicated in another loading unit.
-        pool->untag()->data()[i].raw_obj_ = d->ReadRef();
-      }
-    }
-
     // Reinitialize the dispatch table by rereading the table's serialization
     // in the root snapshot.
     IsolateGroup* group = d->thread()->isolate()->group();
@@ -5845,9 +5672,6 @@
     // TODO(41974): Are these always type testing stubs?
     unit_id = LoadingUnit::kRootId;
   }
-  if (unit_id == LoadingUnit::kRootId) {
-    return true;
-  }
   if (unit_id != current_loading_unit_id_) {
     if (record) {
       (*loading_units_)[unit_id]->AddDeferredObject(static_cast<CodePtr>(obj));
@@ -5858,58 +5682,11 @@
 }
 
 #if !defined(DART_PRECOMPILED_RUNTIME)
-void Serializer::PrepareInstructions() {
-  if (!Snapshot::IncludesCode(kind())) return;
-
-  CodeSerializationCluster* cluster =
-      static_cast<CodeSerializationCluster*>(clusters_by_cid_[kCodeCid]);
-
-  // Code objects that have identical/duplicate instructions must be adjacent in
-  // the order that Code objects are written because the encoding of the
-  // reference from the Code to the Instructions assumes monotonically
-  // increasing offsets as part of a delta encoding. Also the code order table
-  // that allows for mapping return addresses back to Code objects depends on
-  // this sorting.
-  if (cluster != nullptr) {
-    CodeSerializationCluster::Sort(cluster->objects());
-  }
-  if ((loading_units_ != nullptr) &&
-      (current_loading_unit_id_ == LoadingUnit::kRootId)) {
-    for (intptr_t i = LoadingUnit::kRootId + 1; i < loading_units_->length();
-         i++) {
-      auto unit_objects = loading_units_->At(i)->deferred_objects();
-      CodeSerializationCluster::Sort(unit_objects);
-      for (intptr_t j = 0; j < unit_objects->length(); j++) {
-        cluster->deferred_objects()->Add(unit_objects->At(j)->ptr());
-      }
-    }
-  }
-
+void Serializer::PrepareInstructions(GrowableArray<CodePtr>* code_objects) {
 #if defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
   if ((kind() == Snapshot::kFullAOT) && FLAG_use_bare_instructions) {
-    // Group the code objects whose instructions are not being deferred in this
-    // snapshot unit in the order they will be written: first the code objects
-    // encountered for this first time in this unit being written by the
-    // CodeSerializationCluster, then code object previously deferred whose
-    // instructions are now written by UnitSerializationRoots. This order needs
-    // to be known to finalize bare-instructions-mode's PC-relative calls.
-    GrowableArray<CodePtr> code_objects;
-    if (cluster != nullptr) {
-      auto in = cluster->objects();
-      for (intptr_t i = 0; i < in->length(); i++) {
-        code_objects.Add(in->At(i));
-      }
-    }
-    if (loading_units_ != nullptr) {
-      auto in =
-          loading_units_->At(current_loading_unit_id_)->deferred_objects();
-      for (intptr_t i = 0; i < in->length(); i++) {
-        code_objects.Add(in->At(i)->ptr());
-      }
-    }
-
     GrowableArray<ImageWriterCommand> writer_commands;
-    RelocateCodeObjects(vm_, &code_objects, &writer_commands);
+    RelocateCodeObjects(vm_, code_objects, &writer_commands);
     image_writer_->PrepareForSerialization(&writer_commands);
   }
 #endif  // defined(DART_PRECOMPILER) && !defined(TARGET_ARCH_IA32)
@@ -6117,22 +5894,6 @@
   }
 
   GrowableArray<SerializationCluster*> canonical_clusters;
-  // The order that PostLoad runs matters for some classes. Explicitly place
-  // these clusters first, then add the rest ordered by class id.
-#define ADD_NEXT(cid)                                                          \
-  if (canonical_clusters_by_cid_[cid] != nullptr) {                            \
-    canonical_clusters.Add(canonical_clusters_by_cid_[cid]);                   \
-    canonical_clusters_by_cid_[cid] = nullptr;                                 \
-  }
-  ADD_NEXT(kOneByteStringCid)
-  ADD_NEXT(kTwoByteStringCid)
-  ADD_NEXT(kMintCid)
-  ADD_NEXT(kDoubleCid)
-  ADD_NEXT(kTypeParameterCid)
-  ADD_NEXT(kTypeCid)
-  ADD_NEXT(kTypeArgumentsCid)
-  ADD_NEXT(kClosureCid)
-#undef ADD_NEXT
   for (intptr_t cid = 0; cid < num_cids_; cid++) {
     if (canonical_clusters_by_cid_[cid] != nullptr) {
       canonical_clusters.Add(canonical_clusters_by_cid_[cid]);
@@ -6159,8 +5920,6 @@
   }
 #endif
 
-  PrepareInstructions();
-
   intptr_t num_objects = num_base_objects_ + num_written_objects_;
 #if defined(ARCH_IS_64_BIT)
   if (!Utils::IsInt(32, num_objects)) {
@@ -6278,7 +6037,8 @@
   ASSERT(code_cluster != nullptr);
   // Reference IDs in a cluster are allocated sequentially, so we can use the
   // first code object's reference ID to calculate the cluster index.
-  const intptr_t first_code_id = RefId(code_cluster->objects()->At(0));
+  const intptr_t first_code_id =
+      RefId(code_cluster->discovered_objects()->At(0));
   // The first object in the code cluster must have its reference ID allocated.
   ASSERT(IsAllocatedReference(first_code_id));
 
@@ -6968,17 +6728,18 @@
     ASSERT_EQUAL(next_ref_index_ - kFirstReference, num_objects_);
 
     {
-      TIMELINE_DURATION(thread(), Isolate, "PostLoad");
+      TIMELINE_DURATION(thread(), Isolate, "ReadFill");
       for (intptr_t i = 0; i < num_canonical_clusters_; i++) {
-        bool stamp_canonical = isolate() == Dart::vm_isolate();
-        canonical_clusters_[i]->ReadFill(this, stamp_canonical);
+        TIMELINE_DURATION(thread(), Isolate, canonical_clusters_[i]->name());
+        canonical_clusters_[i]->ReadFill(this, /*is_canonical*/ true);
 #if defined(DEBUG)
         int32_t section_marker = Read<int32_t>();
         ASSERT(section_marker == kSectionMarker);
 #endif
       }
       for (intptr_t i = 0; i < num_clusters_; i++) {
-        clusters_[i]->ReadFill(this, /*stamp_canonical*/ false);
+        TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name());
+        clusters_[i]->ReadFill(this, /*is_canonical*/ false);
 #if defined(DEBUG)
         int32_t section_marker = Read<int32_t>();
         ASSERT(section_marker == kSectionMarker);
@@ -6999,22 +6760,26 @@
   roots->PostLoad(this, refs);
 
 #if defined(DEBUG)
-  isolate()->ValidateClassTable();
-  if (isolate() != Dart::vm_isolate()) {
-    isolate_group()->heap()->Verify();
+  Isolate* isolate = thread()->isolate();
+  isolate->ValidateClassTable();
+  if (isolate != Dart::vm_isolate()) {
+    isolate->group()->heap()->Verify();
   }
 #endif
 
+  // TODO(rmacnak): When splitting literals, load clusters requiring
+  // canonicalization first, canonicalize and update the ref array, the load
+  // the remaining clusters to avoid a full heap walk to update references to
+  // the losers of any canonicalization races.
   {
     TIMELINE_DURATION(thread(), Isolate, "PostLoad");
     for (intptr_t i = 0; i < num_canonical_clusters_; i++) {
       TIMELINE_DURATION(thread(), Isolate, canonical_clusters_[i]->name());
-      bool canonicalize = isolate() != Dart::vm_isolate();
-      canonical_clusters_[i]->PostLoad(this, refs, canonicalize);
+      canonical_clusters_[i]->PostLoad(this, refs, /*is_canonical*/ true);
     }
     for (intptr_t i = 0; i < num_clusters_; i++) {
       TIMELINE_DURATION(thread(), Isolate, clusters_[i]->name());
-      clusters_[i]->PostLoad(this, refs, /*canonicalize*/ false);
+      clusters_[i]->PostLoad(this, refs, /*is_canonical*/ false);
     }
   }
 }
diff --git a/runtime/vm/clustered_snapshot.h b/runtime/vm/clustered_snapshot.h
index d35b83a..ac72aed 100644
--- a/runtime/vm/clustered_snapshot.h
+++ b/runtime/vm/clustered_snapshot.h
@@ -116,20 +116,16 @@
 
   // Allocate memory for all objects in the cluster and write their addresses
   // into the ref array. Do not touch this memory.
-  virtual void ReadAlloc(Deserializer* deserializer, bool stamp_canonical) = 0;
+  virtual void ReadAlloc(Deserializer* deserializer, bool is_canonical) = 0;
 
   // Initialize the cluster's objects. Do not touch the memory of other objects.
-  virtual void ReadFill(Deserializer* deserializer, bool stamp_canonical) = 0;
+  virtual void ReadFill(Deserializer* deserializer, bool is_canonical) = 0;
 
   // Complete any action that requires the full graph to be deserialized, such
   // as rehashing.
   virtual void PostLoad(Deserializer* deserializer,
                         const Array& refs,
-                        bool canonicalize) {
-    if (canonicalize) {
-      FATAL1("%s needs canonicalization but doesn't define PostLoad", name());
-    }
-  }
+                        bool is_canonical) {}
 
   const char* name() const { return name_; }
 
@@ -351,7 +347,7 @@
     Write<int32_t>(cid);
   }
 
-  void PrepareInstructions();
+  void PrepareInstructions(GrowableArray<CodePtr>* codes);
   void WriteInstructions(InstructionsPtr instr,
                          uint32_t unchecked_offset,
                          CodePtr code,
@@ -384,7 +380,6 @@
   void set_loading_units(GrowableArray<LoadingUnitSerializationData*>* units) {
     loading_units_ = units;
   }
-  intptr_t current_loading_unit_id() { return current_loading_unit_id_; }
   void set_current_loading_unit_id(intptr_t id) {
     current_loading_unit_id_ = id;
   }
@@ -419,13 +414,6 @@
     FATAL("Missing ref");
   }
 
-  bool HasRef(ObjectPtr object) const {
-    return heap_->GetObjectId(object) != kUnreachableReference;
-  }
-  bool IsWritten(ObjectPtr object) const {
-    return heap_->GetObjectId(object) > num_base_objects_;
-  }
-
  private:
   const char* ReadOnlyObjectType(intptr_t cid);
 
@@ -585,8 +573,6 @@
 
   uword ReadWordWith32BitReads() { return stream_.ReadWordWith32BitReads(); }
 
-  intptr_t position() const { return stream_.Position(); }
-  void set_position(intptr_t p) { stream_.SetPosition(p); }
   const uint8_t* CurrentBufferAddress() const {
     return stream_.AddressOfCurrentPosition();
   }
diff --git a/runtime/vm/compiler/relocation.cc b/runtime/vm/compiler/relocation.cc
index ff5ea61..f19bbbd 100644
--- a/runtime/vm/compiler/relocation.cc
+++ b/runtime/vm/compiler/relocation.cc
@@ -71,15 +71,8 @@
   // We're guaranteed to have all calls resolved, since
   //   * backwards calls are resolved eagerly
   //   * forward calls are resolved once the target is written
-  if (!all_unresolved_calls_.IsEmpty()) {
-    for (auto call : all_unresolved_calls_) {
-      OS::PrintErr("Unresolved call to %s from %s\n",
-                   Object::Handle(call->callee).ToCString(),
-                   Object::Handle(call->caller).ToCString());
-    }
-  }
-  RELEASE_ASSERT(all_unresolved_calls_.IsEmpty());
-  RELEASE_ASSERT(unresolved_calls_by_destination_.IsEmpty());
+  ASSERT(all_unresolved_calls_.IsEmpty());
+  ASSERT(unresolved_calls_by_destination_.IsEmpty());
 
   // Any trampolines we created must be patched with the right offsets.
   auto it = trampolines_by_destination_.GetIterator();
diff --git a/runtime/vm/compiler/runtime_offsets_extracted.h b/runtime/vm/compiler/runtime_offsets_extracted.h
index 9694444..5bdddc8 100644
--- a/runtime/vm/compiler/runtime_offsets_extracted.h
+++ b/runtime/vm/compiler/runtime_offsets_extracted.h
@@ -86,7 +86,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 4;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    48;
+    52;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     88;
 static constexpr dart::compiler::target::word Class_super_type_offset = 44;
@@ -611,7 +611,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 8;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    96;
+    104;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     164;
 static constexpr dart::compiler::target::word Class_super_type_offset = 88;
@@ -1140,7 +1140,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 4;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    48;
+    52;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     88;
 static constexpr dart::compiler::target::word Class_super_type_offset = 44;
@@ -1662,7 +1662,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 8;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    96;
+    104;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     164;
 static constexpr dart::compiler::target::word Class_super_type_offset = 88;
@@ -2194,7 +2194,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 4;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    48;
+    52;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     88;
 static constexpr dart::compiler::target::word Class_super_type_offset = 44;
@@ -2713,7 +2713,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 8;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    96;
+    104;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     164;
 static constexpr dart::compiler::target::word Class_super_type_offset = 88;
@@ -3236,7 +3236,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 4;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    48;
+    52;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     88;
 static constexpr dart::compiler::target::word Class_super_type_offset = 44;
@@ -3752,7 +3752,7 @@
 static constexpr dart::compiler::target::word Array_tags_offset = 0;
 static constexpr dart::compiler::target::word Array_type_arguments_offset = 8;
 static constexpr dart::compiler::target::word Class_declaration_type_offset =
-    96;
+    104;
 static constexpr dart::compiler::target::word Class_num_type_arguments_offset =
     164;
 static constexpr dart::compiler::target::word Class_super_type_offset = 88;
@@ -4280,7 +4280,7 @@
 static constexpr dart::compiler::target::word AOT_Array_type_arguments_offset =
     4;
 static constexpr dart::compiler::target::word
-    AOT_Class_declaration_type_offset = 48;
+    AOT_Class_declaration_type_offset = 52;
 static constexpr dart::compiler::target::word
     AOT_Class_num_type_arguments_offset = 88;
 static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 44;
@@ -4864,7 +4864,7 @@
 static constexpr dart::compiler::target::word AOT_Array_type_arguments_offset =
     8;
 static constexpr dart::compiler::target::word
-    AOT_Class_declaration_type_offset = 96;
+    AOT_Class_declaration_type_offset = 104;
 static constexpr dart::compiler::target::word
     AOT_Class_num_type_arguments_offset = 164;
 static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 88;
@@ -5454,7 +5454,7 @@
 static constexpr dart::compiler::target::word AOT_Array_type_arguments_offset =
     8;
 static constexpr dart::compiler::target::word
-    AOT_Class_declaration_type_offset = 96;
+    AOT_Class_declaration_type_offset = 104;
 static constexpr dart::compiler::target::word
     AOT_Class_num_type_arguments_offset = 164;
 static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 88;
@@ -6043,7 +6043,7 @@
 static constexpr dart::compiler::target::word AOT_Array_type_arguments_offset =
     4;
 static constexpr dart::compiler::target::word
-    AOT_Class_declaration_type_offset = 48;
+    AOT_Class_declaration_type_offset = 52;
 static constexpr dart::compiler::target::word
     AOT_Class_num_type_arguments_offset = 88;
 static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 44;
@@ -6620,7 +6620,7 @@
 static constexpr dart::compiler::target::word AOT_Array_type_arguments_offset =
     8;
 static constexpr dart::compiler::target::word
-    AOT_Class_declaration_type_offset = 96;
+    AOT_Class_declaration_type_offset = 104;
 static constexpr dart::compiler::target::word
     AOT_Class_num_type_arguments_offset = 164;
 static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 88;
@@ -7203,7 +7203,7 @@
 static constexpr dart::compiler::target::word AOT_Array_type_arguments_offset =
     8;
 static constexpr dart::compiler::target::word
-    AOT_Class_declaration_type_offset = 96;
+    AOT_Class_declaration_type_offset = 104;
 static constexpr dart::compiler::target::word
     AOT_Class_num_type_arguments_offset = 164;
 static constexpr dart::compiler::target::word AOT_Class_super_type_offset = 88;
diff --git a/runtime/vm/compiler/type_testing_stubs_arm.cc b/runtime/vm/compiler/type_testing_stubs_arm.cc
index ccd9d36..ed1ace2 100644
--- a/runtime/vm/compiler/type_testing_stubs_arm.cc
+++ b/runtime/vm/compiler/type_testing_stubs_arm.cc
@@ -20,8 +20,16 @@
     const Type& type,
     const Class& type_class) {
   BuildOptimizedTypeTestStubFastCases(assembler, hi, type, type_class);
-  __ Branch(compiler::Address(
-      THR, compiler::target::Thread::slow_type_test_entry_point_offset()));
+  if (!compiler::IsSameObject(
+          compiler::NullObject(),
+          compiler::CastHandle<Object>(slow_type_test_stub))) {
+    __ GenerateUnRelocatedPcRelativeTailCall();
+    unresolved_calls->Add(new compiler::UnresolvedPcRelativeCall(
+        __ CodeSize(), slow_type_test_stub, /*is_tail_call=*/true));
+  } else {
+    __ Branch(compiler::Address(
+        THR, compiler::target::Thread::slow_type_test_entry_point_offset()));
+  }
 }
 
 }  // namespace dart
diff --git a/runtime/vm/compiler/type_testing_stubs_arm64.cc b/runtime/vm/compiler/type_testing_stubs_arm64.cc
index 850b198..0540b01 100644
--- a/runtime/vm/compiler/type_testing_stubs_arm64.cc
+++ b/runtime/vm/compiler/type_testing_stubs_arm64.cc
@@ -20,11 +20,19 @@
     const Type& type,
     const Class& type_class) {
   BuildOptimizedTypeTestStubFastCases(assembler, hi, type, type_class);
-  __ ldr(
-      TMP,
-      compiler::Address(
-          THR, compiler::target::Thread::slow_type_test_entry_point_offset()));
-  __ br(TMP);
+  if (!compiler::IsSameObject(
+          compiler::NullObject(),
+          compiler::CastHandle<Object>(slow_type_test_stub))) {
+    __ GenerateUnRelocatedPcRelativeTailCall();
+    unresolved_calls->Add(new compiler::UnresolvedPcRelativeCall(
+        __ CodeSize(), slow_type_test_stub, /*is_tail_call=*/true));
+  } else {
+    __ ldr(TMP,
+           compiler::Address(
+               THR,
+               compiler::target::Thread::slow_type_test_entry_point_offset()));
+    __ br(TMP);
+  }
 }
 
 }  // namespace dart
diff --git a/runtime/vm/compiler/type_testing_stubs_x64.cc b/runtime/vm/compiler/type_testing_stubs_x64.cc
index 7722cee..f7a974c 100644
--- a/runtime/vm/compiler/type_testing_stubs_x64.cc
+++ b/runtime/vm/compiler/type_testing_stubs_x64.cc
@@ -20,8 +20,16 @@
     const Type& type,
     const Class& type_class) {
   BuildOptimizedTypeTestStubFastCases(assembler, hi, type, type_class);
-  __ jmp(compiler::Address(
-      THR, compiler::target::Thread::slow_type_test_entry_point_offset()));
+  if (!compiler::IsSameObject(
+          compiler::NullObject(),
+          compiler::CastHandle<Object>(slow_type_test_stub))) {
+    __ GenerateUnRelocatedPcRelativeTailCall();
+    unresolved_calls->Add(new compiler::UnresolvedPcRelativeCall(
+        __ CodeSize(), slow_type_test_stub, /*is_tail_call=*/true));
+  } else {
+    __ jmp(compiler::Address(
+        THR, compiler::target::Thread::slow_type_test_entry_point_offset()));
+  }
 }
 
 }  // namespace dart
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index fae18df..d98aa2d 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -20212,11 +20212,9 @@
       }
     }
     ASSERT(this->Equals(type));
-    // TODO(rmacnak): Revisit immediately returning type after to changes to
-    // recanonicalization on load for literal splitting.
-    if (type.IsCanonical()) {
-      return type.ptr();
-    }
+    ASSERT(type.IsCanonical());
+    ASSERT(type.IsOld());
+    return type.ptr();
   }
 
   Type& type = Type::Handle(zone);
diff --git a/runtime/vm/program_visitor.cc b/runtime/vm/program_visitor.cc
index b8e3de1..9082979 100644
--- a/runtime/vm/program_visitor.cc
+++ b/runtime/vm/program_visitor.cc
@@ -1371,15 +1371,9 @@
     MergeAssignment(obj_, id);
     obj_ = code.compressed_stackmaps();
     MergeAssignment(obj_, id);
-    if (!FLAG_use_bare_instructions) {
-      obj_ = code.object_pool();
-      MergeAssignment(obj_, id);
-    }
   }
 
   void MergeAssignment(const Object& obj, intptr_t id) {
-    if (obj.IsNull()) return;
-
     intptr_t old_id = heap_->GetLoadingUnit(obj_.ptr());
     if (old_id == WeakTable::kNoValue) {
       heap_->SetLoadingUnit(obj_.ptr(), id);
diff --git a/runtime/vm/raw_object.h b/runtime/vm/raw_object.h
index 8a02e59..d209ad2 100644
--- a/runtime/vm/raw_object.h
+++ b/runtime/vm/raw_object.h
@@ -28,8 +28,8 @@
 //  * Target architecture
 //  * DART_PRECOMPILED_RUNTIME (i.e, AOT vs. JIT)
 //
-// That is, fields in UntaggedObject and its subclasses should only be included or
-// excluded conditionally based on these factors. Otherwise, the generated
+// That is, fields in UntaggedObject and its subclasses should only be included
+// or excluded conditionally based on these factors. Otherwise, the generated
 // offsets can be wrong (which should be caught by offset checking in dart.cc).
 //
 // TODO(dartbug.com/43646): Add DART_PRECOMPILER as another axis.
@@ -819,6 +819,8 @@
   POINTER_FIELD(LibraryPtr, library)
   POINTER_FIELD(TypeArgumentsPtr, type_parameters)  // Array of TypeParameter.
   POINTER_FIELD(AbstractTypePtr, super_type)
+  POINTER_FIELD(ArrayPtr,
+                constants)  // Canonicalized const instances of this class.
   POINTER_FIELD(TypePtr, declaration_type)  // Declaration type for this class.
   POINTER_FIELD(ArrayPtr,
                 invocation_dispatcher_cache)  // Cache for dispatcher functions.
@@ -828,9 +830,7 @@
                 direct_implementors)                        // Array of Class.
   POINTER_FIELD(GrowableObjectArrayPtr, direct_subclasses)  // Array of Class.
   POINTER_FIELD(ArrayPtr, dependent_code)  // CHA optimized codes.
-  POINTER_FIELD(ArrayPtr,
-                constants)  // Canonicalized const instances of this class.
-  VISIT_TO(ObjectPtr, constants)
+  VISIT_TO(ObjectPtr, dependent_code)
   ObjectPtr* to_snapshot(Snapshot::Kind kind) {
     switch (kind) {
       case Snapshot::kFullAOT:
@@ -1642,8 +1642,6 @@
 
   friend class Object;
   friend class CodeSerializationCluster;
-  friend class UnitSerializationRoots;
-  friend class UnitDeserializationRoots;
 };
 
 class UntaggedInstructions : public UntaggedObject {
diff --git a/tools/VERSION b/tools/VERSION
index 41eeca7..0f13953 100644
--- a/tools/VERSION
+++ b/tools/VERSION
@@ -27,5 +27,5 @@
 MAJOR 2
 MINOR 12
 PATCH 0
-PRERELEASE 273
+PRERELEASE 274
 PRERELEASE_PATCH 0
\ No newline at end of file