From d8b53d97ffedfa33775e401ea0eb69d9cdded3eb Mon Sep 17 00:00:00 2001 From: Aske Simon Christensen Date: Thu, 13 Apr 2023 16:45:14 +0000 Subject: [PATCH] [dart2wasm] Implement identityHashCode via br_table. This saves the space for the `_identityHashCode` method in the dispatch table and allows customizing `identityHashCode` for classes not in `dart:core`. Change-Id: I829fb0f53dea1f3baf92e12079b9772ce2ff8c2a Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/292820 Commit-Queue: Aske Simon Christensen Reviewed-by: Joshua Litt Auto-Submit: Aske Simon Christensen --- pkg/dart2wasm/lib/class_info.dart | 5 ++ pkg/dart2wasm/lib/intrinsics.dart | 51 +++++++++++++++++++ pkg/dart2wasm/lib/kernel_nodes.dart | 2 + pkg/dart2wasm/lib/translator.dart | 11 ++++ sdk/lib/_internal/wasm/lib/double.dart | 1 - .../_internal/wasm/lib/identical_patch.dart | 3 +- sdk/lib/_internal/wasm/lib/int.dart | 1 - sdk/lib/_internal/wasm/lib/object_patch.dart | 1 - sdk/lib/_internal/wasm/lib/string_patch.dart | 2 - 9 files changed, 70 insertions(+), 7 deletions(-) diff --git a/pkg/dart2wasm/lib/class_info.dart b/pkg/dart2wasm/lib/class_info.dart index 349b18f63f2..01df8110108 100644 --- a/pkg/dart2wasm/lib/class_info.dart +++ b/pkg/dart2wasm/lib/class_info.dart @@ -452,6 +452,11 @@ class ClassInfoCollector { // parameters. _initialize(translator.typeClass); + // Initialize value classes to make sure they have low class IDs. + for (Class cls in translator.valueClasses.keys) { + _initialize(cls); + } + // Initialize masquerade classes to make sure they have low class IDs. for (Class cls in _masquerades.values) { _initialize(cls); diff --git a/pkg/dart2wasm/lib/intrinsics.dart b/pkg/dart2wasm/lib/intrinsics.dart index 8da4873fb70..bdb7c948767 100644 --- a/pkg/dart2wasm/lib/intrinsics.dart +++ b/pkg/dart2wasm/lib/intrinsics.dart @@ -1305,6 +1305,57 @@ class Intrinsifier { return true; } + if (member.enclosingLibrary == translator.coreTypes.coreLibrary && + name == "identityHashCode") { + final w.Local arg = paramLocals[0]; + final w.Local nonNullArg = + function.addLocal(translator.topInfo.nonNullableType); + final List classIds = translator.valueClasses.keys + .map((cls) => translator.classInfo[cls]!.classId) + .toList() + ..sort(); + + // If the argument is `null`, return the hash code of `null`. + final w.Label notNull = + b.block(const [], [translator.topInfo.nonNullableType]); + b.local_get(arg); + b.br_on_non_null(notNull); + b.i64_const(null.hashCode); + b.return_(); + b.end(); // notNull + b.local_set(nonNullArg); + + // Branch on class ID. + final w.Label defaultLabel = b.block(); + final List labels = + List.generate(classIds.length, (_) => b.block()); + b.local_get(nonNullArg); + b.struct_get(translator.topInfo.struct, FieldIndex.classId); + int labelIndex = 0; + final List targets = List.generate(classIds.last + 1, (id) { + return id == classIds[labelIndex] ? labels[labelIndex++] : defaultLabel; + }); + b.br_table(targets, defaultLabel); + + // For value classes, dispatch to their `hashCode` implementation. + for (final int id in classIds.reversed) { + final Class cls = translator.valueClasses[translator.classes[id].cls!]!; + final Procedure hashCodeProcedure = + cls.procedures.firstWhere((p) => p.name.text == "hashCode"); + b.end(); // Jump target for class ID + b.local_get(nonNullArg); + codeGen.call(hashCodeProcedure.reference); + b.return_(); + } + + // For all other classes, dispatch to the `hashCode` implementation in + // `Object`. + b.end(); // defaultLabel + b.local_get(nonNullArg); + codeGen.call(translator.objectHashCode.reference); + return true; + } + // _typeArguments if (member.name.text == "_typeArguments") { Class cls = member.enclosingClass!; diff --git a/pkg/dart2wasm/lib/kernel_nodes.dart b/pkg/dart2wasm/lib/kernel_nodes.dart index 31c633ac617..d31bc35fb30 100644 --- a/pkg/dart2wasm/lib/kernel_nodes.dart +++ b/pkg/dart2wasm/lib/kernel_nodes.dart @@ -160,6 +160,8 @@ mixin KernelNodes { "dart:collection", "_HashAbstractImmutableBase", "get:_indexNullable"); // dart:core various procedures + late final Procedure objectHashCode = + index.getProcedure("dart:core", "Object", "get:hashCode"); late final Procedure objectNoSuchMethod = index.getProcedure("dart:core", "Object", "noSuchMethod"); late final Procedure objectGetTypeArguments = diff --git a/pkg/dart2wasm/lib/translator.dart b/pkg/dart2wasm/lib/translator.dart index a4dd59b74e7..15d452e9124 100644 --- a/pkg/dart2wasm/lib/translator.dart +++ b/pkg/dart2wasm/lib/translator.dart @@ -151,6 +151,17 @@ class Translator with KernelNodes { w.NumType.f64: boxedDoubleClass, }; + /// Classes whose identity hash code is their hash code rather than the + /// identity hash code field in the struct. Each implementation class maps to + /// the class containing the implementation of its `hashCode` getter. + late final Map valueClasses = { + boxedIntClass: boxedIntClass, + boxedDoubleClass: boxedDoubleClass, + boxedBoolClass: coreTypes.boolClass, + oneByteStringClass: stringBaseClass, + twoByteStringClass: stringBaseClass, + }; + /// Type for vtable entries for dynamic calls. These entries are used in /// dynamic invocations and `Function.apply`. late final w.FunctionType dynamicCallVtableEntryFunctionType = diff --git a/sdk/lib/_internal/wasm/lib/double.dart b/sdk/lib/_internal/wasm/lib/double.dart index c0d0255f4f3..4e47275f04a 100644 --- a/sdk/lib/_internal/wasm/lib/double.dart +++ b/sdk/lib/_internal/wasm/lib/double.dart @@ -68,7 +68,6 @@ final class _BoxedDouble extends double { static const int _mantissaMask = 0x000FFFFFFFFFFFFF; int get hashCode => _doubleHashCode(this); - int get _identityHashCode => _doubleHashCode(this); static int _doubleHashCode(double value) { const int maxInt = 0x7FFFFFFFFFFFFFFF; diff --git a/sdk/lib/_internal/wasm/lib/identical_patch.dart b/sdk/lib/_internal/wasm/lib/identical_patch.dart index 2aeb4a6e7eb..2a97f699e4e 100644 --- a/sdk/lib/_internal/wasm/lib/identical_patch.dart +++ b/sdk/lib/_internal/wasm/lib/identical_patch.dart @@ -8,5 +8,4 @@ part of "core_patch.dart"; external bool identical(Object? a, Object? b); @patch -int identityHashCode(Object? object) => - object == null ? Null._HASH_CODE : object._identityHashCode; +external int identityHashCode(Object? object); diff --git a/sdk/lib/_internal/wasm/lib/int.dart b/sdk/lib/_internal/wasm/lib/int.dart index c5543fb2a54..8de3f321419 100644 --- a/sdk/lib/_internal/wasm/lib/int.dart +++ b/sdk/lib/_internal/wasm/lib/int.dart @@ -517,7 +517,6 @@ final class _BoxedInt extends int { } int get hashCode => _intHashCode(this); - int get _identityHashCode => _intHashCode(this); static int _intHashCode(int value) { const int magic = 0x2D51; diff --git a/sdk/lib/_internal/wasm/lib/object_patch.dart b/sdk/lib/_internal/wasm/lib/object_patch.dart index e971efb5132..d7c8be1f2d7 100644 --- a/sdk/lib/_internal/wasm/lib/object_patch.dart +++ b/sdk/lib/_internal/wasm/lib/object_patch.dart @@ -32,7 +32,6 @@ class Object { @patch int get hashCode => _objectHashCode(this); - int get _identityHashCode => _objectHashCode(this); /// Concrete subclasses of [Object] will have overrides of [_typeArguments] /// which return their type arguments. diff --git a/sdk/lib/_internal/wasm/lib/string_patch.dart b/sdk/lib/_internal/wasm/lib/string_patch.dart index 4fc78b0c42b..a05875efe9e 100644 --- a/sdk/lib/_internal/wasm/lib/string_patch.dart +++ b/sdk/lib/_internal/wasm/lib/string_patch.dart @@ -105,8 +105,6 @@ abstract final class _StringBase implements String { int _computeHashCode(); - int get _identityHashCode => hashCode; - bool get _isOneByte { // Alternatively return false and override it on one-byte string classes. return this is _OneByteString;