Skip to content

Commit 7684ae5

Browse files
alexmarkovCommit Queue
authored andcommitted
[vm,aot] Use inferred type arguments to remove type checks
When TFA is able to infer type arguments, write _exact type_ into the inferred types metadata. Use inferred exact type in the VM: load it from metadata and attach to a field if necessary. CompileType is extended to hold exact type if it is known. In the instruction canonicalizer, if type arguments are loaded from a value which has a known exact type, then replace this LoadField with Constant type arguments from exact type. This allows VM to use inferred type arguments to remove parameter type checks. TEST=runtime/tests/vm/dart/eliminate_type_check_using_exact_type_il_test.dart Closes #61083 Change-Id: I766c5e8c49be406f3e12d38e8d966e5813500c4d Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/440064 Commit-Queue: Alexander Markov <[email protected]> Reviewed-by: Slava Egorov <[email protected]>
1 parent fc345a7 commit 7684ae5

File tree

85 files changed

+1720
-512
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+1720
-512
lines changed

pkg/vm/lib/metadata/inferred_type.dart

Lines changed: 48 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import 'package:kernel/src/printer.dart';
99

1010
/// Metadata for annotating nodes with an inferred type information.
1111
class InferredType {
12+
final InterfaceType? exactType;
1213
final Reference? _concreteClassReference;
1314
final Constant? _constantValue;
1415
final Reference? _closureMemberReference;
@@ -29,27 +30,21 @@ class InferredType {
2930
// Contains inferred closure value.
3031
static const int flagClosure = 1 << 5;
3132

32-
// Entire list may be null if no type arguments were inferred.
33-
// Will always be null if `concreteClass` is null.
34-
//
35-
// Each component may be null if that particular type argument was not
36-
// inferred.
37-
//
38-
// Otherwise, a non-null type argument indicates that that particular type
39-
// argument (in the runtime type) is always exactly a particular `DartType`.
40-
final List<DartType?>? exactTypeArguments;
33+
// Contains exact type.
34+
static const int flagExactType = 1 << 6;
4135

4236
InferredType(
37+
InterfaceType? exactType,
4338
Class? concreteClass,
4439
bool nullable,
4540
bool isInt,
4641
Constant? constantValue,
4742
Member? closureMember,
4843
int closureId, {
49-
List<DartType?>? exactTypeArguments,
5044
bool skipCheck = false,
5145
bool receiverNotInt = false,
5246
}) : this._byReference(
47+
exactType,
5348
concreteClass?.reference,
5449
constantValue,
5550
closureMember?.reference,
@@ -59,19 +54,25 @@ class InferredType {
5954
(skipCheck ? flagSkipCheck : 0) |
6055
(constantValue != null ? flagConstant : 0) |
6156
(receiverNotInt ? flagReceiverNotInt : 0) |
62-
(closureMember != null ? flagClosure : 0),
63-
exactTypeArguments,
57+
(closureMember != null ? flagClosure : 0) |
58+
(exactType != null ? flagExactType : 0),
6459
);
6560

6661
InferredType._byReference(
62+
this.exactType,
6763
this._concreteClassReference,
6864
this._constantValue,
6965
this._closureMemberReference,
7066
this._closureId,
7167
this._flags,
72-
this.exactTypeArguments,
7368
) {
74-
assert(exactTypeArguments == null || _concreteClassReference != null);
69+
assert(
70+
exactType == null || _concreteClassReference == exactType!.classReference,
71+
);
72+
assert(
73+
exactType == null ||
74+
(nullable == (exactType!.nullability == Nullability.nullable)),
75+
);
7576
assert(_constantValue == null || _concreteClassReference != null);
7677
assert(_closureMemberReference == null || _concreteClassReference != null);
7778
assert(_closureId >= 0);
@@ -94,27 +95,25 @@ class InferredType {
9495
@override
9596
String toString() {
9697
final StringBuffer buf = new StringBuffer();
97-
if (concreteClass != null) {
98-
buf.write(concreteClass!.toText(astTextStrategyForTesting));
98+
final exactType = this.exactType;
99+
final concreteClass = this.concreteClass;
100+
if (exactType != null) {
101+
buf.write(exactType.toText(astTextStrategyForTesting));
102+
} else if (concreteClass != null) {
103+
buf.write(concreteClass.toText(astTextStrategyForTesting));
104+
if (nullable) {
105+
buf.write('?');
106+
}
99107
} else if (isInt) {
100108
buf.write('int');
109+
if (nullable) {
110+
buf.write('?');
111+
}
112+
} else if (nullable) {
113+
buf.write('?');
101114
} else {
102115
buf.write('!');
103116
}
104-
if (nullable) {
105-
buf.write('?');
106-
}
107-
if (exactTypeArguments != null) {
108-
buf.write('<');
109-
buf.write(
110-
exactTypeArguments!
111-
.map(
112-
(t) => t != null ? "${t.toText(astTextStrategyForTesting)}" : "?",
113-
)
114-
.join(", "),
115-
);
116-
buf.write('>');
117-
}
118117
if (skipCheck) {
119118
buf.write(' (skip check)');
120119
}
@@ -147,13 +146,15 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
147146

148147
@override
149148
void writeToBinary(InferredType metadata, Node node, BinarySink sink) {
150-
// TODO(sjindel/tfa): Implement serialization of type arguments when can use
151-
// them for optimizations.
152-
sink.writeNullAllowedCanonicalNameReference(
153-
metadata.concreteClass?.reference,
154-
);
155149
final flags = metadata._flags;
156-
sink.writeByte(metadata._flags);
150+
sink.writeUInt30(metadata._flags);
151+
if ((flags & InferredType.flagExactType) != 0) {
152+
sink.writeDartType(metadata.exactType!);
153+
} else {
154+
sink.writeNullAllowedCanonicalNameReference(
155+
metadata.concreteClass?.reference,
156+
);
157+
}
157158
if ((flags & InferredType.flagConstant) != 0) {
158159
sink.writeConstantReference(metadata.constantValue!);
159160
}
@@ -167,11 +168,16 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
167168

168169
@override
169170
InferredType readFromBinary(Node node, BinarySource source) {
170-
// TODO(sjindel/tfa): Implement serialization of type arguments when can use
171-
// them for optimizations.
172-
final concreteClassReference =
173-
source.readNullableCanonicalNameReference()?.reference;
174-
final flags = source.readByte();
171+
final flags = source.readUInt30();
172+
InterfaceType? exactType;
173+
Reference? concreteClassReference;
174+
if ((flags & InferredType.flagExactType) != 0) {
175+
exactType = source.readDartType() as InterfaceType;
176+
concreteClassReference = exactType.classReference;
177+
} else {
178+
concreteClassReference =
179+
source.readNullableCanonicalNameReference()?.reference;
180+
}
175181
final constantValue =
176182
(flags & InferredType.flagConstant) != 0
177183
? source.readConstantReference()
@@ -183,12 +189,12 @@ class InferredTypeMetadataRepository extends MetadataRepository<InferredType> {
183189
final closureId =
184190
(flags & InferredType.flagClosure) != 0 ? source.readUInt30() : 0;
185191
return new InferredType._byReference(
192+
exactType,
186193
concreteClassReference,
187194
constantValue,
188195
closureMemberReference,
189196
closureId,
190197
flags,
191-
null,
192198
);
193199
}
194200
}

pkg/vm/lib/transformations/type_flow/transformer.dart

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -468,6 +468,7 @@ class AnnotateKernel extends RecursiveVisitor {
468468
bool skipCheck = false,
469469
bool receiverNotInt = false,
470470
}) {
471+
InterfaceType? exactType;
471472
Class? concreteClass;
472473
Constant? constantValue;
473474
Member? closureMember;
@@ -515,35 +516,42 @@ class AnnotateKernel extends RecursiveVisitor {
515516
}
516517
}
517518

518-
List<DartType?>? typeArgs;
519519
if (type is ConcreteType && type.typeArgs != null) {
520-
typeArgs =
521-
type.typeArgs!
522-
.take(type.numImmediateTypeArgs)
523-
.map(
524-
(t) =>
525-
t is UnknownType
526-
? null
527-
: (t as RuntimeType).representedType,
528-
)
529-
.toList();
530-
}
531-
532-
if (concreteClass != null ||
520+
final typeArgs = <DartType>[];
521+
bool allKnown = true;
522+
for (int i = 0, n = type.numImmediateTypeArgs; i < n; ++i) {
523+
final t = type.typeArgs![i];
524+
if (t is UnknownType) {
525+
allKnown = false;
526+
break;
527+
}
528+
typeArgs.add((t as RuntimeType).representedType);
529+
}
530+
if (allKnown) {
531+
exactType = InterfaceType(
532+
concreteClass!,
533+
nullable ? Nullability.nullable : Nullability.nonNullable,
534+
typeArgs,
535+
);
536+
}
537+
}
538+
539+
if (exactType != null ||
540+
concreteClass != null ||
533541
!nullable ||
534542
isInt ||
535543
constantValue != null ||
536544
skipCheck ||
537545
receiverNotInt ||
538546
closureMember != null) {
539547
return new InferredType(
548+
exactType,
540549
concreteClass,
541550
nullable,
542551
isInt,
543552
constantValue,
544553
closureMember,
545554
closureId,
546-
exactTypeArguments: typeArgs,
547555
skipCheck: skipCheck,
548556
receiverNotInt: receiverNotInt,
549557
);

pkg/vm/testcases/transformations/ffi/abi_specific_int.dart.aot.expect

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ static method testStoreLoad() → void {
103103
final ffi::Pointer<self::WChar> p = let final core::int #t1 = [@vm.inferred-type.metadata=dart.core::_Smi] self::WChar::#sizeOf in [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] #C33.{ffi::Allocator::allocate}<self::WChar>(){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WChar>};
104104
ffi::_storeAbiSpecificInt<self::WChar>(p, #C19, 10);
105105
core::print([@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificInt<self::WChar>(p, #C19));
106-
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
106+
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
107107
}
108108

109109
[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
@@ -113,17 +113,17 @@ static method testStoreLoadIndexed() → void {
113113
ffi::_storeAbiSpecificIntAtIndex<self::WChar>(p, #C19, 1, 3);
114114
core::print([@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, #C19, 0));
115115
core::print([@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificIntAtIndex<self::WChar>(p, #C19, 1));
116-
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
116+
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
117117
}
118118

119119
[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
120120
static method testStruct() → void {
121121
final ffi::Pointer<self::WCharStruct> p = let final core::int #t3 = [@vm.inferred-type.metadata=dart.core::_Smi] self::WCharStruct::#sizeOf in [@vm.direct-call.metadata=#lib::_DummyAllocator.allocate] [@vm.inferred-type.metadata=dart.ffi::Pointer (skip check)] #C33.{ffi::Allocator::allocate}<self::WCharStruct>(){(core::int, {alignment: core::int?}) → ffi::Pointer<self::WCharStruct>};
122-
[@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=!? (skip check)] new self::WCharStruct::#fromTypedDataBase(_in::unsafeCast<ffi::Pointer<self::WCharStruct>>(p)).{self::WCharStruct::a0} = 1;
122+
[@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=? (skip check)] new self::WCharStruct::#fromTypedDataBase(_in::unsafeCast<ffi::Pointer<self::WCharStruct>>(p)).{self::WCharStruct::a0} = 1;
123123
core::print([@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=int] new self::WCharStruct::#fromTypedDataBase(_in::unsafeCast<ffi::Pointer<self::WCharStruct>>(p)).{self::WCharStruct::a0}{core::int});
124-
[@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=!? (skip check)] new self::WCharStruct::#fromTypedDataBase(_in::unsafeCast<ffi::Pointer<self::WCharStruct>>(p)).{self::WCharStruct::a0} = 2;
124+
[@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=? (skip check)] new self::WCharStruct::#fromTypedDataBase(_in::unsafeCast<ffi::Pointer<self::WCharStruct>>(p)).{self::WCharStruct::a0} = 2;
125125
core::print([@vm.direct-call.metadata=#lib::WCharStruct.a0] [@vm.inferred-type.metadata=int] new self::WCharStruct::#fromTypedDataBase(_in::unsafeCast<ffi::Pointer<self::WCharStruct>>(p)).{self::WCharStruct::a0}{core::int});
126-
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
126+
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
127127
}
128128

129129
[@vm.inferred-return-type.metadata=dart.core::Null? (value: null)]
@@ -134,17 +134,17 @@ static method testInlineArray() → void {
134134
block {
135135
synthesized ffi::Array<dynamic> #array = _in::unsafeCast<ffi::Array<self::WChar>>(array);
136136
synthesized core::int #index = _in::unsafeCast<core::int>(i);
137-
[@vm.direct-call.metadata=dart.ffi::Array._checkIndex] [@vm.inferred-type.metadata=!? (skip check)] #array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
137+
[@vm.direct-call.metadata=dart.ffi::Array._checkIndex] [@vm.inferred-type.metadata=? (skip check)] #array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
138138
} =>ffi::_storeAbiSpecificIntAtIndex<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] #array.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] #array.{ffi::_Compound::_offsetInBytes}{core::int}, #index, i);
139139
}
140140
for (core::int i = 0; [@vm.direct-call.metadata=dart.core::_IntegerImplementation.<] [@vm.inferred-type.metadata=dart.core::bool (skip check)] i.{core::num::<}(100){(core::num) → core::bool}; i = [@vm.direct-call.metadata=dart.core::_IntegerImplementation.+] [@vm.inferred-type.metadata=int (skip check)] i.{core::num::+}(1){(core::num) → core::int}) {
141141
core::print( block {
142142
synthesized ffi::Array<dynamic> #array = _in::unsafeCast<ffi::Array<self::WChar>>(array);
143143
synthesized core::int #index = _in::unsafeCast<core::int>(i);
144-
[@vm.direct-call.metadata=dart.ffi::Array._checkIndex] [@vm.inferred-type.metadata=!? (skip check)] #array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
144+
[@vm.direct-call.metadata=dart.ffi::Array._checkIndex] [@vm.inferred-type.metadata=? (skip check)] #array.{ffi::Array::_checkIndex}(#index){(core::int) → void};
145145
} =>[@vm.inferred-type.metadata=int] ffi::_loadAbiSpecificIntAtIndex<self::WChar>([@vm.direct-call.metadata=dart.ffi::_Compound._typedDataBase] #array.{ffi::_Compound::_typedDataBase}{core::Object}, [@vm.direct-call.metadata=dart.ffi::_Compound._offsetInBytes] [@vm.inferred-type.metadata=int?] #array.{ffi::_Compound::_offsetInBytes}{core::int}, #index));
146146
}
147-
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=!? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
147+
[@vm.direct-call.metadata=#lib::_DummyAllocator.free] [@vm.inferred-type.metadata=? (skip check)] #C33.{self::_DummyAllocator::free}(){(ffi::Pointer<ffi::NativeType>) → void};
148148
}
149149
constants {
150150
#C1 = "vm:ffi:abi-specific-mapping"

0 commit comments

Comments
 (0)