[vm] Use ClassId in Instance::CanonicalizeHash

We want to reuse CanonicalizeHash for const maps and sets. See design in
go/dart-vm-const-maps.

Before, we only used CanonicalizeHash on a per-class basis. However,
when we start using it for const maps and sets we don't want hash
collisions between const instances with identical layout but a different
class id. So we include the class id into the hash rather than the size.

Bug: https://github.com/dart-lang/sdk/issues/45908

TEST=vm/cc/CanonicalizeHash_Const_Instances
TEST=A ton of bots for existing tests.

Change-Id: Idc0eed49fbae8f76dcfdda21dd9fe2674dc4a240
Cq-Include-Trybots: luci.dart.try:analyzer-nnbd-linux-release-try,app-kernel-linux-debug-x64-try,dart-sdk-linux-try,front-end-nnbd-linux-release-x64-try,pkg-linux-debug-try,vm-canary-linux-debug-try,vm-kernel-asan-linux-release-x64-try,vm-kernel-checked-linux-release-x64-try,vm-kernel-linux-debug-x64c-try,vm-kernel-linux-debug-x64-try,vm-kernel-linux-debug-simarm64c-try,vm-kernel-nnbd-linux-release-simarm-try,vm-kernel-optcounter-threshold-linux-release-x64-try,vm-kernel-precomp-android-release-arm_x64-try,vm-kernel-precomp-linux-debug-x64-try,vm-kernel-precomp-linux-debug-x64c-try,vm-kernel-reload-linux-debug-x64-try,vm-kernel-reload-rollback-linux-debug-x64-try,vm-precomp-ffi-qemu-linux-release-arm-try,vm-kernel-precomp-linux-debug-simarm_x64-try,vm-kernel-precomp-linux-debug-x64-try
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/208085
Reviewed-by: Tess Strickland <sstrickl@google.com>
Commit-Queue: Daco Harkes <dacoharkes@google.com>
diff --git a/runtime/vm/object.cc b/runtime/vm/object.cc
index 1903854..c52f156 100644
--- a/runtime/vm/object.cc
+++ b/runtime/vm/object.cc
@@ -18961,9 +18961,9 @@
   if (Symbol::IsSymbolCid(GetClassId())) {
     hash = Symbol::CanonicalizeHash(*this);
   } else {
-    const intptr_t instance_size = SizeFromClass();
-    ASSERT(instance_size != 0);
-    hash = instance_size / kCompressedWordSize;
+    const intptr_t class_id = cls.id();
+    ASSERT(class_id != 0);
+    hash = class_id;
     uword this_addr = reinterpret_cast<uword>(this->untag());
     Object& obj = Object::Handle(zone);
     Instance& instance = Instance::Handle(zone);
diff --git a/runtime/vm/object_test.cc b/runtime/vm/object_test.cc
index dae798f..93cfc12 100644
--- a/runtime/vm/object_test.cc
+++ b/runtime/vm/object_test.cc
@@ -5029,6 +5029,64 @@
                                         /*check_identity=*/false));
 }
 
+// Because we want to reuse CanonicalizeHash for hashCode, we should not have
+// collisions.
+TEST_CASE(CanonicalizeHash_Const_Instances) {
+  const char* kScript =
+      "class A {\n"
+      "  final int n;\n"
+      "  \n"
+      "  const A(this.n);\n"
+      "}\n"
+      "\n"
+      "class B {\n"
+      "  final int n;\n"
+      "  \n"
+      "  const B(this.n);\n"
+      "}\n"
+      "\n"
+      "valueA() {\n"
+      "  return const A(5);\n"
+      "}\n"
+      "\n"
+      "valueB() {\n"
+      "  return const B(5);\n"
+      "}\n";
+
+  Dart_Handle lib = TestCase::LoadTestScript(kScript, nullptr);
+  EXPECT_VALID(lib);
+
+  Dart_Handle value_a_result =
+      Dart_Invoke(lib, NewString("valueA"), 0, nullptr);
+  EXPECT_VALID(value_a_result);
+  Dart_Handle value_b_result =
+      Dart_Invoke(lib, NewString("valueB"), 0, nullptr);
+  EXPECT_VALID(value_b_result);
+
+  TransitionNativeToVM transition(Thread::Current());
+
+  const auto& value_a_dart = Instance::CheckedHandle(
+      Thread::Current()->zone(), Api::UnwrapHandle(value_a_result));
+  const auto& value_b_dart = Instance::CheckedHandle(
+      Thread::Current()->zone(), Api::UnwrapHandle(value_b_result));
+
+  const uint32_t canonicalize_hash_a = value_a_dart.CanonicalizeHash();
+  const uint32_t canonicalize_hash_b = value_b_dart.CanonicalizeHash();
+
+  bool success = canonicalize_hash_a != canonicalize_hash_b;
+
+  if (!success) {
+    LogBlock lb;
+    THR_Print("Hash collision between %s and %s\n", value_a_dart.ToCString(),
+              value_b_dart.ToCString());
+    THR_Print("VM CanonicalizeHash a %" Px32 " %" Pd32 "\n",
+              canonicalize_hash_a, canonicalize_hash_a);
+    THR_Print("VM CanonicalizeHash b %" Px32 " %" Pd32 "\n",
+              canonicalize_hash_b, canonicalize_hash_b);
+  }
+  EXPECT(success);
+}
+
 TEST_CASE(LinkedHashMap_iteration) {
   const char* kScript =
       "makeMap() {\n"