Skip to content

Commit e63dcaf

Browse files
mkustermannCommit Queue
authored andcommitted
[dart2wasm] Add wasm module printing functionality.
This adds support for printing module IR as text format. For convenience we add a `pkg/dart2wasm/bin/wami.dart` that produces very similar output to V8's `wami`. The goal is to use this to write size/perf optimization tests by dumping IR into expectation files (will add this infrastructure in a future CL) Issue #60928 Change-Id: I42d19c2b8c6242f55693d6ed5d844a5c1ecb1f39 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/454600 Commit-Queue: Martin Kustermann <[email protected]> Reviewed-by: Ömer Ağacan <[email protected]>
1 parent d70aa28 commit e63dcaf

File tree

12 files changed

+2390
-2
lines changed

12 files changed

+2390
-2
lines changed

pkg/dart2wasm/bin/wasm2wat.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
7+
import 'package:wasm_builder/wasm_builder.dart';
8+
9+
void main(List<String> args) {
10+
final input = args[0];
11+
final wasmBytes = File(input).readAsBytesSync();
12+
13+
final deserializer = Deserializer(wasmBytes);
14+
final module = Module.deserialize(deserializer);
15+
print(module.printAsWat());
16+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io';
6+
import 'dart:typed_data';
7+
8+
import 'package:path/path.dart' as path;
9+
import 'package:wasm_builder/wasm_builder.dart';
10+
11+
import 'self_compile_test.dart' show withTempDir, run;
12+
13+
Future main() async {
14+
if (!Platform.isLinux && !Platform.isMacOS) return;
15+
16+
await withTempDir((String tempDir) async {
17+
final dartFilename = 'third_party/flute/benchmarks/lib/complex.dart';
18+
19+
final wasmFilename = path.join(tempDir, 'flute.wasm');
20+
final optWasmFilename = path.join(tempDir, 'flute.opt.wasm');
21+
final wasmFile = File(wasmFilename);
22+
final optWasmFile = File(wasmFilename);
23+
24+
// Ensure we can print unoptimized dart2wasm modules
25+
await run([
26+
Platform.executable,
27+
'compile',
28+
'wasm',
29+
'-O0',
30+
dartFilename,
31+
'-o',
32+
wasmFilename,
33+
]);
34+
wasmPrint(wasmFile.readAsBytesSync());
35+
36+
// Ensure we can print wasm-opt optimized wasm modules
37+
await run([
38+
Platform.executable,
39+
'compile',
40+
'wasm',
41+
'-O3',
42+
dartFilename,
43+
'-o',
44+
optWasmFilename,
45+
]);
46+
wasmPrint(optWasmFile.readAsBytesSync());
47+
48+
// Temporary files will be deleted when returning to [withTempDir].
49+
});
50+
}
51+
52+
void wasmPrint(Uint8List wasmBytes) {
53+
final deserializer = Deserializer(wasmBytes);
54+
final module = Module.deserialize(deserializer);
55+
print('len = ${module.printAsWat().length}');
56+
}

pkg/wasm_builder/lib/src/ir/function.dart

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import '../serialize/serialize.dart';
6+
import '../serialize/printer.dart';
67
import 'ir.dart';
78

89
/// A local variable defined in a function.
@@ -12,6 +13,15 @@ class Local {
1213

1314
Local(this.index, this.type);
1415

16+
void printTo(IrPrinter p, Map<int, String> localNames,
17+
{bool isParam = false}) {
18+
p.write(isParam ? 'param' : 'local');
19+
p.write(' ');
20+
p.writeLocalIndexReference(index);
21+
p.write(' ');
22+
p.writeValueType(type);
23+
}
24+
1525
@override
1626
String toString() => "$index";
1727
}
@@ -82,6 +92,39 @@ class DefinedFunction extends BaseFunction implements Serializable {
8292
s.writeData(localS);
8393
}
8494

95+
void printTo(IrPrinter p) {
96+
p.write('(func ');
97+
p.writeFunctionReference(this);
98+
String? exportName;
99+
for (final f in enclosingModule.exports.exported) {
100+
if (f is FunctionExport && f.function == this) {
101+
exportName = f.name;
102+
break;
103+
}
104+
}
105+
if (exportName != null) {
106+
p.write(' ');
107+
p.writeExport(exportName);
108+
}
109+
110+
p.withLocalNames(localNames, () {
111+
if (type.inputs.isNotEmpty || type.outputs.isNotEmpty) {
112+
p.write(' ');
113+
type.printSignatureWithNamesTo(p, oneLine: true);
114+
}
115+
p.writeln('');
116+
p.withIndent(() {
117+
for (int i = type.inputs.length; i < locals.length; ++i) {
118+
p.write('(');
119+
locals[i].printTo(p, localNames);
120+
p.writeln(')');
121+
}
122+
body.printTo(p);
123+
});
124+
});
125+
p.write(')');
126+
}
127+
85128
@override
86129
String toString() => functionName ?? "#$finalizableIndex";
87130
}
@@ -105,6 +148,15 @@ class ImportedFunction extends BaseFunction implements Import {
105148
s.writeUnsigned(type.index);
106149
}
107150

151+
void printTo(IrPrinter p) {
152+
p.write('(func ');
153+
p.writeFunctionReference(this);
154+
p.writeImport(module, name);
155+
p.write(' ');
156+
type.printOneLineSignatureTo(p);
157+
p.write(')');
158+
}
159+
108160
@override
109161
String toString() => "$module.$name";
110162
}

pkg/wasm_builder/lib/src/ir/global.dart

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// BSD-style license that can be found in the LICENSE file.
44

55
import '../serialize/serialize.dart';
6+
import '../serialize/printer.dart';
67
import 'ir.dart';
78

89
/// An (imported or defined) global variable.
@@ -26,6 +27,8 @@ abstract class Global with Indexable, Exportable {
2627
Export buildExport(String name) {
2728
return GlobalExport(name, this);
2829
}
30+
31+
void printTo(IrPrinter p) => throw 'not implemented';
2932
}
3033

3134
/// A global variable defined in a module.
@@ -41,6 +44,21 @@ class DefinedGlobal extends Global implements Serializable {
4144
s.write(type);
4245
s.write(initializer);
4346
}
47+
48+
@override
49+
void printTo(IrPrinter p) {
50+
// This may generate globals this one refers to.
51+
final ip = p.dup();
52+
initializer.printInitializerTo(ip);
53+
54+
p.write('(global ');
55+
p.writeGlobalReference(this);
56+
p.write(' ');
57+
type.printTo(p);
58+
p.write(' ');
59+
p.write(ip.getText().trim());
60+
p.write(')');
61+
}
4462
}
4563

4664
/// An imported global variable.
@@ -62,6 +80,17 @@ class ImportedGlobal extends Global implements Import {
6280
s.writeByte(0x03);
6381
s.write(type);
6482
}
83+
84+
@override
85+
void printTo(IrPrinter p) {
86+
p.write('(global ');
87+
p.writeGlobalReference(this);
88+
p.write(' ');
89+
p.writeImport(module, name);
90+
p.write(' ');
91+
p.writeValueType(type.type);
92+
p.write(')');
93+
}
6594
}
6695

6796
class GlobalExport extends Export {

0 commit comments

Comments
 (0)