Skip to content

Commit 9b11bf4

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Take advantage of fast js-string builtins
Some wasm engines have started to optimize the `js-string` builtin proposal (e.g. V8) and those that haven't yet are probaly going to do soon. So we can start taking advantage of it in dart2wasm. => We make use of them in the JS<->Dart string copy code. => This will also provide a better baseline when evaluating whether switching to JS stringes entirely makes sense. A somewhat unrelated (but necessary for this CL) change is to tighten the types we use in `@pragma('wasm:import')` and `@pragma('wasm:export')` in some cases: We should only use 'pure' wasm types (i.e. not wasm struct / function types we define for dart classes & functions) and mostly non-composed types in import/exports as the `--closed-world` optimizations from binaryen rely on that (and error otherwise). Overall this leads to significant improvements in Dart<->JS string copies. The `WasmDataTransfer.*{From,To}BrowserString` benchmarks improve something between 50-100%. Change-Id: I2048113c462ecb2047402c0616d2b3b1f45773f5 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/400641 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Slava Egorov <[email protected]>
1 parent e8ebee8 commit 9b11bf4

File tree

8 files changed

+142
-328
lines changed

8 files changed

+142
-328
lines changed

pkg/dart2wasm/lib/js/inline_expander.dart

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class InlineExpander {
8888
String parameterString = 'x$j';
8989
DartType type = originalArgument.getStaticType(_staticTypeContext);
9090
dartPositionalParameters.add(VariableDeclaration(parameterString,
91-
type: type, isSynthesized: true));
91+
type: _toExternalType(type), isSynthesized: true));
9292
if (originalArgument is! VariableGet) {
9393
allArgumentsAreGet = false;
9494
}
@@ -121,4 +121,18 @@ class InlineExpander {
121121
_methodCollector.addMethod(dartProcedure, jsMethodName, codeTemplate);
122122
return result;
123123
}
124+
125+
// The `JS<foo>("...some js code ...", arg0, arg1)` expressions will produce
126+
// wasm imports. We want to only use types in those wasm imports that binaryen
127+
// allows under closed world assumptions (and not blindly use `arg<N>`s static
128+
// type).
129+
//
130+
// For now we special case `WasmArray<>` which we turn into a generic `array`
131+
// type (super type of all wasm arrays).
132+
DartType _toExternalType(DartType type) {
133+
if (type is InterfaceType && type.classNode == _util.wasmArrayClass) {
134+
return InterfaceType(_util.wasmArrayRefClass, type.declaredNullability);
135+
}
136+
return type;
137+
}
124138
}

pkg/dart2wasm/lib/js/runtime_blob.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ const jsRuntimeBlobPart3 = r'''
129129
"fromCharCode": (i) => String.fromCharCode(i),
130130
"length": (s) => s.length,
131131
"substring": (s, a, b) => s.substring(a, b),
132+
"fromCharCodeArray": (a, start, end) => {
133+
if (end <= start) return '';
134+
135+
const read = dartInstance.exports.$wasmI16ArrayGet;
136+
let result = '';
137+
let index = start;
138+
const chunkLength = Math.min(end - index, 500);
139+
let array = new Array(chunkLength);
140+
while (index < end) {
141+
const newChunkLength = Math.min(end - index, 500);
142+
for (let i = 0; i < newChunkLength; i++) {
143+
array[i] = read(a, index++);
144+
}
145+
if (newChunkLength < chunkLength) {
146+
array = array.slice(0, newChunkLength);
147+
}
148+
result += String.fromCharCode(...array);
149+
}
150+
return result;
151+
},
132152
};
133153
134154
const deferredLibraryHelper = {

pkg/dart2wasm/lib/js/util.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ class CoreTypesUtil {
2727
final Procedure jsValueUnboxTarget;
2828
final Procedure numToIntTarget;
2929
final Class wasmExternRefClass;
30+
final Class wasmArrayClass;
31+
final Class wasmArrayRefClass;
3032
final Procedure wrapDartFunctionTarget;
3133

3234
CoreTypesUtil(this.coreTypes, this._extensionIndex)
@@ -64,6 +66,9 @@ class CoreTypesUtil {
6466
.firstWhere((p) => p.name.text == 'unbox'),
6567
wasmExternRefClass =
6668
coreTypes.index.getClass('dart:_wasm', 'WasmExternRef'),
69+
wasmArrayClass = coreTypes.index.getClass('dart:_wasm', 'WasmArray'),
70+
wasmArrayRefClass =
71+
coreTypes.index.getClass('dart:_wasm', 'WasmArrayRef'),
6772
wrapDartFunctionTarget = coreTypes.index
6873
.getTopLevelProcedure('dart:_js_helper', '_wrapDartFunction');
6974

pkg/dart2wasm/lib/translator.dart

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ class Translator with KernelNodes {
690690
/// (`anyref`, `funcref` or `externref`).
691691
/// This function can be called before the class info is built.
692692
w.ValueType translateExternalType(DartType type) {
693-
bool isPotentiallyNullable = type.isPotentiallyNullable;
693+
final bool isPotentiallyNullable = type.isPotentiallyNullable;
694694
if (type is InterfaceType) {
695695
Class cls = type.classNode;
696696
if (cls == wasmFuncRefClass || cls == wasmFunctionClass) {
@@ -699,6 +699,16 @@ class Translator with KernelNodes {
699699
if (cls == wasmExternRefClass) {
700700
return w.RefType.extern(nullable: isPotentiallyNullable);
701701
}
702+
if (cls == wasmArrayRefClass) {
703+
return w.RefType.array(nullable: isPotentiallyNullable);
704+
}
705+
if (cls == wasmArrayClass) {
706+
final elementType =
707+
translateExternalStorageType(type.typeArguments.single);
708+
return w.RefType.def(
709+
wasmArrayType(elementType, '$elementType', mutable: true),
710+
nullable: isPotentiallyNullable);
711+
}
702712
if (!isPotentiallyNullable) {
703713
w.StorageType? builtin = builtinTypes[cls];
704714
if (builtin != null && builtin.isPrimitive) {
@@ -711,6 +721,22 @@ class Translator with KernelNodes {
711721
return w.RefType.any(nullable: true);
712722
}
713723

724+
w.StorageType translateExternalStorageType(DartType type) {
725+
if (type is InterfaceType) {
726+
final cls = type.classNode;
727+
if (isWasmType(cls)) {
728+
final isNullable = type.isPotentiallyNullable;
729+
final w.StorageType? builtin = builtinTypes[cls];
730+
if (builtin != null) {
731+
if (!isNullable) return builtin;
732+
if (builtin.isPrimitive) throw "Wasm numeric types can't be nullable";
733+
return (builtin as w.RefType).withNullability(isNullable);
734+
}
735+
}
736+
}
737+
return translateExternalType(type) as w.RefType;
738+
}
739+
714740
/// Creates a global reference to [f] in [module]. [f] must also be located
715741
/// in [module].
716742
w.Global makeFunctionRef(w.ModuleBuilder module, w.BaseFunction f) {

0 commit comments

Comments
 (0)