Skip to content

Commit 74aa17d

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Emit unreachable instructions after calls that never return
This allows wasm optimizers as well as wasm runtimes to optimize code better as they know calls to slow paths that throw will never return. Change-Id: Iace1827062dbe00ce24c737b6369bf588e748ee9 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/404582 Reviewed-by: Ömer Ağacan <[email protected]> Commit-Queue: Martin Kustermann <[email protected]>
1 parent 41a8e81 commit 74aa17d

File tree

4 files changed

+50
-11
lines changed

4 files changed

+50
-11
lines changed

pkg/dart2wasm/lib/code_generator.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3941,7 +3941,6 @@ class StaticFieldImplicitAccessorCodeGenerator extends AstCodeGenerator {
39413941
} else {
39423942
if (flag != null) {
39433943
// Explicit initialization flag
3944-
assert(global.type.type == initFunction.type.outputs.single);
39453944
b.global_get(flag);
39463945
b.if_(const [], [global.type.type]);
39473946
b.global_get(global);
@@ -4291,6 +4290,24 @@ enum _VirtualCallKind {
42914290
}
42924291

42934292
extension MacroAssembler on w.InstructionsBuilder {
4293+
/// If the given [outputs] of a call contain bottom types then we will emit an
4294+
/// `unreachable` instruction.
4295+
///
4296+
/// This can help wasm compilers / wasm runtimes to optimize things more as
4297+
/// they know control flow has ended here.
4298+
List<w.ValueType> emitUnreachableIfNoResult(List<w.ValueType> outputs) {
4299+
for (int i = 0; i < outputs.length; ++i) {
4300+
final output = outputs[i];
4301+
if (output is w.RefType &&
4302+
output.heapType == w.HeapType.none &&
4303+
!output.nullable) {
4304+
unreachable();
4305+
break;
4306+
}
4307+
}
4308+
return outputs;
4309+
}
4310+
42944311
/// `[i32] -> [i32]`
42954312
///
42964313
/// Consumes a `i32` class ID, leaves an `i32` as `bool` for whether
@@ -4620,8 +4637,7 @@ extension MacroAssembler on w.InstructionsBuilder {
46204637
comment('Direct call to ${target.name}');
46214638
call(target.function);
46224639
}
4623-
4624-
return target.signature.outputs;
4640+
return emitUnreachableIfNoResult(target.signature.outputs);
46254641
}
46264642

46274643
/// Pushes fields common to all Dart objects (class id, id hash).

pkg/dart2wasm/lib/dispatch_table.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ class SelectorInfo {
152152
for (int i = 0; i < returnCount; i++) {
153153
if (i < returns.length) {
154154
DartType type = returns[i];
155-
outputSets[i].add(translator.translateType(type));
155+
outputSets[i].add(translator.translateReturnType(type));
156156
} else {
157157
outputSets[i].add(translator.topInfo.nullableType);
158158
}

pkg/dart2wasm/lib/functions.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,9 @@ w.FunctionType _makeFunctionType(
490490
w.ValueType translateType(DartType type) => isImportOrExport
491491
? translator.translateExternalType(type)
492492
: translator.translateType(type);
493+
w.ValueType translateReturnType(DartType type) => isImportOrExport
494+
? translator.translateExternalType(type)
495+
: translator.translateReturnType(type);
493496

494497
final List<w.ValueType> inputs = _getInputTypes(
495498
translator, target, receiverType, isImportOrExport, translateType);
@@ -508,7 +511,8 @@ w.FunctionType _makeFunctionType(
508511
outputs = const [];
509512
} else {
510513
final DartType returnType = translator.typeOfReturnValue(member);
511-
outputs = !isVoidType(returnType) ? [translateType(returnType)] : const [];
514+
outputs =
515+
!isVoidType(returnType) ? [translateReturnType(returnType)] : const [];
512516
}
513517

514518
return translator.typesBuilder.defineFunction(inputs, outputs);

pkg/dart2wasm/lib/translator.dart

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ class Translator with KernelNodes {
482482
b.ref_as_non_null();
483483
b.call_ref(function.type);
484484
}
485-
return function.type.outputs;
485+
return b.emitUnreachableIfNoResult(function.type.outputs);
486486
}
487487

488488
void callDispatchTable(w.InstructionsBuilder b, SelectorInfo selector) {
@@ -493,6 +493,7 @@ class Translator with KernelNodes {
493493
b.i32_add();
494494
}
495495
b.call_indirect(selector.signature, dispatchTable.getWasmTable(b.module));
496+
b.emitUnreachableIfNoResult(selector.signature.outputs);
496497
functions.recordSelectorUse(selector);
497498
}
498499

@@ -544,6 +545,13 @@ class Translator with KernelNodes {
544545
w.Tag getExceptionTag(w.ModuleBuilder module) =>
545546
exceptionTag.getExceptionTag(module);
546547

548+
w.ValueType translateReturnType(DartType type) {
549+
if (type is NeverType && !type.isPotentiallyNullable) {
550+
return const w.RefType.none(nullable: false);
551+
}
552+
return translateType(type);
553+
}
554+
547555
w.ValueType translateType(DartType type) {
548556
w.StorageType wasmType = translateStorageType(type);
549557
if (wasmType is w.ValueType) return wasmType;
@@ -640,7 +648,22 @@ class Translator with KernelNodes {
640648
if (type is DynamicType || type is VoidType) {
641649
return topInfo.nullableType;
642650
}
643-
if (type is NullType || type is NeverType) {
651+
if (type is NullType) {
652+
return const w.RefType.none(nullable: true);
653+
}
654+
if (type is NeverType) {
655+
// We should translate `Never` to a bottom type in wasm. Though right now
656+
// for examples like this
657+
// ```
658+
// Never a;
659+
// try {
660+
// a = throw 'a;
661+
// } catch (e, s) {}
662+
// ```
663+
// our code generator makes a local for `a` and tries to initialize it
664+
// with a default value (of which there are none if we make it real
665+
// bottom).
666+
// => We make it nullable here.
644667
return const w.RefType.none(nullable: true);
645668
}
646669
if (type is TypeParameterType) {
@@ -1194,10 +1217,6 @@ class Translator with KernelNodes {
11941217
return translateType(typeOfParameterVariable(node, isRequired));
11951218
}
11961219

1197-
w.ValueType translateTypeOfReturnValue(Member node) {
1198-
return translateType(typeOfReturnValue(node));
1199-
}
1200-
12011220
w.ValueType translateTypeOfField(Field node) {
12021221
return translateType(typeOfField(node));
12031222
}

0 commit comments

Comments
 (0)