Skip to content

Commit 8d4bcd6

Browse files
authored
wasm2js: run full optimizations during the pipeline (#2071)
We flatten for the i64 lowering etc. passes, and it is worth optimizing afterwards, to clean up stuff they created. That is run if the user ran wasm2js with an optimization level (like wasm2js -O3). Split the test files to check both optimized and unoptimized code.
1 parent fba743c commit 8d4bcd6

80 files changed

Lines changed: 23001 additions & 6058 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

scripts/test/wasm2js.py

Lines changed: 80 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -36,63 +36,68 @@
3636

3737

3838
def test_wasm2js_output():
39-
for wasm in tests + spec_tests + extra_wasm2js_tests:
40-
if not wasm.endswith('.wast'):
41-
continue
42-
basename = os.path.basename(wasm)
43-
if basename in wasm2js_blacklist:
44-
continue
39+
for opt in (0, 1):
40+
for wasm in tests + spec_tests + extra_wasm2js_tests:
41+
if not wasm.endswith('.wast'):
42+
continue
43+
basename = os.path.basename(wasm)
44+
if basename in wasm2js_blacklist:
45+
continue
4546

46-
asm = basename.replace('.wast', '.2asm.js')
47-
expected_file = os.path.join(wasm2js_dir, asm)
47+
asm = basename.replace('.wast', '.2asm.js')
48+
expected_file = os.path.join(wasm2js_dir, asm)
49+
if opt:
50+
expected_file += '.opt'
4851

49-
if not os.path.exists(expected_file):
50-
continue
52+
if not os.path.exists(expected_file):
53+
continue
5154

52-
print '..', wasm
55+
print '..', wasm
5356

54-
t = os.path.join(options.binaryen_test, wasm)
57+
t = os.path.join(options.binaryen_test, wasm)
5558

56-
all_out = []
59+
all_out = []
5760

58-
for module, asserts in split_wast(t):
59-
with open('split.wast', 'w') as o:
60-
o.write(module + '\n'.join(asserts))
61+
for module, asserts in split_wast(t):
62+
with open('split.wast', 'w') as o:
63+
o.write(module + '\n'.join(asserts))
6164

62-
cmd = WASM2JS + ['split.wast', '-O']
63-
if 'emscripten' in wasm:
64-
cmd += ['--emscripten']
65-
out = run_command(cmd)
66-
all_out.append(out)
65+
cmd = WASM2JS + ['split.wast']
66+
if opt:
67+
cmd += ['-O']
68+
if 'emscripten' in wasm:
69+
cmd += ['--emscripten']
70+
out = run_command(cmd)
71+
all_out.append(out)
6772

68-
if not NODEJS and not MOZJS:
69-
print 'No JS interpreters. Skipping spec tests.'
70-
continue
73+
if not NODEJS and not MOZJS:
74+
print 'No JS interpreters. Skipping spec tests.'
75+
continue
7176

72-
open('a.2asm.mjs', 'w').write(out)
77+
open('a.2asm.mjs', 'w').write(out)
7378

74-
cmd += ['--allow-asserts']
75-
out = run_command(cmd)
76-
# also verify it passes pass-debug verifications
77-
with_pass_debug(lambda: run_command(cmd))
79+
cmd += ['--allow-asserts']
80+
out = run_command(cmd)
81+
# also verify it passes pass-debug verifications
82+
with_pass_debug(lambda: run_command(cmd))
7883

79-
open('a.2asm.asserts.mjs', 'w').write(out)
84+
open('a.2asm.asserts.mjs', 'w').write(out)
8085

81-
# verify asm.js is valid js, note that we're using --experimental-modules
82-
# to enable ESM syntax and we're also passing a custom loader to handle the
83-
# `spectest` and `env` modules in our tests.
84-
if NODEJS:
85-
node = [NODEJS, '--experimental-modules', '--loader', './scripts/test/node-esm-loader.mjs']
86-
cmd = node[:]
87-
cmd.append('a.2asm.mjs')
88-
out = run_command(cmd)
89-
fail_if_not_identical(out, '')
90-
cmd = node[:]
91-
cmd.append('a.2asm.asserts.mjs')
92-
out = run_command(cmd, expected_err='', err_ignore='The ESM module loader is experimental')
93-
fail_if_not_identical(out, '')
86+
# verify asm.js is valid js, note that we're using --experimental-modules
87+
# to enable ESM syntax and we're also passing a custom loader to handle the
88+
# `spectest` and `env` modules in our tests.
89+
if NODEJS:
90+
node = [NODEJS, '--experimental-modules', '--loader', './scripts/test/node-esm-loader.mjs']
91+
cmd = node[:]
92+
cmd.append('a.2asm.mjs')
93+
out = run_command(cmd)
94+
fail_if_not_identical(out, '')
95+
cmd = node[:]
96+
cmd.append('a.2asm.asserts.mjs')
97+
out = run_command(cmd, expected_err='', err_ignore='The ESM module loader is experimental')
98+
fail_if_not_identical(out, '')
9499

95-
fail_if_not_identical_to_file(''.join(all_out), expected_file)
100+
fail_if_not_identical_to_file(''.join(all_out), expected_file)
96101

97102

98103
def test_asserts_output():
@@ -123,41 +128,46 @@ def test_wasm2js():
123128
def update_wasm2js_tests():
124129
print '\n[ checking wasm2js ]\n'
125130

126-
for wasm in tests + spec_tests + extra_wasm2js_tests:
127-
if not wasm.endswith('.wast'):
128-
continue
131+
for opt in (0, 1):
132+
for wasm in tests + spec_tests + extra_wasm2js_tests:
133+
if not wasm.endswith('.wast'):
134+
continue
129135

130-
if os.path.basename(wasm) in wasm2js_blacklist:
131-
continue
136+
if os.path.basename(wasm) in wasm2js_blacklist:
137+
continue
132138

133-
asm = os.path.basename(wasm).replace('.wast', '.2asm.js')
134-
expected_file = os.path.join(wasm2js_dir, asm)
139+
asm = os.path.basename(wasm).replace('.wast', '.2asm.js')
140+
expected_file = os.path.join(wasm2js_dir, asm)
141+
if opt:
142+
expected_file += '.opt'
135143

136-
# we run wasm2js on tests and spec tests only if the output
137-
# exists - only some work so far. the tests in extra are in
138-
# the test/wasm2js dir and so are specific to wasm2js, and
139-
# we run all of those.
140-
if wasm not in extra_wasm2js_tests and not os.path.exists(expected_file):
141-
continue
144+
# we run wasm2js on tests and spec tests only if the output
145+
# exists - only some work so far. the tests in extra are in
146+
# the test/wasm2js dir and so are specific to wasm2js, and
147+
# we run all of those.
148+
if wasm not in extra_wasm2js_tests and not os.path.exists(expected_file):
149+
continue
142150

143-
print '..', wasm
151+
print '..', wasm
144152

145-
t = os.path.join(options.binaryen_test, wasm)
153+
t = os.path.join(options.binaryen_test, wasm)
146154

147-
all_out = []
155+
all_out = []
148156

149-
for module, asserts in split_wast(t):
150-
with open('split.wast', 'w') as o:
151-
o.write(module + '\n'.join(asserts))
157+
for module, asserts in split_wast(t):
158+
with open('split.wast', 'w') as o:
159+
o.write(module + '\n'.join(asserts))
152160

153-
cmd = WASM2JS + ['split.wast', '-O']
154-
if 'emscripten' in wasm:
155-
cmd += ['--emscripten']
156-
out = run_command(cmd)
157-
all_out.append(out)
161+
cmd = WASM2JS + ['split.wast']
162+
if opt:
163+
cmd += ['-O']
164+
if 'emscripten' in wasm:
165+
cmd += ['--emscripten']
166+
out = run_command(cmd)
167+
all_out.append(out)
158168

159-
with open(expected_file, 'w') as o:
160-
o.write(''.join(all_out))
169+
with open(expected_file, 'w') as o:
170+
o.write(''.join(all_out))
161171

162172
for wasm in assert_tests:
163173
print '..', wasm

src/wasm2js.h

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -267,29 +267,40 @@ Ref Wasm2JSBuilder::processWasm(Module* wasm, Name funcName) {
267267
// If later on they aren't needed, we'll clean them up.
268268
ABI::wasm2js::ensureScratchMemoryHelpers(wasm);
269269

270-
PassRunner runner(wasm);
271-
runner.add<AutoDrop>();
272-
runner.add("legalize-js-interface");
273-
// First up remove as many non-JS operations we can, including things like
274-
// 64-bit integer multiplication/division, `f32.nearest` instructions, etc.
275-
// This may inject intrinsics which use i64 so it needs to be run before the
276-
// i64-to-i32 lowering pass.
277-
runner.add("remove-non-js-ops");
278-
// Currently the i64-to-32 lowering pass requires that `flatten` be run before
279-
// it to produce correct code. For some more details about this see #1480
280-
runner.add("flatten");
281-
runner.add("i64-to-i32-lowering");
282-
runner.add("flatten");
283-
runner.add("simplify-locals-notee-nostructure");
284-
runner.add("reorder-locals");
285-
runner.add("remove-unused-names");
286-
runner.add("vacuum");
287-
runner.add("remove-unused-module-elements");
288-
runner.setDebug(flags.debug);
289-
runner.run();
290-
291-
// Make sure we didn't corrupt anything if we're in --allow-asserts mode (aka
292-
// tests)
270+
// Process the code, and optimize if relevant.
271+
// First, do the lowering to a JS-friendly subset.
272+
{
273+
PassRunner runner(wasm, options);
274+
runner.add<AutoDrop>();
275+
runner.add("legalize-js-interface");
276+
// First up remove as many non-JS operations we can, including things like
277+
// 64-bit integer multiplication/division, `f32.nearest` instructions, etc.
278+
// This may inject intrinsics which use i64 so it needs to be run before the
279+
// i64-to-i32 lowering pass.
280+
runner.add("remove-non-js-ops");
281+
// Currently the i64-to-32 lowering pass requires that `flatten` be run
282+
// before it to produce correct code. For some more details about this see
283+
// #1480
284+
runner.add("flatten");
285+
runner.add("i64-to-i32-lowering");
286+
// Next, optimize that as best we can. This should not generate
287+
// non-JS-friendly things.
288+
if (options.optimizeLevel > 0) {
289+
runner.addDefaultOptimizationPasses();
290+
}
291+
// Finally, get the code into the flat form we need for wasm2js itself, and
292+
// optimize that a little in a way that keeps flat property.
293+
runner.add("flatten");
294+
runner.add("simplify-locals-notee-nostructure");
295+
// TODO: coalesce-locals?
296+
runner.add("reorder-locals");
297+
runner.add("remove-unused-names");
298+
runner.add("vacuum");
299+
runner.add("remove-unused-module-elements");
300+
runner.setDebug(flags.debug);
301+
runner.run();
302+
}
303+
293304
#ifndef NDEBUG
294305
if (!WasmValidator().validate(*wasm)) {
295306
WasmPrinter::printModule(wasm);

test/wasm2js/address.2asm.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,24 @@ function asmFunc(global, env, buffer) {
2626
function $0(i) {
2727
i = i | 0;
2828
var wasm2js_i32$0 = 0;
29-
print(HEAPU8[i | 0] | 0);
30-
print(HEAPU8[i + 1 | 0] | 0);
31-
print(HEAPU8[i + 2 | 0] | 0);
32-
print(HEAPU8[i + 25 | 0] | 0);
33-
print(HEAPU16[i >> 1] | 0);
34-
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 | 0] | HEAPU8[wasm2js_i32$0 + 1 | 0] << 8) | 0);
35-
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 + 1 | 0] | HEAPU8[wasm2js_i32$0 + 2 | 0] << 8) | 0);
36-
print(HEAPU16[i + 2 >> 1] | 0);
37-
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 + 25 | 0] | HEAPU8[wasm2js_i32$0 + 26 | 0] << 8) | 0);
38-
print(HEAP32[i >> 2] | 0);
39-
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 + 1 | 0] | HEAPU8[wasm2js_i32$0 + 2 | 0] << 8 | HEAPU8[wasm2js_i32$0 + 3 | 0] << 16 | HEAPU8[wasm2js_i32$0 + 4 | 0] << 24) | 0);
40-
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 + 2 | 0] | HEAPU8[wasm2js_i32$0 + 3 | 0] << 8 | HEAPU8[wasm2js_i32$0 + 4 | 0] << 16 | HEAPU8[wasm2js_i32$0 + 5 | 0] << 24) | 0);
41-
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 + 25 | 0] | HEAPU8[wasm2js_i32$0 + 26 | 0] << 8 | HEAPU8[wasm2js_i32$0 + 27 | 0] << 16 | HEAPU8[wasm2js_i32$0 + 28 | 0] << 24) | 0);
29+
print(HEAPU8[i >> 0] | 0 | 0);
30+
print(HEAPU8[(i + 1 | 0) >> 0] | 0 | 0);
31+
print(HEAPU8[(i + 2 | 0) >> 0] | 0 | 0);
32+
print(HEAPU8[(i + 25 | 0) >> 0] | 0 | 0);
33+
print(HEAPU16[i >> 1] | 0 | 0);
34+
print((wasm2js_i32$0 = i, HEAPU8[wasm2js_i32$0 >> 0] | 0 | 0 | (HEAPU8[(wasm2js_i32$0 + 1 | 0) >> 0] | 0 | 0) << 8) | 0);
35+
print((wasm2js_i32$0 = i, HEAPU8[(wasm2js_i32$0 + 1 | 0) >> 0] | 0 | 0 | (HEAPU8[(wasm2js_i32$0 + 2 | 0) >> 0] | 0 | 0) << 8) | 0);
36+
print(HEAPU16[(i + 2 | 0) >> 1] | 0 | 0);
37+
print((wasm2js_i32$0 = i, HEAPU8[(wasm2js_i32$0 + 25 | 0) >> 0] | 0 | 0 | (HEAPU8[(wasm2js_i32$0 + 26 | 0) >> 0] | 0 | 0) << 8) | 0);
38+
print(HEAP32[i >> 2] | 0 | 0);
39+
print((wasm2js_i32$0 = i, HEAPU8[(wasm2js_i32$0 + 1 | 0) >> 0] | 0 | 0 | (HEAPU8[(wasm2js_i32$0 + 2 | 0) >> 0] | 0 | 0) << 8 | (HEAPU8[(wasm2js_i32$0 + 3 | 0) >> 0] | 0 | 0) << 16 | (HEAPU8[(wasm2js_i32$0 + 4 | 0) >> 0] | 0 | 0) << 24) | 0);
40+
print((wasm2js_i32$0 = i, HEAPU8[(wasm2js_i32$0 + 2 | 0) >> 0] | 0 | 0 | (HEAPU8[(wasm2js_i32$0 + 3 | 0) >> 0] | 0 | 0) << 8 | (HEAPU8[(wasm2js_i32$0 + 4 | 0) >> 0] | 0 | 0) << 16 | (HEAPU8[(wasm2js_i32$0 + 5 | 0) >> 0] | 0 | 0) << 24) | 0);
41+
print((wasm2js_i32$0 = i, HEAPU8[(wasm2js_i32$0 + 25 | 0) >> 0] | 0 | 0 | (HEAPU8[(wasm2js_i32$0 + 26 | 0) >> 0] | 0 | 0) << 8 | (HEAPU8[(wasm2js_i32$0 + 27 | 0) >> 0] | 0 | 0) << 16 | (HEAPU8[(wasm2js_i32$0 + 28 | 0) >> 0] | 0 | 0) << 24) | 0);
4242
}
4343

4444
function $1(i) {
4545
i = i | 0;
46-
HEAP32[i + 4294967295 >> 2];
46+
HEAP32[(i + 4294967295 | 0) >> 2] | 0;
4747
}
4848

4949
var FUNCTION_TABLE = [];

test/wasm2js/base64.2asm.js.opt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
2+
function asmFunc(global, env, buffer) {
3+
"almost asm";
4+
var HEAP8 = new global.Int8Array(buffer);
5+
var HEAP16 = new global.Int16Array(buffer);
6+
var HEAP32 = new global.Int32Array(buffer);
7+
var HEAPU8 = new global.Uint8Array(buffer);
8+
var HEAPU16 = new global.Uint16Array(buffer);
9+
var HEAPU32 = new global.Uint32Array(buffer);
10+
var HEAPF32 = new global.Float32Array(buffer);
11+
var HEAPF64 = new global.Float64Array(buffer);
12+
var Math_imul = global.Math.imul;
13+
var Math_fround = global.Math.fround;
14+
var Math_abs = global.Math.abs;
15+
var Math_clz32 = global.Math.clz32;
16+
var Math_min = global.Math.min;
17+
var Math_max = global.Math.max;
18+
var Math_floor = global.Math.floor;
19+
var Math_ceil = global.Math.ceil;
20+
var Math_sqrt = global.Math.sqrt;
21+
var abort = env.abort;
22+
var nan = global.NaN;
23+
var infinity = global.Infinity;
24+
var FUNCTION_TABLE = [];
25+
return {
26+
27+
};
28+
}
29+
30+
var memasmFunc = new ArrayBuffer(65536);
31+
var retasmFunc = asmFunc({Math,Int8Array,Uint8Array,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array,NaN,Infinity}, {abort:function() { throw new Error('abort'); }},memasmFunc);

test/wasm2js/block.2asm.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ function asmFunc(global, env, buffer) {
5757

5858
function $6() {
5959
dummy();
60-
return __wasm_ctz_i32(13) | 0;
60+
return __wasm_ctz_i32(13 | 0) | 0 | 0;
6161
}
6262

6363
function $7() {
@@ -159,10 +159,11 @@ function asmFunc(global, env, buffer) {
159159
}
160160

161161
function __wasm_ctz_i32(var$0) {
162+
var$0 = var$0 | 0;
162163
if (var$0) {
163-
return 31 - Math_clz32(var$0 + -1 ^ var$0) | 0
164+
return 31 - Math_clz32((var$0 + -1 | 0) ^ var$0 | 0) | 0 | 0
164165
}
165-
return 32;
166+
return 32 | 0;
166167
}
167168

168169
var FUNCTION_TABLE = [];

0 commit comments

Comments
 (0)