|
4 | 4 |
|
5 | 5 | import 'package:kernel/ast.dart'; |
6 | 6 | import 'package:kernel/core_types.dart'; |
| 7 | +import 'package:kernel/type_environment.dart'; |
7 | 8 | import 'package:wasm_builder/wasm_builder.dart' as w; |
8 | 9 |
|
9 | 10 | import 'abi.dart' show kWasmAbiEnumIndex; |
@@ -1023,29 +1024,58 @@ class Intrinsifier { |
1023 | 1024 | return codeGen.voidMarker; |
1024 | 1025 |
|
1025 | 1026 | 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) { |
1033 | 1049 | codeGen.translateExpression(first, w.NumType.i64); |
1034 | 1050 | codeGen.translateExpression(second, w.NumType.i64); |
1035 | 1051 | b.i64_eq(); |
1036 | 1052 | return w.NumType.i32; |
1037 | 1053 | } |
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); |
1046 | 1075 | b.ref_eq(); |
1047 | 1076 | return w.NumType.i32; |
1048 | 1077 | } |
| 1078 | + |
1049 | 1079 | return null; |
1050 | 1080 | case StaticIntrinsic.isObjectClassId: |
1051 | 1081 | final classId = node.arguments.positional.single; |
@@ -1615,7 +1645,6 @@ class Intrinsifier { |
1615 | 1645 | case MemberIntrinsic.identical: |
1616 | 1646 | w.Local first = paramLocals[0]; |
1617 | 1647 | w.Local second = paramLocals[1]; |
1618 | | - ClassInfo boolInfo = translator.classInfo[translator.boxedBoolClass]!; |
1619 | 1648 | ClassInfo intInfo = translator.classInfo[translator.boxedIntClass]!; |
1620 | 1649 | ClassInfo doubleInfo = |
1621 | 1650 | translator.classInfo[translator.boxedDoubleClass]!; |
@@ -1643,20 +1672,9 @@ class Intrinsifier { |
1643 | 1672 | b.i32_ne(); |
1644 | 1673 | b.br_if(fail); |
1645 | 1674 |
|
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. |
1660 | 1678 |
|
1661 | 1679 | // Both int? |
1662 | 1680 | b.local_get(cid); |
|
0 commit comments