Skip to content

Commit 302885c

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Smaller as T type checks due to less inlining
Currently we are quite agressive when inlining `as T` type checks. This is due to a number of `@pragma('wasm:prefer-inline')` annotations combined with `@pragma('wasm:static-dispatch')` annotations causing us to generate polymorphic dispatcher functions for calls to `_Type._checkInstance`, combined with the polymorphic call target force inlining targets with <= 2 specializations. These combination of factors lead to a `x as T` to become something like <... code for checking x & T's nullability ...> classId = x.classId; if classId = ClassId.getClassId(_InterfaceClass) ... else ... This lead to binaryen sometimes infer the `x.classId` value to be a constant which prunes the branches which then calls the faster path for interface type checks. Though this is quite a lot of code size. So instead of inlining all these things, but still taking advantage of the binaryen global optimizations that may infer `x.classId` we load the class id (which binaryen may sometimes turn into a constant) and then pass it to the polymorphic dispatcher (which we no longer inline to safe code size). This way if the class id is a constant, either binaryen or V8 will see that it can inline the polymorphic dispatcher as most of its body disappears if the class id is known. Since we no longer inline the polyhmorphic dispatcher, we can now also mark other common types via `@pragma('wasm:static-dispatch')` - such as `_RecordType._checkInstance`. This in return will speed up any code that uses records in collections (e.g. in maps / sets / lists) as the covariance checks now involve loading class id and branching on it to a devirtualized `_RecordType._checkInstance` instead of an indirect call that also involves a function type check). We also remove the `@pragma('wasm:prefer-inline')` on the `_checkSubclassRelationshipViaTable` function: The idea was that if binaryen infers the load of class id most of the code that follows can be optimized away at compile time. Unfortunately the tables can get large, which made us not use `ImmutableWasmArray` but instead normal `WasmArray`. That in return makes binaryen unable to optimize loads from it (at constant index) away, as the contents of the array may change (they never do, but binaryen doesn't know that). So there's little benefit in inlining it. Change-Id: I416fbdd35c6425a626378f2e9ea2009e50bf600b Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/420320 Reviewed-by: Ömer Ağacan <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent 727c0e5 commit 302885c

File tree

3 files changed

+19
-9
lines changed

3 files changed

+19
-9
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1976,13 +1976,19 @@ abstract class AstCodeGenerator
19761976
}
19771977
}
19781978

1979+
final callPolymorphicDispatcher = staticDispatchRanges.isNotEmpty;
1980+
19791981
// Receiver is already on stack.
19801982
w.Local receiverVar = addLocal(signature.inputs.first);
19811983
assert(!receiverVar.type.nullable);
19821984
b.local_tee(receiverVar);
1985+
if (callPolymorphicDispatcher) {
1986+
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
1987+
b.local_get(receiverVar);
1988+
}
19831989
pushArguments(signature, selector.paramInfo);
19841990

1985-
if (staticDispatchRanges.isNotEmpty) {
1991+
if (callPolymorphicDispatcher) {
19861992
b.invoke(translator
19871993
.getPolymorphicDispatchersForModule(b.module)
19881994
.getPolymorphicDispatcher(selector,

pkg/dart2wasm/lib/translator.dart

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2373,7 +2373,7 @@ class PolymorphicDispatcherCallTarget extends CallTarget {
23732373
.targets(unchecked: useUncheckedEntry)
23742374
.staticDispatchRanges
23752375
.length <=
2376-
2;
2376+
1;
23772377

23782378
@override
23792379
CodeGenerator get inliningCodeGen => PolymorphicDispatcherCodeGenerator(
@@ -2382,8 +2382,8 @@ class PolymorphicDispatcherCallTarget extends CallTarget {
23822382
@override
23832383
late final w.BaseFunction function = (() {
23842384
final function = callingModule.functions.define(
2385-
translator.typesBuilder
2386-
.defineFunction(signature.inputs, signature.outputs),
2385+
translator.typesBuilder.defineFunction(
2386+
[w.NumType.i32, ...signature.inputs], signature.outputs),
23872387
name);
23882388
translator.compilationQueue.add(CompilationTask(function, inliningCodeGen));
23892389
return function;
@@ -2413,24 +2413,26 @@ class PolymorphicDispatcherCodeGenerator implements CodeGenerator {
24132413
final bool needFallback =
24142414
targets.targetRanges.length > targets.staticDispatchRanges.length;
24152415

2416+
// First parameter to the dispatcher is the class id.
2417+
const int classIdParameterOffset = 1;
2418+
24162419
void emitDirectCall(Reference target) {
24172420
for (int i = 0; i < signature.inputs.length; ++i) {
2418-
b.local_get(paramLocals[i]);
2421+
b.local_get(paramLocals[classIdParameterOffset + i]);
24192422
}
24202423
translator.callReference(target, b);
24212424
}
24222425

24232426
void emitDispatchTableCall() {
24242427
for (int i = 0; i < signature.inputs.length; ++i) {
2425-
b.local_get(paramLocals[i]);
2428+
b.local_get(paramLocals[classIdParameterOffset + i]);
24262429
}
2427-
b.local_get(paramLocals[0]);
2430+
b.local_get(paramLocals[1]);
24282431
translator.callDispatchTable(b, selector,
24292432
useUncheckedEntry: useUncheckedEntry);
24302433
}
24312434

24322435
b.local_get(paramLocals[0]);
2433-
b.struct_get(translator.topInfo.struct, FieldIndex.classId);
24342436
b.classIdSearch(targetRanges, signature.outputs, emitDirectCall,
24352437
needFallback ? emitDispatchTableCall : null);
24362438

sdk/lib/_internal/wasm/lib/type.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ class _BottomType extends _Type {
107107
_Type get _asNullable => _literal<Null>();
108108

109109
@override
110+
@pragma("wasm:static-dispatch")
110111
bool _checkInstance(Object o) => false;
111112

112113
@override
@@ -450,6 +451,7 @@ class _FunctionType extends _Type {
450451
);
451452

452453
@override
454+
@pragma("wasm:static-dispatch")
453455
bool _checkInstance(Object o) {
454456
if (ClassID.getID(o) != ClassID.cid_Closure) return false;
455457
return _TypeUniverse.isFunctionSubtype(
@@ -581,6 +583,7 @@ class _RecordType extends _Type {
581583

582584
@override
583585
@pragma('dyn-module:callable')
586+
@pragma("wasm:static-dispatch")
584587
bool _checkInstance(Object o) {
585588
if (!_isRecordClassId(ClassID.getID(o))) return false;
586589
return unsafeCast<Record>(o)._checkRecordType(fieldTypes, names);
@@ -1210,7 +1213,6 @@ abstract class _TypeUniverse {
12101213
return _checkSubclassRelationshipViaTable(sId, tId);
12111214
}
12121215

1213-
@pragma('wasm:prefer-inline')
12141216
static WasmI32 _checkSubclassRelationshipViaTable(WasmI32 sId, WasmI32 tId) {
12151217
final sModuleId = classIdToModuleId(sId);
12161218
final tModuleId = classIdToModuleId(tId);

0 commit comments

Comments
 (0)