Skip to content

Commit f109f3c

Browse files
alexcrichtonkripken
authored andcommitted
Rename wasm2asm to wasm2js, emit ESM by default (#1642)
* Rename the `wasm2asm` tool to `wasm2js` This commit performs a relatively simple rename of the `wasm2asm` tool to `wasm2js`. The functionality of the tool doesn't change just yet but it's intended that we'll start generating an ES module instead of just an `asm.js` function soon. * wasm2js: Support `*.wasm` input files Previously `wasm2js` only supported `*.wast` files but to make it a bit easier to use in tooling pipelines this commit adds support for reading in a `*.wasm` file directly. Determining which parser to use depends on the input filename, where the binary parser is used with `*.wasm` files and the wast parser is used for all other files. * wasm2js: Emit ESM imports/exports by default This commit alters the default behavior of `wasm2js` to emit an ESM by default, either importing items from the environment or exporting. Items like initialization of memory are also handled here.
1 parent 3976440 commit f109f3c

File tree

85 files changed

+2506
-1078
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

+2506
-1078
lines changed

CMakeLists.txt

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -227,15 +227,15 @@ SET_PROPERTY(TARGET asm2wasm PROPERTY CXX_STANDARD 11)
227227
SET_PROPERTY(TARGET asm2wasm PROPERTY CXX_STANDARD_REQUIRED ON)
228228
INSTALL(TARGETS asm2wasm DESTINATION ${CMAKE_INSTALL_BINDIR})
229229

230-
SET(wasm2asm_SOURCES
231-
src/tools/wasm2asm.cpp
230+
SET(wasm2js_SOURCES
231+
src/tools/wasm2js.cpp
232232
)
233-
ADD_EXECUTABLE(wasm2asm
234-
${wasm2asm_SOURCES})
235-
TARGET_LINK_LIBRARIES(wasm2asm passes wasm asmjs emscripten-optimizer ir cfg support)
236-
SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD 11)
237-
SET_PROPERTY(TARGET wasm2asm PROPERTY CXX_STANDARD_REQUIRED ON)
238-
INSTALL(TARGETS wasm2asm DESTINATION ${CMAKE_INSTALL_BINDIR})
233+
ADD_EXECUTABLE(wasm2js
234+
${wasm2js_SOURCES})
235+
TARGET_LINK_LIBRARIES(wasm2js passes wasm asmjs emscripten-optimizer ir cfg support)
236+
SET_PROPERTY(TARGET wasm2js PROPERTY CXX_STANDARD 11)
237+
SET_PROPERTY(TARGET wasm2js PROPERTY CXX_STANDARD_REQUIRED ON)
238+
INSTALL(TARGETS wasm2js DESTINATION ${CMAKE_INSTALL_BINDIR})
239239

240240
SET(wasm-emscripten-finalize_SOURCES
241241
src/tools/wasm-emscripten-finalize.cpp

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Compilers built using Binaryen include
1212

1313
* [`asm2wasm`](https://github.com/WebAssembly/binaryen/blob/master/src/asm2wasm.h) which compiles asm.js to WebAssembly
1414
* [`AssemblyScript`](https://github.com/AssemblyScript/assemblyscript) which compiles TypeScript to Binaryen IR
15-
* [`wasm2asm`](https://github.com/WebAssembly/binaryen/blob/master/src/wasm2asm.h) which compiles WebAssembly to asm.js
15+
* [`wasm2js`](https://github.com/WebAssembly/binaryen/blob/master/src/wasm2js.h) which compiles WebAssembly to JS
1616
* [`Asterius`](https://github.com/tweag/asterius) which compiles Haskell to WebAssembly
1717

1818
Binaryen also provides a set of **toolchain utilities** that can
@@ -64,7 +64,7 @@ This repository contains code that builds the following tools in `bin/`:
6464
* **wasm-dis**: Un-assembles WebAssembly in binary format into text format (going through Binaryen IR).
6565
* **wasm-opt**: Loads WebAssembly and runs Binaryen IR passes on it.
6666
* **asm2wasm**: An asm.js-to-WebAssembly compiler, using Emscripten's asm optimizer infrastructure. This is used by Emscripten in Binaryen mode when it uses Emscripten's fastcomp asm.js backend.
67-
* **wasm2asm**: A WebAssembly-to-asm.js compiler (still experimental).
67+
* **wasm2js**: A WebAssembly-to-JS compiler (still experimental).
6868
* **wasm-merge**: Combines wasm files into a single big wasm file (without sophisticated linking).
6969
* **wasm-ctor-eval**: A tool that can execute C++ global constructors ahead of time. Used by Emscripten.
7070
* **wasm-emscripten-finalize**: Takes a wasm binary produced by llvm+lld and performs emscripten-specific passes over it.
@@ -212,11 +212,11 @@ This is separate from that. `asm2wasm` focuses on compiling asm.js to WebAssembl
212212

213213
* How about compiling WebAssembly to asm.js (the opposite direction of `asm2wasm`)? Wouldn't that be useful for polyfilling?
214214

215-
Experimentation with this is happening, in `wasm2asm`.
215+
Experimentation with this is happening, in `wasm2js`.
216216

217217
This would be useful, but it is a much harder task, due to some decisions made in WebAssembly. For example, WebAssembly can have control flow nested inside expressions, which can't directly map to asm.js. It could be supported by outlining the code to another function, or to compiling it down into new basic blocks and control-flow-free instructions, but it is hard to do so in a way that is both fast to do and emits code that is fast to execute. On the other hand, compiling asm.js to WebAssembly is almost straightforward.
218218

219-
We just have to do more work on `wasm2asm` and see how efficient we can make it.
219+
We just have to do more work on `wasm2js` and see how efficient we can make it.
220220

221221
* Can `asm2wasm` compile any asm.js code?
222222

auto_update_tests.py

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
from scripts.test.support import run_command, split_wast, node_test_glue, node_has_webassembly
2323
from scripts.test.shared import (
2424
ASM2WASM, MOZJS, NODEJS, WASM_OPT, WASM_AS, WASM_DIS,
25-
WASM_CTOR_EVAL, WASM_MERGE, WASM_REDUCE, WASM2ASM, WASM_METADCE,
25+
WASM_CTOR_EVAL, WASM_MERGE, WASM_REDUCE, WASM2JS, WASM_METADCE,
2626
WASM_EMSCRIPTEN_FINALIZE, BINARYEN_INSTALL_DIR, BINARYEN_JS,
2727
files_with_pattern, has_shell_timeout, options)
28-
from scripts.test.wasm2asm import tests, spec_tests, extra_wasm2asm_tests, assert_tests, wasm2asm_dir
28+
from scripts.test.wasm2js import tests, spec_tests, extra_wasm2js_tests, assert_tests, wasm2js_dir, wasm2js_blacklist
2929

3030

3131
def update_asm_js_tests():
@@ -339,25 +339,28 @@ def update_ctor_eval_tests():
339339
o.write(actual)
340340

341341

342-
def update_wasm2asm_tests():
343-
print '\n[ checking wasm2asm ]\n'
344-
for wasm in tests + spec_tests + extra_wasm2asm_tests:
342+
def update_wasm2js_tests():
343+
print '\n[ checking wasm2js ]\n'
344+
for wasm in tests + spec_tests + extra_wasm2js_tests:
345345
if not wasm.endswith('.wast'):
346346
continue
347347

348+
if os.path.basename(wasm) in wasm2js_blacklist:
349+
continue
350+
348351
asm = os.path.basename(wasm).replace('.wast', '.2asm.js')
349-
expected_file = os.path.join(wasm2asm_dir, asm)
352+
expected_file = os.path.join(wasm2js_dir, asm)
350353

351-
# we run wasm2asm on tests and spec tests only if the output
354+
# we run wasm2js on tests and spec tests only if the output
352355
# exists - only some work so far. the tests in extra are in
353-
# the test/wasm2asm dir and so are specific to wasm2asm, and
356+
# the test/wasm2js dir and so are specific to wasm2js, and
354357
# we run all of those.
355-
if wasm not in extra_wasm2asm_tests and not os.path.exists(expected_file):
358+
if wasm not in extra_wasm2js_tests and not os.path.exists(expected_file):
356359
continue
357360

358361
print '..', wasm
359362

360-
cmd = WASM2ASM + [os.path.join('test', wasm)]
363+
cmd = WASM2JS + [os.path.join('test', wasm)]
361364
out = run_command(cmd)
362365
with open(expected_file, 'w') as o:
363366
o.write(out)
@@ -370,7 +373,7 @@ def update_wasm2asm_tests():
370373
asserts_expected_file = os.path.join('test', asserts)
371374
traps_expected_file = os.path.join('test', traps)
372375

373-
cmd = WASM2ASM + [os.path.join(wasm2asm_dir, wasm), '--allow-asserts']
376+
cmd = WASM2JS + [os.path.join(wasm2js_dir, wasm), '--allow-asserts']
374377
out = run_command(cmd)
375378
with open(asserts_expected_file, 'w') as o:
376379
o.write(out)
@@ -423,7 +426,7 @@ def main():
423426
update_wasm_merge_tests()
424427
update_binaryen_js_tests()
425428
update_ctor_eval_tests()
426-
update_wasm2asm_tests()
429+
update_wasm2js_tests()
427430
update_metadce_tests()
428431
update_reduce_tests()
429432

check.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
import scripts.test.asm2wasm as asm2wasm
3333
import scripts.test.lld as lld
34-
import scripts.test.wasm2asm as wasm2asm
34+
import scripts.test.wasm2js as wasm2js
3535

3636
if options.interpreter:
3737
print '[ using wasm interpreter at "%s" ]' % options.interpreter
@@ -648,7 +648,7 @@ def main():
648648
run_spec_tests()
649649
run_binaryen_js_tests()
650650
lld.test_wasm_emscripten_finalize()
651-
wasm2asm.test_wasm2asm()
651+
wasm2js.test_wasm2js()
652652
run_validator_tests()
653653
if has_vanilla_emcc and has_vanilla_llvm and 0:
654654
run_vanilla_tests()

scripts/test/node-esm-loader.mjs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// originally lifted from https://nodejs.org/api/esm.html
2+
3+
import path from 'path';
4+
import process from 'process';
5+
import Module from 'module';
6+
7+
const builtins = Module.builtinModules;
8+
9+
const baseURL = new URL('file://');
10+
baseURL.pathname = `${process.cwd()}/`;
11+
12+
export function resolve(specifier, parentModuleURL = baseURL, defaultResolve) {
13+
if (builtins.includes(specifier)) {
14+
return {
15+
url: specifier,
16+
format: 'builtin'
17+
};
18+
}
19+
// Resolve the 'spectest' module to our special module which has some builtins
20+
if (specifier == 'spectest') {
21+
const resolved = new URL('./scripts/test/spectest.js', parentModuleURL);
22+
return {
23+
url: resolved.href,
24+
format: 'esm'
25+
};
26+
}
27+
const resolved = new URL(specifier, parentModuleURL);
28+
return {
29+
url: resolved.href,
30+
format: 'esm'
31+
};
32+
}

scripts/test/shared.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -166,7 +166,7 @@ def is_exe(fpath):
166166
WASM_AS = [os.path.join(options.binaryen_bin, 'wasm-as')]
167167
WASM_DIS = [os.path.join(options.binaryen_bin, 'wasm-dis')]
168168
ASM2WASM = [os.path.join(options.binaryen_bin, 'asm2wasm')]
169-
WASM2ASM = [os.path.join(options.binaryen_bin, 'wasm2asm')]
169+
WASM2JS = [os.path.join(options.binaryen_bin, 'wasm2js')]
170170
WASM_CTOR_EVAL = [os.path.join(options.binaryen_bin, 'wasm-ctor-eval')]
171171
WASM_SHELL = [os.path.join(options.binaryen_bin, 'wasm-shell')]
172172
WASM_MERGE = [os.path.join(options.binaryen_bin, 'wasm-merge')]

scripts/test/spectest.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export function print(...args) {
2+
console.log(...args);
3+
}

scripts/test/support.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ def to_end(j):
147147

148148

149149
def run_command(cmd, expected_status=0, stderr=None,
150-
expected_err=None, err_contains=False):
150+
expected_err=None, err_contains=False, err_ignore=None):
151151
if expected_err is not None:
152152
assert stderr == subprocess.PIPE or stderr is None,\
153153
"Can't redirect stderr if using expected_err"
@@ -158,11 +158,13 @@ def run_command(cmd, expected_status=0, stderr=None,
158158
code = proc.returncode
159159
if expected_status is not None and code != expected_status:
160160
raise Exception(('run_command failed (%s)' % code, out + str(err or '')))
161-
err_correct = expected_err is None or \
162-
(expected_err in err if err_contains else expected_err == err)
163-
if not err_correct:
164-
raise Exception(('run_command unexpected stderr',
165-
"expected '%s', actual '%s'" % (expected_err, err)))
161+
if expected_err is not None:
162+
if err_ignore is not None:
163+
err = "\n".join([line for line in err.split('\n') if err_ignore not in line])
164+
err_correct = expected_err in err if err_contains else expected_err == err
165+
if not err_correct:
166+
raise Exception(('run_command unexpected stderr',
167+
"expected '%s', actual '%s'" % (expected_err, err)))
166168
return out
167169

168170

Lines changed: 33 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818

1919
from support import run_command
2020
from shared import (
21-
WASM2ASM, MOZJS, NODEJS, fail_if_not_identical, options, tests,
21+
WASM2JS, MOZJS, NODEJS, fail_if_not_identical, options, tests,
2222
fail_if_not_identical_to_file
2323
)
2424

@@ -27,59 +27,59 @@
2727
spec_tests = [os.path.join(spec_dir, t)
2828
for t in sorted(os.listdir(spec_dir))
2929
if '.fail' not in t]
30-
wasm2asm_dir = os.path.join(options.binaryen_test, 'wasm2asm')
31-
extra_wasm2asm_tests = [os.path.join(wasm2asm_dir, t) for t in
32-
sorted(os.listdir(wasm2asm_dir))]
33-
assert_tests = ['wasm2asm.wast.asserts']
30+
wasm2js_dir = os.path.join(options.binaryen_test, 'wasm2js')
31+
extra_wasm2js_tests = [os.path.join(wasm2js_dir, t) for t in
32+
sorted(os.listdir(wasm2js_dir))]
33+
assert_tests = ['wasm2js.wast.asserts']
34+
# These tests exercise functionality not supported by wasm2js
35+
wasm2js_blacklist = ['empty_imported_table.wast']
3436

3537

36-
def test_wasm2asm_output():
37-
for wasm in tests + spec_tests + extra_wasm2asm_tests:
38+
def test_wasm2js_output():
39+
for wasm in tests + spec_tests + extra_wasm2js_tests:
3840
if not wasm.endswith('.wast'):
3941
continue
42+
basename = os.path.basename(wasm)
43+
if basename in wasm2js_blacklist:
44+
continue
4045

41-
asm = os.path.basename(wasm).replace('.wast', '.2asm.js')
42-
expected_file = os.path.join(wasm2asm_dir, asm)
46+
asm = basename.replace('.wast', '.2asm.js')
47+
expected_file = os.path.join(wasm2js_dir, asm)
4348

4449
if not os.path.exists(expected_file):
4550
continue
4651

4752
print '..', wasm
4853

49-
cmd = WASM2ASM + [os.path.join(options.binaryen_test, wasm)]
54+
cmd = WASM2JS + [os.path.join(options.binaryen_test, wasm)]
5055
out = run_command(cmd)
5156
fail_if_not_identical_to_file(out, expected_file)
5257

5358
if not NODEJS and not MOZJS:
5459
print 'No JS interpreters. Skipping spec tests.'
5560
continue
5661

57-
open('a.2asm.js', 'w').write(out)
62+
open('a.2asm.mjs', 'w').write(out)
5863

5964
cmd += ['--allow-asserts']
6065
out = run_command(cmd)
6166

62-
open('a.2asm.asserts.js', 'w').write(out)
67+
open('a.2asm.asserts.mjs', 'w').write(out)
6368

64-
# verify asm.js is valid js
69+
# verify asm.js is valid js, note that we're using --experimental-modules
70+
# to enable ESM syntax and we're also passing a custom loader to handle the
71+
# `spectest` module in our tests.
6572
if NODEJS:
66-
out = run_command([NODEJS, 'a.2asm.js'])
73+
node = [NODEJS, '--experimental-modules', '--loader', './scripts/test/node-esm-loader.mjs']
74+
cmd = node[:]
75+
cmd.append('a.2asm.mjs')
76+
out = run_command(cmd)
6777
fail_if_not_identical(out, '')
68-
out = run_command([NODEJS, 'a.2asm.asserts.js'], expected_err='')
78+
cmd = node[:]
79+
cmd.append('a.2asm.asserts.mjs')
80+
out = run_command(cmd, expected_err='', err_ignore='The ESM module loader is experimental')
6981
fail_if_not_identical(out, '')
7082

71-
if MOZJS:
72-
# verify asm.js validates, if this is asm.js code (we emit
73-
# almost-asm instead when we need to)
74-
if 'use asm' in open('a.2asm.js').read():
75-
# check only subset of err because mozjs emits timing info
76-
out = run_command([MOZJS, '-w', 'a.2asm.js'],
77-
expected_err='Successfully compiled asm.js code',
78-
err_contains=True)
79-
fail_if_not_identical(out, '')
80-
out = run_command([MOZJS, 'a.2asm.asserts.js'], expected_err='')
81-
fail_if_not_identical(out, '')
82-
8383

8484
def test_asserts_output():
8585
for wasm in assert_tests:
@@ -90,8 +90,8 @@ def test_asserts_output():
9090
asserts_expected_file = os.path.join(options.binaryen_test, asserts)
9191
traps_expected_file = os.path.join(options.binaryen_test, traps)
9292

93-
wasm = os.path.join(wasm2asm_dir, wasm)
94-
cmd = WASM2ASM + [wasm, '--allow-asserts']
93+
wasm = os.path.join(wasm2js_dir, wasm)
94+
cmd = WASM2JS + [wasm, '--allow-asserts']
9595
out = run_command(cmd)
9696
fail_if_not_identical_to_file(out, asserts_expected_file)
9797

@@ -100,11 +100,11 @@ def test_asserts_output():
100100
fail_if_not_identical_to_file(out, traps_expected_file)
101101

102102

103-
def test_wasm2asm():
104-
print '\n[ checking wasm2asm testcases... ]\n'
105-
test_wasm2asm_output()
103+
def test_wasm2js():
104+
print '\n[ checking wasm2js testcases... ]\n'
105+
test_wasm2js_output()
106106
test_asserts_output()
107107

108108

109109
if __name__ == "__main__":
110-
test_wasm2asm()
110+
test_wasm2js()

src/binaryen-c.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
#include "wasm-printing.h"
3030
#include "wasm-s-parser.h"
3131
#include "wasm-validator.h"
32-
#include "wasm2asm.h"
32+
#include "wasm2js.h"
3333
#include "cfg/Relooper.h"
3434
#include "ir/utils.h"
3535
#include "shell-interface.h"
@@ -2008,9 +2008,9 @@ void BinaryenModulePrintAsmjs(BinaryenModuleRef module) {
20082008
}
20092009

20102010
Module* wasm = (Module*)module;
2011-
Wasm2AsmBuilder::Flags builderFlags;
2012-
Wasm2AsmBuilder wasm2asm(builderFlags);
2013-
Ref asmjs = wasm2asm.processWasm(wasm);
2011+
Wasm2JSBuilder::Flags builderFlags;
2012+
Wasm2JSBuilder wasm2js(builderFlags);
2013+
Ref asmjs = wasm2js.processWasm(wasm);
20142014
JSPrinter jser(true, true, asmjs);
20152015
jser.printAst();
20162016

0 commit comments

Comments
 (0)