Skip to content

Commit 0b6df4d

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Utilize true/false singleton property in identical() implementation
We maintain two singleton boxed bool instances for representing `true` and `false`. So the code for `identical()` no longer has to treat them as value classes where it has to unbox and compare the WasmI32. Filed [0] for CFE to improve precision of `ConstantExpression.getStaticType()`. [0] #60368 Change-Id: I8c02191e974aae9e168b4a826db9a87362949516 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/416941 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Ömer Ağacan <[email protected]>
1 parent 57cd03d commit 0b6df4d

File tree

3 files changed

+91
-35
lines changed

3 files changed

+91
-35
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ abstract class AstCodeGenerator
120120
}
121121

122122
DartType dartTypeOf(Expression exp) {
123+
if (exp is ConstantExpression) {
124+
// For constant expressions `getStaticType` returns often `DynamicType`
125+
// instead of a more precise type. See http://dartbug.com/60368
126+
return exp.constant.getType(typeContext);
127+
}
123128
return exp.getStaticType(typeContext);
124129
}
125130

pkg/dart2wasm/lib/intrinsics.dart

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'package:kernel/ast.dart';
66
import 'package:kernel/core_types.dart';
7+
import 'package:kernel/type_environment.dart';
78
import 'package:wasm_builder/wasm_builder.dart' as w;
89

910
import 'abi.dart' show kWasmAbiEnumIndex;
@@ -1023,29 +1024,58 @@ class Intrinsifier {
10231024
return codeGen.voidMarker;
10241025

10251026
case StaticIntrinsic.identical:
1026-
Expression first = node.arguments.positional[0];
1027-
Expression second = node.arguments.positional[1];
1028-
DartType boolType = translator.coreTypes.boolNonNullableRawType;
1029-
InterfaceType intType = translator.coreTypes.intNonNullableRawType;
1030-
DartType doubleType = translator.coreTypes.doubleNonNullableRawType;
1031-
List<DartType> types = [dartTypeOf(first), dartTypeOf(second)];
1032-
if (types.every((t) => t == intType)) {
1027+
// We can use reference equality for `identical()` except if one of the
1028+
// arguments can be a value type which would need to be compared by
1029+
// value instead of by reference.
1030+
//
1031+
// NOTE: Even though `bool` is a value type, it has only two singleton
1032+
// instances for `true` and `false` and we can therefore use reference
1033+
// equality for it. Other value types may have different objects
1034+
// containing the same value and need to be unboxed & compared by value.
1035+
//
1036+
// NOTE: We may get more value types in the future in Dart, for example
1037+
// for SIMD types, see: https://github.com/dart-lang/sdk/issues/43255
1038+
1039+
final first = node.arguments.positional[0];
1040+
final second = node.arguments.positional[1];
1041+
1042+
final firstType =
1043+
translator.toMostSpecificInterfaceType(dartTypeOf(first));
1044+
final secondType =
1045+
translator.toMostSpecificInterfaceType(dartTypeOf(second));
1046+
1047+
final intType = translator.boxedIntType;
1048+
if (firstType == intType && secondType == intType) {
10331049
codeGen.translateExpression(first, w.NumType.i64);
10341050
codeGen.translateExpression(second, w.NumType.i64);
10351051
b.i64_eq();
10361052
return w.NumType.i32;
10371053
}
1038-
if (types.any((t) =>
1039-
t is InterfaceType &&
1040-
t != boolType &&
1041-
t != doubleType &&
1042-
!translator.hierarchy
1043-
.isSubInterfaceOf(intType.classNode, t.classNode))) {
1044-
codeGen.translateExpression(first, w.RefType.eq(nullable: true));
1045-
codeGen.translateExpression(second, w.RefType.eq(nullable: true));
1054+
1055+
final doubleType = translator.boxedDoubleType;
1056+
if (firstType == doubleType && secondType == doubleType) {
1057+
codeGen.translateExpression(first, w.NumType.f64);
1058+
b.i64_reinterpret_f64();
1059+
codeGen.translateExpression(second, w.NumType.f64);
1060+
b.i64_reinterpret_f64();
1061+
b.i64_eq();
1062+
return w.NumType.i32;
1063+
}
1064+
1065+
bool canBeValueType(DartType type) =>
1066+
translator.typeEnvironment.isSubtypeOf(
1067+
doubleType, type, SubtypeCheckMode.withNullabilities) ||
1068+
translator.typeEnvironment
1069+
.isSubtypeOf(intType, type, SubtypeCheckMode.withNullabilities);
1070+
1071+
if (!canBeValueType(firstType) || !canBeValueType(secondType)) {
1072+
final nullableEqRefType = w.RefType.eq(nullable: true);
1073+
codeGen.translateExpression(first, nullableEqRefType);
1074+
codeGen.translateExpression(second, nullableEqRefType);
10461075
b.ref_eq();
10471076
return w.NumType.i32;
10481077
}
1078+
10491079
return null;
10501080
case StaticIntrinsic.isObjectClassId:
10511081
final classId = node.arguments.positional.single;
@@ -1615,7 +1645,6 @@ class Intrinsifier {
16151645
case MemberIntrinsic.identical:
16161646
w.Local first = paramLocals[0];
16171647
w.Local second = paramLocals[1];
1618-
ClassInfo boolInfo = translator.classInfo[translator.boxedBoolClass]!;
16191648
ClassInfo intInfo = translator.classInfo[translator.boxedIntClass]!;
16201649
ClassInfo doubleInfo =
16211650
translator.classInfo[translator.boxedDoubleClass]!;
@@ -1643,20 +1672,9 @@ class Intrinsifier {
16431672
b.i32_ne();
16441673
b.br_if(fail);
16451674

1646-
// Both bool?
1647-
b.local_get(cid);
1648-
b.i32_const((boolInfo.classId as AbsoluteClassId).value);
1649-
b.i32_eq();
1650-
b.if_();
1651-
b.local_get(first);
1652-
b.ref_cast(boolInfo.nonNullableType);
1653-
b.struct_get(boolInfo.struct, FieldIndex.boxValue);
1654-
b.local_get(second);
1655-
b.ref_cast(boolInfo.nonNullableType);
1656-
b.struct_get(boolInfo.struct, FieldIndex.boxValue);
1657-
b.i32_eq();
1658-
b.return_();
1659-
b.end();
1675+
// Cannot be equal `bool`s as we have unique singleton objects for `true`
1676+
// and `false`. If they were equal bools we would have triggered the
1677+
// reference equality above.
16601678

16611679
// Both int?
16621680
b.local_get(cid);

pkg/dart2wasm/lib/translator.dart

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,11 @@ class Translator with KernelNodes {
248248
late final w.RefType nullableObjectArrayTypeRef =
249249
w.RefType.def(nullableObjectArrayType, nullable: false);
250250

251+
late final boxedIntType =
252+
boxedIntClass.getThisType(coreTypes, Nullability.nonNullable);
253+
late final boxedDoubleType =
254+
boxedDoubleClass.getThisType(coreTypes, Nullability.nonNullable);
255+
251256
final Map<w.ModuleBuilder, PartialInstantiator> _partialInstantiators = {};
252257
PartialInstantiator getPartialInstantiatorForModule(w.ModuleBuilder module) {
253258
return _partialInstantiators[module] ??= PartialInstantiator(this, module);
@@ -605,11 +610,39 @@ class Translator with KernelNodes {
605610
}
606611

607612
Class classForType(DartType type) {
608-
return type is InterfaceType
609-
? type.classNode
610-
: type is TypeParameterType
611-
? classForType(type.bound)
612-
: coreTypes.objectClass;
613+
return toMostSpecificInterfaceType(type).classNode;
614+
}
615+
616+
InterfaceType toMostSpecificInterfaceType(DartType originalType) {
617+
var type = originalType;
618+
while (type is TypeParameterType) {
619+
type = type.bound;
620+
}
621+
while (type is StructuralParameterType) {
622+
type = type.bound;
623+
}
624+
final objectType = coreTypes.objectNonNullableRawType;
625+
final nullability = originalType.isPotentiallyNullable
626+
? Nullability.nullable
627+
: Nullability.nonNullable;
628+
return (switch (type) {
629+
InterfaceType() => type,
630+
FunctionType() => coreTypes.functionNonNullableRawType,
631+
RecordType() => coreTypes.recordNonNullableRawType,
632+
IntersectionType() => toMostSpecificInterfaceType(type.right),
633+
ExtensionType() => toMostSpecificInterfaceType(type.extensionTypeErasure),
634+
DynamicType() || VoidType() => objectType,
635+
NullType() => objectType,
636+
NeverType() => objectType,
637+
FutureOrType() => objectType,
638+
StructuralParameterType() ||
639+
TypeParameterType() =>
640+
throw 'unreachable, handled above',
641+
TypedefType() => throw 'unreachable, should be desugared by CFE',
642+
InvalidType() => throw 'unreachable, should be compile-time error',
643+
AuxiliaryType() => throw 'unreachable, unused by dart2wasm',
644+
})
645+
.withDeclaredNullability(nullability);
613646
}
614647

615648
void pushModuleId(w.InstructionsBuilder b) {

0 commit comments

Comments
 (0)