Skip to content

Commit a9f91b9

Browse files
Share trap mode between asm2wasm and s2wasm (#1168)
* Extract Asm2WasmBuilder::TrapMode to shared FloatTrapMode * Extract makeTrappingI32Binary * Extract makeTrappingI64Binary * Extract asm2wasm test script into scripts/test/asm2wasm.py This matches s2wasm.py, and makes iterating on asm2wasm slightly faster. * Simplify callsites with an arg struct * Combine func adding across i32 and i64 * Support f32-to-int in asm2wasm * Add BinaryenTrapMode pass, run pass from s2wasm * BinaryenTrapMode pass takes trap context as a parameter * Pass fully supports non-trapping binary ops * Defer adding functions until after iteration (hackily) * Update asm2wasm to work with deferred function adding, rebuild tests * Extract makeTrappingFloatToInt32 * Extract makeTrappingFloatToInt64 * Add unary conversions to trap pass * Add functions in the pass itself * Set s2wasm trap mode with command-line arguments * Print BINARYEN_PASS_DEBUG state when testing * Get asm2wasm using the BinaryenTrapMode pass instead of handling it inline * Also handle f32 to int in asm2wasm * Make BinaryenTrapMode only need a FloatTrapMode from the caller * Just pass the current binary Expression directly * Combine makeTrappingI32Binary with makeTrappingI64Binary * Pass Unary expr to makeTrappingFloatToInt32 * Unify makeTrappingFloatToInt32 & 64 * Move makeTrapping* functions inside BinaryenTrapMode, make addedFunctions non-static * Remove FloatTrapContext * Minor cleanups * Extract some smaller subfunctions * Emit name switch/casing, rename is32Bit to isI64 for consistency * Rename BinaryenTrapMode to FloatTrap, make trap mode a nested enum * Add some comments explaining why FloatTrap is non-parallel * Rename addedFunctions to generatedFunctions for precision * Rename move and split float-clamp.h to passes/FloatTrap.(h|cpp) * Use builder instead of allocator * Instantiate trap handling passes via the pass manager * Move passes/FloatTrap.h to ast/trapping.h * Add helper function to add trap-handling passes * Add trap mode pass tests * Rename FloatTrap.cpp to TrapMode.cpp * Add s2wasm trap mode tests. Force float->int conversion to be signed * Add trapping_sint_div_s test to unit.asm.js * Fix flake8 issues with test scripts * Update pass description comment * Extract building functions methods * Make generate functions into top-level functions * Add GeneratedTrappingFunctions class to manage function/import additions * Move ensure/makeTrapping functions outside class scope * Use GeneratedTrappingFunctions to add immediately in asm2wasm mode * Remove trapping_sint_div_s test We only added it to test that trapping divisions would get constant-folded at the correct time. Now that we're not changing the timing of trapping modes, the test is unneeded (and problematic). * Review feedback, add validator/*.wasm to .gitignore * Add support for unsigned float-to-int conversion * Use opcode directly instead of bools * Update s2wasm clamp test for unsigned ftoi
1 parent 28d670a commit a9f91b9

33 files changed

+2312
-510
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ trace.cpp
3030
test/wasm-binaries-*.tbz2
3131
test/wasm-torture-s-*.tbz2
3232
test/wasm-install/
33+
test/validator/*.wasm
3334

3435
*.pyc
3536
CMakeFiles/

check.py

Lines changed: 5 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,15 @@
2222

2323
from scripts.test.support import run_command, split_wast
2424
from scripts.test.shared import (
25-
ASM2WASM, BIN_DIR, EMCC, MOZJS, NATIVECC, NATIVEXX, NODEJS, S2WASM_EXE,
25+
BIN_DIR, EMCC, MOZJS, NATIVECC, NATIVEXX, NODEJS, S2WASM_EXE,
2626
WASM_AS, WASM_CTOR_EVAL, WASM_OPT, WASM_SHELL, WASM_MERGE, WASM_SHELL_EXE,
2727
WASM_DIS, WASM_REDUCE, binary_format_check, delete_from_orbit, fail, fail_with_error,
2828
fail_if_not_identical, fail_if_not_contained, has_vanilla_emcc,
2929
has_vanilla_llvm, minify_check, num_failures, options, tests,
3030
requested, warnings, has_shell_timeout
3131
)
3232

33+
import scripts.test.asm2wasm as asm2wasm
3334
import scripts.test.s2wasm as s2wasm
3435
import scripts.test.wasm2asm as wasm2asm
3536

@@ -144,6 +145,7 @@ def run_wasm_opt_tests():
144145
actual = actual.replace('printing before:\n', '')
145146

146147
expected = open(f, 'rb').read()
148+
147149
if actual != expected:
148150
fail(actual, expected)
149151

@@ -152,126 +154,6 @@ def run_wasm_opt_tests():
152154

153155
minify_check(t)
154156

155-
def run_asm2wasm_tests():
156-
print '[ checking asm2wasm testcases... ]\n'
157-
158-
for asm in tests:
159-
if asm.endswith('.asm.js'):
160-
for precise in [0, 1, 2]:
161-
for opts in [1, 0]:
162-
cmd = ASM2WASM + [os.path.join(options.binaryen_test, asm)]
163-
wasm = asm.replace('.asm.js', '.fromasm')
164-
if not precise:
165-
cmd += ['--emit-potential-traps', '--ignore-implicit-traps']
166-
wasm += '.imprecise'
167-
elif precise == 2:
168-
cmd += ['--emit-clamped-potential-traps']
169-
wasm += '.clamp'
170-
if not opts:
171-
wasm += '.no-opts'
172-
if precise:
173-
cmd += ['-O0'] # test that -O0 does nothing
174-
else:
175-
cmd += ['-O']
176-
if 'debugInfo' in asm:
177-
cmd += ['-g']
178-
if 'noffi' in asm:
179-
cmd += ['--no-legalize-javascript-ffi']
180-
if precise and opts:
181-
# test mem init importing
182-
open('a.mem', 'wb').write(asm)
183-
cmd += ['--mem-init=a.mem']
184-
if asm[0] == 'e':
185-
cmd += ['--mem-base=1024']
186-
if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm:
187-
cmd += ['--wasm-only']
188-
wasm = os.path.join(options.binaryen_test, wasm)
189-
print '..', asm, wasm
190-
191-
def do_asm2wasm_test():
192-
actual = run_command(cmd)
193-
194-
# verify output
195-
if not os.path.exists(wasm):
196-
fail_with_error('output .wast file %s does not exist' % wasm)
197-
expected = open(wasm, 'rb').read()
198-
if actual != expected:
199-
fail(actual, expected)
200-
201-
binary_format_check(wasm, verify_final_result=False)
202-
203-
# test both normally and with pass debug (so each inter-pass state is validated)
204-
old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG')
205-
try:
206-
os.environ['BINARYEN_PASS_DEBUG'] = '1'
207-
do_asm2wasm_test()
208-
del os.environ['BINARYEN_PASS_DEBUG']
209-
do_asm2wasm_test()
210-
finally:
211-
if old_pass_debug is not None:
212-
os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug
213-
else:
214-
if 'BINARYEN_PASS_DEBUG' in os.environ:
215-
del os.environ['BINARYEN_PASS_DEBUG']
216-
217-
# verify in wasm
218-
if options.interpreter:
219-
# remove imports, spec interpreter doesn't know what to do with them
220-
subprocess.check_call(WASM_OPT + ['--remove-imports', wasm], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE)
221-
proc = subprocess.Popen([options.interpreter, 'ztemp.wast'], stderr=subprocess.PIPE)
222-
out, err = proc.communicate()
223-
if proc.returncode != 0:
224-
try: # to parse the error
225-
reported = err.split(':')[1]
226-
start, end = reported.split('-')
227-
start_line, start_col = map(int, start.split('.'))
228-
lines = open('ztemp.wast').read().split('\n')
229-
print
230-
print '='*80
231-
print lines[start_line-1]
232-
print (' '*(start_col-1)) + '^'
233-
print (' '*(start_col-2)) + '/_\\'
234-
print '='*80
235-
print err
236-
except Exception, e:
237-
fail_with_error('wasm interpreter error: ' + err) # failed to pretty-print
238-
fail_with_error('wasm interpreter error')
239-
240-
# verify debug info
241-
if 'debugInfo' in asm:
242-
jsmap = 'a.wasm.map'
243-
cmd += ['--source-map', jsmap,
244-
'--source-map-url', 'http://example.org/' + jsmap,
245-
'-o', 'a.wasm']
246-
run_command(cmd)
247-
if not os.path.isfile(jsmap):
248-
fail_with_error('Debug info map not created: %s' % jsmap)
249-
with open(wasm + '.map', 'rb') as expected:
250-
with open(jsmap, 'rb') as actual:
251-
fail_if_not_identical(actual.read(), expected.read())
252-
with open('a.wasm', 'rb') as binary:
253-
url_section_name = bytearray([16]) + bytearray('sourceMappingURL')
254-
payload = 'http://example.org/' + jsmap
255-
assert len(payload) < 256, 'name too long'
256-
url_section_contents = bytearray([len(payload)]) + bytearray(payload)
257-
print url_section_name
258-
binary_contents = bytearray(binary.read())
259-
if url_section_name not in binary_contents:
260-
fail_with_error('source map url section not found in binary')
261-
if url_section_contents not in binary_contents[binary_contents.index(url_section_name):]:
262-
fail_with_error('source map url not found in url section')
263-
264-
265-
print '\n[ checking asm2wasm binary reading/writing... ]\n'
266-
267-
asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js')
268-
delete_from_orbit('a.wasm')
269-
delete_from_orbit('b.wast')
270-
run_command(ASM2WASM + [asmjs, '-o', 'a.wasm'])
271-
assert open('a.wasm', 'rb').read()[0] == '\0', 'we emit binary by default'
272-
run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S'])
273-
assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S'
274-
275157
def run_wasm_dis_tests():
276158
print '\n[ checking wasm-dis on provided binaries... ]\n'
277159

@@ -728,7 +610,8 @@ def execute():
728610
# Run all the tests
729611
run_help_tests()
730612
run_wasm_opt_tests()
731-
run_asm2wasm_tests()
613+
asm2wasm.test_asm2wasm()
614+
asm2wasm.test_asm2wasm_binary()
732615
run_wasm_dis_tests()
733616
run_wasm_merge_tests()
734617
run_ctor_eval_tests()

scripts/test/asm2wasm.py

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
#!/usr/bin/env python
2+
3+
# Copyright 2017 WebAssembly Community Group participants
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
import os
18+
import subprocess
19+
20+
from support import run_command
21+
from shared import (
22+
ASM2WASM, WASM_OPT, binary_format_check, delete_from_orbit,
23+
fail, fail_with_error, fail_if_not_identical, options, tests
24+
)
25+
26+
27+
def test_asm2wasm():
28+
print '[ checking asm2wasm testcases... ]\n'
29+
30+
for asm in tests:
31+
if not asm.endswith('.asm.js'):
32+
continue
33+
for precise in [0, 1, 2]:
34+
for opts in [1, 0]:
35+
cmd = ASM2WASM + [os.path.join(options.binaryen_test, asm)]
36+
wasm = asm.replace('.asm.js', '.fromasm')
37+
if not precise:
38+
cmd += ['--emit-potential-traps', '--ignore-implicit-traps']
39+
wasm += '.imprecise'
40+
elif precise == 2:
41+
cmd += ['--emit-clamped-potential-traps']
42+
wasm += '.clamp'
43+
if not opts:
44+
wasm += '.no-opts'
45+
if precise:
46+
cmd += ['-O0'] # test that -O0 does nothing
47+
else:
48+
cmd += ['-O']
49+
if 'debugInfo' in asm:
50+
cmd += ['-g']
51+
if 'noffi' in asm:
52+
cmd += ['--no-legalize-javascript-ffi']
53+
if precise and opts:
54+
# test mem init importing
55+
open('a.mem', 'wb').write(asm)
56+
cmd += ['--mem-init=a.mem']
57+
if asm[0] == 'e':
58+
cmd += ['--mem-base=1024']
59+
if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm:
60+
cmd += ['--wasm-only']
61+
wasm = os.path.join(options.binaryen_test, wasm)
62+
print '..', asm, wasm
63+
64+
def do_asm2wasm_test():
65+
actual = run_command(cmd)
66+
67+
# verify output
68+
if not os.path.exists(wasm):
69+
fail_with_error('output .wast file %s does not exist' % wasm)
70+
expected = open(wasm, 'rb').read()
71+
if actual != expected:
72+
fail(actual, expected)
73+
74+
binary_format_check(wasm, verify_final_result=False)
75+
76+
# test both normally and with pass debug (so each inter-pass state
77+
# is validated)
78+
old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG')
79+
try:
80+
os.environ['BINARYEN_PASS_DEBUG'] = '1'
81+
print "With BINARYEN_PASS_DEBUG=1:"
82+
do_asm2wasm_test()
83+
del os.environ['BINARYEN_PASS_DEBUG']
84+
print "With BINARYEN_PASS_DEBUG disabled:"
85+
do_asm2wasm_test()
86+
finally:
87+
if old_pass_debug is not None:
88+
os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug
89+
else:
90+
if 'BINARYEN_PASS_DEBUG' in os.environ:
91+
del os.environ['BINARYEN_PASS_DEBUG']
92+
93+
# verify in wasm
94+
if options.interpreter:
95+
# remove imports, spec interpreter doesn't know what to do with them
96+
subprocess.check_call(WASM_OPT + ['--remove-imports', wasm],
97+
stdout=open('ztemp.wast', 'w'),
98+
stderr=subprocess.PIPE)
99+
proc = subprocess.Popen([options.interpreter, 'ztemp.wast'],
100+
stderr=subprocess.PIPE)
101+
out, err = proc.communicate()
102+
if proc.returncode != 0:
103+
try: # to parse the error
104+
reported = err.split(':')[1]
105+
start, end = reported.split('-')
106+
start_line, start_col = map(int, start.split('.'))
107+
lines = open('ztemp.wast').read().split('\n')
108+
print
109+
print '=' * 80
110+
print lines[start_line - 1]
111+
print (' ' * (start_col - 1)) + '^'
112+
print (' ' * (start_col - 2)) + '/_\\'
113+
print '=' * 80
114+
print err
115+
except Exception:
116+
# failed to pretty-print
117+
fail_with_error('wasm interpreter error: ' + err)
118+
fail_with_error('wasm interpreter error')
119+
120+
# verify debug info
121+
if 'debugInfo' in asm:
122+
jsmap = 'a.wasm.map'
123+
cmd += ['--source-map', jsmap,
124+
'--source-map-url', 'http://example.org/' + jsmap,
125+
'-o', 'a.wasm']
126+
run_command(cmd)
127+
if not os.path.isfile(jsmap):
128+
fail_with_error('Debug info map not created: %s' % jsmap)
129+
with open(wasm + '.map', 'rb') as expected:
130+
with open(jsmap, 'rb') as actual:
131+
fail_if_not_identical(actual.read(), expected.read())
132+
with open('a.wasm', 'rb') as binary:
133+
url_section_name = bytearray([16]) + bytearray('sourceMappingURL')
134+
url = 'http://example.org/' + jsmap
135+
assert len(url) < 256, 'name too long'
136+
url_section_contents = bytearray([len(url)]) + bytearray(url)
137+
print url_section_name
138+
binary_contents = bytearray(binary.read())
139+
if url_section_name not in binary_contents:
140+
fail_with_error('source map url section not found in binary')
141+
url_section_index = binary_contents.index(url_section_name)
142+
if url_section_contents not in binary_contents[url_section_index:]:
143+
fail_with_error('source map url not found in url section')
144+
145+
146+
def test_asm2wasm_binary():
147+
print '\n[ checking asm2wasm binary reading/writing... ]\n'
148+
149+
asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js')
150+
delete_from_orbit('a.wasm')
151+
delete_from_orbit('b.wast')
152+
run_command(ASM2WASM + [asmjs, '-o', 'a.wasm'])
153+
assert open('a.wasm', 'rb').read()[0] == '\0', 'we emit binary by default'
154+
run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S'])
155+
assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S'
156+
157+
158+
if __name__ == '__main__':
159+
test_asm2wasm()
160+
test_asm2wasm_binary()

0 commit comments

Comments
 (0)