summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xcheck.py788
-rw-r--r--src/ast/branch-utils.h22
-rw-r--r--src/ast_utils.h2
-rw-r--r--src/passes/OptimizeInstructions.cpp2
-rw-r--r--src/passes/Print.cpp5
-rw-r--r--src/passes/RemoveUnusedBrs.cpp4
-rw-r--r--src/passes/SimplifyLocals.cpp2
-rw-r--r--src/wasm-binary.h6
-rw-r--r--src/wasm-validator.h1
-rw-r--r--src/wasm.h4
-rw-r--r--src/wasm/wasm-binary.cpp22
-rw-r--r--src/wasm/wasm-s-parser.cpp9
-rw-r--r--src/wasm/wasm-validator.cpp20
-rw-r--r--src/wasm/wasm.cpp3
-rw-r--r--test/signext.wast12
-rw-r--r--test/signext.wast.from-wast33
-rw-r--r--test/signext.wast.fromBinary36
-rw-r--r--test/signext.wast.fromBinary.noDebugInfo36
18 files changed, 595 insertions, 412 deletions
diff --git a/check.py b/check.py
index 84086a9e9..e0d569373 100755
--- a/check.py
+++ b/check.py
@@ -39,136 +39,66 @@ if options.interpreter:
# tests
-print '[ checking --help is useful... ]\n'
-
-not_executable_suffix = ['.txt', '.js', '.ilk', '.pdb', '.dll']
-executables = sorted(filter(lambda x: not any(x.endswith(s) for s in
- not_executable_suffix) and os.path.isfile(x),
- os.listdir(options.binaryen_bin)))
-for e in executables:
- print '.. %s --help' % e
- out, err = subprocess.Popen([os.path.join(options.binaryen_bin, e), '--help'],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE).communicate()
- assert len(out) == 0, 'Expected no stdout, got:\n%s' % out
- assert e.replace('.exe', '') in err, 'Expected help to contain program name, got:\n%s' % err
- assert len(err.split('\n')) > 8, 'Expected some help, got:\n%s' % err
-
-print '\n[ checking wasm-opt -o notation... ]\n'
-
-wast = os.path.join(options.binaryen_test, 'hello_world.wast')
-delete_from_orbit('a.wast')
-cmd = WASM_OPT + [wast, '-o', 'a.wast', '-S']
-run_command(cmd)
-fail_if_not_identical(open('a.wast').read(), open(wast).read())
-
-print '\n[ checking wasm-opt binary reading/writing... ]\n'
-
-shutil.copyfile(os.path.join(options.binaryen_test, 'hello_world.wast'), 'a.wast')
-delete_from_orbit('a.wasm')
-delete_from_orbit('b.wast')
-run_command(WASM_OPT + ['a.wast', '-o', 'a.wasm'])
-assert open('a.wasm', 'rb').read()[0] == '\0', 'we emit binary by default'
-run_command(WASM_OPT + ['a.wasm', '-o', 'b.wast', '-S'])
-assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S'
-
-print '\n[ checking wasm-opt passes... ]\n'
-
-for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'passes'))):
- if t.endswith(('.wast', '.wasm')):
- print '..', t
- binary = '.wasm' in t
- passname = os.path.basename(t).replace('.wast', '').replace('.wasm', '')
- opts = [('--' + p if not p.startswith('O') else '-' + p) for p in passname.split('_')]
- t = os.path.join(options.binaryen_test, 'passes', t)
- actual = ''
- for module, asserts in split_wast(t):
- assert len(asserts) == 0
- with open('split.wast', 'w') as o: o.write(module)
- cmd = WASM_OPT + opts + ['split.wast', '--print']
- curr = run_command(cmd)
- actual += curr
- # also check debug mode output is valid
- debugged = run_command(cmd + ['--debug'], stderr=subprocess.PIPE)
- fail_if_not_contained(actual, debugged)
- # also check pass-debug mode
- old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG')
- try:
- os.environ['BINARYEN_PASS_DEBUG'] = '1'
- pass_debug = run_command(cmd)
- fail_if_not_identical(curr, pass_debug)
- finally:
- if old_pass_debug is not None:
- os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug
- else:
- if 'BINARYEN_PASS_DEBUG' in os.environ:
- del os.environ['BINARYEN_PASS_DEBUG']
-
- fail_if_not_identical(actual, open(os.path.join('test', 'passes', passname + ('.bin' if binary else '') + '.txt'), 'rb').read())
-
- if 'emit-js-wrapper' in t:
- with open('a.js') as actual:
- with open(t + '.js') as expected:
- fail_if_not_identical(actual.read(), expected.read())
- if 'emit-spec-wrapper' in t:
- with open('a.wat') as actual:
- with open(t + '.wat') as expected:
- fail_if_not_identical(actual.read(), expected.read())
-
-print '[ checking asm2wasm testcases... ]\n'
-
-for asm in tests:
- if asm.endswith('.asm.js'):
- for precise in [0, 1, 2]:
- for opts in [1, 0]:
- cmd = ASM2WASM + [os.path.join(options.binaryen_test, asm)]
- wasm = asm.replace('.asm.js', '.fromasm')
- if not precise:
- cmd += ['--emit-potential-traps', '--ignore-implicit-traps']
- wasm += '.imprecise'
- elif precise == 2:
- cmd += ['--emit-clamped-potential-traps']
- wasm += '.clamp'
- if not opts:
- wasm += '.no-opts'
- if precise:
- cmd += ['-O0'] # test that -O0 does nothing
- else:
- cmd += ['-O']
- if 'debugInfo' in asm:
- cmd += ['-g']
- if 'noffi' in asm:
- cmd += ['--no-legalize-javascript-ffi']
- if precise and opts:
- # test mem init importing
- open('a.mem', 'wb').write(asm)
- cmd += ['--mem-init=a.mem']
- if asm[0] == 'e':
- cmd += ['--mem-base=1024']
- if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm:
- cmd += ['--wasm-only']
- wasm = os.path.join(options.binaryen_test, wasm)
- print '..', asm, wasm
-
- def do_asm2wasm_test():
- actual = run_command(cmd)
-
- # verify output
- if not os.path.exists(wasm):
- fail_with_error('output .wast file %s does not exist' % wasm)
- expected = open(wasm, 'rb').read()
- if actual != expected:
- fail(actual, expected)
-
- binary_format_check(wasm, verify_final_result=False)
-
- # test both normally and with pass debug (so each inter-pass state is validated)
+def run_help_tests():
+ print '[ checking --help is useful... ]\n'
+
+ not_executable_suffix = ['.txt', '.js', '.ilk', '.pdb', '.dll']
+ executables = sorted(filter(lambda x: not any(x.endswith(s) for s in
+ not_executable_suffix) and os.path.isfile(x),
+ os.listdir(options.binaryen_bin)))
+ for e in executables:
+ print '.. %s --help' % e
+ out, err = subprocess.Popen([os.path.join(options.binaryen_bin, e), '--help'],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE).communicate()
+ assert len(out) == 0, 'Expected no stdout, got:\n%s' % out
+ assert e.replace('.exe', '') in err, 'Expected help to contain program name, got:\n%s' % err
+ assert len(err.split('\n')) > 8, 'Expected some help, got:\n%s' % err
+
+def run_wasm_opt_tests():
+ print '\n[ checking wasm-opt -o notation... ]\n'
+
+ wast = os.path.join(options.binaryen_test, 'hello_world.wast')
+ delete_from_orbit('a.wast')
+ cmd = WASM_OPT + [wast, '-o', 'a.wast', '-S']
+ run_command(cmd)
+ fail_if_not_identical(open('a.wast').read(), open(wast).read())
+
+ print '\n[ checking wasm-opt binary reading/writing... ]\n'
+
+ shutil.copyfile(os.path.join(options.binaryen_test, 'hello_world.wast'), 'a.wast')
+ delete_from_orbit('a.wasm')
+ delete_from_orbit('b.wast')
+ run_command(WASM_OPT + ['a.wast', '-o', 'a.wasm'])
+ assert open('a.wasm', 'rb').read()[0] == '\0', 'we emit binary by default'
+ run_command(WASM_OPT + ['a.wasm', '-o', 'b.wast', '-S'])
+ assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S'
+
+ print '\n[ checking wasm-opt passes... ]\n'
+
+ for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'passes'))):
+ if t.endswith(('.wast', '.wasm')):
+ print '..', t
+ binary = '.wasm' in t
+ passname = os.path.basename(t).replace('.wast', '').replace('.wasm', '')
+ opts = [('--' + p if not p.startswith('O') else '-' + p) for p in passname.split('_')]
+ t = os.path.join(options.binaryen_test, 'passes', t)
+ actual = ''
+ for module, asserts in split_wast(t):
+ assert len(asserts) == 0
+ with open('split.wast', 'w') as o: o.write(module)
+ cmd = WASM_OPT + opts + ['split.wast', '--print']
+ curr = run_command(cmd)
+ actual += curr
+ # also check debug mode output is valid
+ debugged = run_command(cmd + ['--debug'], stderr=subprocess.PIPE)
+ fail_if_not_contained(actual, debugged)
+ # also check pass-debug mode
old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG')
try:
os.environ['BINARYEN_PASS_DEBUG'] = '1'
- do_asm2wasm_test()
- del os.environ['BINARYEN_PASS_DEBUG']
- do_asm2wasm_test()
+ pass_debug = run_command(cmd)
+ fail_if_not_identical(curr, pass_debug)
finally:
if old_pass_debug is not None:
os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug
@@ -176,152 +106,228 @@ for asm in tests:
if 'BINARYEN_PASS_DEBUG' in os.environ:
del os.environ['BINARYEN_PASS_DEBUG']
- # verify in wasm
- if options.interpreter:
- # remove imports, spec interpreter doesn't know what to do with them
- subprocess.check_call(WASM_OPT + ['--remove-imports', wasm], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE)
- proc = subprocess.Popen([options.interpreter, 'ztemp.wast'], stderr=subprocess.PIPE)
- out, err = proc.communicate()
- if proc.returncode != 0:
- try: # to parse the error
- reported = err.split(':')[1]
- start, end = reported.split('-')
- start_line, start_col = map(int, start.split('.'))
- lines = open('ztemp.wast').read().split('\n')
- print
- print '='*80
- print lines[start_line-1]
- print (' '*(start_col-1)) + '^'
- print (' '*(start_col-2)) + '/_\\'
- print '='*80
- print err
- except Exception, e:
- fail_with_error('wasm interpreter error: ' + err) # failed to pretty-print
- fail_with_error('wasm interpreter error')
-
- # verify debug info
- if 'debugInfo' in asm:
- jsmap = 'a.wasm.map'
- cmd += ['--source-map', jsmap,
- '--source-map-url', 'http://example.org/' + jsmap,
- '-o', 'a.wasm']
- run_command(cmd)
- if not os.path.isfile(jsmap):
- fail_with_error('Debug info map not created: %s' % jsmap)
- with open(wasm + '.map', 'rb') as expected:
- with open(jsmap, 'rb') as actual:
- fail_if_not_identical(actual.read(), expected.read())
- with open('a.wasm', 'rb') as binary:
- url_section_name = bytearray([16]) + bytearray('sourceMappingURL')
- payload = 'http://example.org/' + jsmap
- assert len(payload) < 256, 'name too long'
- url_section_contents = bytearray([len(payload)]) + bytearray(payload)
- print url_section_name
- binary_contents = bytearray(binary.read())
- if url_section_name not in binary_contents:
- fail_with_error('source map url section not found in binary')
- if url_section_contents not in binary_contents[binary_contents.index(url_section_name):]:
- fail_with_error('source map url not found in url section')
-
-
-print '\n[ checking asm2wasm binary reading/writing... ]\n'
-
-asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js')
-delete_from_orbit('a.wasm')
-delete_from_orbit('b.wast')
-run_command(ASM2WASM + [asmjs, '-o', 'a.wasm'])
-assert open('a.wasm', 'rb').read()[0] == '\0', 'we emit binary by default'
-run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S'])
-assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S'
-
-print '\n[ checking wasm-opt parsing & printing... ]\n'
-
-for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'print'))):
- if t.endswith('.wast'):
- print '..', t
- wasm = os.path.basename(t).replace('.wast', '')
- cmd = WASM_OPT + [os.path.join(options.binaryen_test, 'print', t), '--print']
- print ' ', ' '.join(cmd)
- actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
- fail_if_not_identical(actual, open(os.path.join(options.binaryen_test, 'print', wasm + '.txt')).read())
- cmd = WASM_OPT + [os.path.join(options.binaryen_test, 'print', t), '--print-minified']
- print ' ', ' '.join(cmd)
- actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
- fail_if_not_identical(actual.strip(), open(os.path.join(options.binaryen_test, 'print', wasm + '.minified.txt')).read().strip())
-
-print '\n[ checking wasm-opt testcases... ]\n'
-
-for t in tests:
- if t.endswith('.wast') and not t.startswith('spec'):
- print '..', t
- t = os.path.join(options.binaryen_test, t)
- f = t + '.from-wast'
- cmd = WASM_OPT + [t, '--print']
- actual = run_command(cmd)
- actual = actual.replace('printing before:\n', '')
-
- expected = open(f, 'rb').read()
- if actual != expected:
- fail(actual, expected)
-
- binary_format_check(t, wasm_as_args=['-g']) # test with debuginfo
- binary_format_check(t, wasm_as_args=[], binary_suffix='.fromBinary.noDebugInfo') # test without debuginfo
-
- minify_check(t)
-
-print '\n[ checking wasm-dis on provided binaries... ]\n'
-
-for t in tests:
- if t.endswith('.wasm') and not t.startswith('spec'):
- print '..', t
- t = os.path.join(options.binaryen_test, t)
- cmd = WASM_DIS + [t]
- if os.path.isfile(t + '.map'): cmd += ['--source-map', t + '.map']
-
- actual = run_command(cmd)
-
- with open(t + '.fromBinary') as f:
- expected = f.read()
+ fail_if_not_identical(actual, open(os.path.join('test', 'passes', passname + ('.bin' if binary else '') + '.txt'), 'rb').read())
+
+ if 'emit-js-wrapper' in t:
+ with open('a.js') as actual:
+ with open(t + '.js') as expected:
+ fail_if_not_identical(actual.read(), expected.read())
+ if 'emit-spec-wrapper' in t:
+ with open('a.wat') as actual:
+ with open(t + '.wat') as expected:
+ fail_if_not_identical(actual.read(), expected.read())
+
+ print '\n[ checking wasm-opt parsing & printing... ]\n'
+
+ for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'print'))):
+ if t.endswith('.wast'):
+ print '..', t
+ wasm = os.path.basename(t).replace('.wast', '')
+ cmd = WASM_OPT + [os.path.join(options.binaryen_test, 'print', t), '--print']
+ print ' ', ' '.join(cmd)
+ actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+ fail_if_not_identical(actual, open(os.path.join(options.binaryen_test, 'print', wasm + '.txt')).read())
+ cmd = WASM_OPT + [os.path.join(options.binaryen_test, 'print', t), '--print-minified']
+ print ' ', ' '.join(cmd)
+ actual, err = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
+ fail_if_not_identical(actual.strip(), open(os.path.join(options.binaryen_test, 'print', wasm + '.minified.txt')).read().strip())
+
+ print '\n[ checking wasm-opt testcases... ]\n'
+
+ for t in tests:
+ if t.endswith('.wast') and not t.startswith('spec'):
+ print '..', t
+ t = os.path.join(options.binaryen_test, t)
+ f = t + '.from-wast'
+ cmd = WASM_OPT + [t, '--print']
+ actual = run_command(cmd)
+ actual = actual.replace('printing before:\n', '')
+
+ expected = open(f, 'rb').read()
if actual != expected:
fail(actual, expected)
-print '\n[ checking wasm-merge... ]\n'
-
-for t in os.listdir(os.path.join('test', 'merge')):
- if t.endswith(('.wast', '.wasm')):
- print '..', t
- t = os.path.join('test', 'merge', t)
- u = t + '.toMerge'
- for finalize in [0, 1]:
- for opt in [0, 1]:
- cmd = WASM_MERGE + [t, u, '-o', 'a.wast', '-S', '--verbose']
- if finalize: cmd += ['--finalize-memory-base=1024', '--finalize-table-base=8']
- if opt: cmd += ['-O']
- stdout = run_command(cmd)
- actual = open('a.wast').read()
- out = t + '.combined'
- if finalize: out += '.finalized'
- if opt: out += '.opt'
- with open(out) as f:
- fail_if_not_identical(f.read(), actual)
- with open(out + '.stdout') as f:
- fail_if_not_identical(f.read(), stdout)
-
-print '\n[ checking wasm-ctor-eval... ]\n'
-
-for t in os.listdir(os.path.join('test', 'ctor-eval')):
- if t.endswith(('.wast', '.wasm')):
- print '..', t
- t = os.path.join('test', 'ctor-eval', t)
- ctors = open(t + '.ctors').read().strip()
- cmd = WASM_CTOR_EVAL + [t, '-o', 'a.wast', '-S', '--ctors', ctors]
- stdout = run_command(cmd)
- actual = open('a.wast').read()
- out = t + '.out'
- with open(out) as f:
- fail_if_not_identical(f.read(), actual)
+ binary_format_check(t, wasm_as_args=['-g']) # test with debuginfo
+ binary_format_check(t, wasm_as_args=[], binary_suffix='.fromBinary.noDebugInfo') # test without debuginfo
+
+ minify_check(t)
+
+def run_asm2wasm_tests():
+ print '[ checking asm2wasm testcases... ]\n'
+
+ for asm in tests:
+ if asm.endswith('.asm.js'):
+ for precise in [0, 1, 2]:
+ for opts in [1, 0]:
+ cmd = ASM2WASM + [os.path.join(options.binaryen_test, asm)]
+ wasm = asm.replace('.asm.js', '.fromasm')
+ if not precise:
+ cmd += ['--emit-potential-traps', '--ignore-implicit-traps']
+ wasm += '.imprecise'
+ elif precise == 2:
+ cmd += ['--emit-clamped-potential-traps']
+ wasm += '.clamp'
+ if not opts:
+ wasm += '.no-opts'
+ if precise:
+ cmd += ['-O0'] # test that -O0 does nothing
+ else:
+ cmd += ['-O']
+ if 'debugInfo' in asm:
+ cmd += ['-g']
+ if 'noffi' in asm:
+ cmd += ['--no-legalize-javascript-ffi']
+ if precise and opts:
+ # test mem init importing
+ open('a.mem', 'wb').write(asm)
+ cmd += ['--mem-init=a.mem']
+ if asm[0] == 'e':
+ cmd += ['--mem-base=1024']
+ if 'i64' in asm or 'wasm-only' in asm or 'noffi' in asm:
+ cmd += ['--wasm-only']
+ wasm = os.path.join(options.binaryen_test, wasm)
+ print '..', asm, wasm
+
+ def do_asm2wasm_test():
+ actual = run_command(cmd)
+
+ # verify output
+ if not os.path.exists(wasm):
+ fail_with_error('output .wast file %s does not exist' % wasm)
+ expected = open(wasm, 'rb').read()
+ if actual != expected:
+ fail(actual, expected)
+
+ binary_format_check(wasm, verify_final_result=False)
+
+ # test both normally and with pass debug (so each inter-pass state is validated)
+ old_pass_debug = os.environ.get('BINARYEN_PASS_DEBUG')
+ try:
+ os.environ['BINARYEN_PASS_DEBUG'] = '1'
+ do_asm2wasm_test()
+ del os.environ['BINARYEN_PASS_DEBUG']
+ do_asm2wasm_test()
+ finally:
+ if old_pass_debug is not None:
+ os.environ['BINARYEN_PASS_DEBUG'] = old_pass_debug
+ else:
+ if 'BINARYEN_PASS_DEBUG' in os.environ:
+ del os.environ['BINARYEN_PASS_DEBUG']
+
+ # verify in wasm
+ if options.interpreter:
+ # remove imports, spec interpreter doesn't know what to do with them
+ subprocess.check_call(WASM_OPT + ['--remove-imports', wasm], stdout=open('ztemp.wast', 'w'), stderr=subprocess.PIPE)
+ proc = subprocess.Popen([options.interpreter, 'ztemp.wast'], stderr=subprocess.PIPE)
+ out, err = proc.communicate()
+ if proc.returncode != 0:
+ try: # to parse the error
+ reported = err.split(':')[1]
+ start, end = reported.split('-')
+ start_line, start_col = map(int, start.split('.'))
+ lines = open('ztemp.wast').read().split('\n')
+ print
+ print '='*80
+ print lines[start_line-1]
+ print (' '*(start_col-1)) + '^'
+ print (' '*(start_col-2)) + '/_\\'
+ print '='*80
+ print err
+ except Exception, e:
+ fail_with_error('wasm interpreter error: ' + err) # failed to pretty-print
+ fail_with_error('wasm interpreter error')
+
+ # verify debug info
+ if 'debugInfo' in asm:
+ jsmap = 'a.wasm.map'
+ cmd += ['--source-map', jsmap,
+ '--source-map-url', 'http://example.org/' + jsmap,
+ '-o', 'a.wasm']
+ run_command(cmd)
+ if not os.path.isfile(jsmap):
+ fail_with_error('Debug info map not created: %s' % jsmap)
+ with open(wasm + '.map', 'rb') as expected:
+ with open(jsmap, 'rb') as actual:
+ fail_if_not_identical(actual.read(), expected.read())
+ with open('a.wasm', 'rb') as binary:
+ url_section_name = bytearray([16]) + bytearray('sourceMappingURL')
+ payload = 'http://example.org/' + jsmap
+ assert len(payload) < 256, 'name too long'
+ url_section_contents = bytearray([len(payload)]) + bytearray(payload)
+ print url_section_name
+ binary_contents = bytearray(binary.read())
+ if url_section_name not in binary_contents:
+ fail_with_error('source map url section not found in binary')
+ if url_section_contents not in binary_contents[binary_contents.index(url_section_name):]:
+ fail_with_error('source map url not found in url section')
+
+
+ print '\n[ checking asm2wasm binary reading/writing... ]\n'
+
+ asmjs = os.path.join(options.binaryen_test, 'hello_world.asm.js')
+ delete_from_orbit('a.wasm')
+ delete_from_orbit('b.wast')
+ run_command(ASM2WASM + [asmjs, '-o', 'a.wasm'])
+ assert open('a.wasm', 'rb').read()[0] == '\0', 'we emit binary by default'
+ run_command(ASM2WASM + [asmjs, '-o', 'b.wast', '-S'])
+ assert open('b.wast', 'rb').read()[0] != '\0', 'we emit text with -S'
+
+def run_wasm_dis_tests():
+ print '\n[ checking wasm-dis on provided binaries... ]\n'
+
+ for t in tests:
+ if t.endswith('.wasm') and not t.startswith('spec'):
+ print '..', t
+ t = os.path.join(options.binaryen_test, t)
+ cmd = WASM_DIS + [t]
+ if os.path.isfile(t + '.map'): cmd += ['--source-map', t + '.map']
-if has_shell_timeout():
+ actual = run_command(cmd)
+
+ with open(t + '.fromBinary') as f:
+ expected = f.read()
+ if actual != expected:
+ fail(actual, expected)
+
+def run_wasm_merge_tests():
+ print '\n[ checking wasm-merge... ]\n'
+
+ for t in os.listdir(os.path.join('test', 'merge')):
+ if t.endswith(('.wast', '.wasm')):
+ print '..', t
+ t = os.path.join('test', 'merge', t)
+ u = t + '.toMerge'
+ for finalize in [0, 1]:
+ for opt in [0, 1]:
+ cmd = WASM_MERGE + [t, u, '-o', 'a.wast', '-S', '--verbose']
+ if finalize: cmd += ['--finalize-memory-base=1024', '--finalize-table-base=8']
+ if opt: cmd += ['-O']
+ stdout = run_command(cmd)
+ actual = open('a.wast').read()
+ out = t + '.combined'
+ if finalize: out += '.finalized'
+ if opt: out += '.opt'
+ with open(out) as f:
+ fail_if_not_identical(f.read(), actual)
+ with open(out + '.stdout') as f:
+ fail_if_not_identical(f.read(), stdout)
+
+def run_ctor_eval_tests():
+ print '\n[ checking wasm-ctor-eval... ]\n'
+
+ for t in os.listdir(os.path.join('test', 'ctor-eval')):
+ if t.endswith(('.wast', '.wasm')):
+ print '..', t
+ t = os.path.join('test', 'ctor-eval', t)
+ ctors = open(t + '.ctors').read().strip()
+ cmd = WASM_CTOR_EVAL + [t, '-o', 'a.wast', '-S', '--ctors', ctors]
+ stdout = run_command(cmd)
+ actual = open('a.wast').read()
+ out = t + '.out'
+ with open(out) as f:
+ fail_if_not_identical(f.read(), actual)
+
+def run_wasm_reduce_tests():
print '\n[ checking wasm-reduce ]\n'
for t in os.listdir(os.path.join('test', 'reduce')):
@@ -330,108 +336,109 @@ if has_shell_timeout():
t = os.path.join('test', 'reduce', t)
# convert to wasm
run_command(WASM_AS + [t, '-o', 'a.wasm'])
- print run_command(WASM_REDUCE + ['a.wasm', '--command=bin/wasm-opt b.wasm --fuzz-exec', '-t', 'b.wasm', '-w', 'c.wasm'])
+ print run_command(WASM_REDUCE + ['a.wasm', '--command=%s b.wasm --fuzz-exec' % WASM_OPT[0], '-t', 'b.wasm', '-w', 'c.wasm'])
expected = t + '.txt'
run_command(WASM_DIS + ['c.wasm', '-o', 'a.wast'])
with open('a.wast') as seen:
with open(expected) as correct:
fail_if_not_identical(seen.read(), correct.read())
-print '\n[ checking wasm-shell spec testcases... ]\n'
+def run_spec_tests():
+ print '\n[ checking wasm-shell spec testcases... ]\n'
-if len(requested) == 0:
- BLACKLIST = ['memory.wast', 'binary.wast'] # FIXME we support old and new memory formats, for now, until 0xc, and so can't pass this old-style test.
- # FIXME to update the spec to 0xd, we need to implement (register "name") for import.wast
- spec_tests = [os.path.join('spec', t) for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'spec'))) if t not in BLACKLIST]
-else:
- spec_tests = requested[:]
+ if len(requested) == 0:
+ BLACKLIST = ['memory.wast', 'binary.wast'] # FIXME we support old and new memory formats, for now, until 0xc, and so can't pass this old-style test.
+ # FIXME to update the spec to 0xd, we need to implement (register "name") for import.wast
+ spec_tests = [os.path.join('spec', t) for t in sorted(os.listdir(os.path.join(options.binaryen_test, 'spec'))) if t not in BLACKLIST]
+ else:
+ spec_tests = requested[:]
-for t in spec_tests:
- if t.startswith('spec') and t.endswith('.wast'):
- print '..', t
- wast = os.path.join(options.binaryen_test, t)
+ for t in spec_tests:
+ if t.startswith('spec') and t.endswith('.wast'):
+ print '..', t
+ wast = os.path.join(options.binaryen_test, t)
- # skip checks for some tests
- if os.path.basename(wast) in ['linking.wast', 'nop.wast', 'stack.wast', 'typecheck.wast', 'unwind.wast']: # FIXME
- continue
+ # skip checks for some tests
+ if os.path.basename(wast) in ['linking.wast', 'nop.wast', 'stack.wast', 'typecheck.wast', 'unwind.wast']: # FIXME
+ continue
- def run_spec_test(wast):
- cmd = WASM_SHELL + [wast]
- # we must skip the stack machine portions of spec tests or apply other extra args
- extra = {
- }
- cmd = cmd + (extra.get(os.path.basename(wast)) or [])
- return run_command(cmd, stderr=subprocess.PIPE)
-
- def run_opt_test(wast):
- # check optimization validation
- cmd = WASM_OPT + [wast, '-O']
- run_command(cmd)
-
- def check_expected(actual, expected):
- if expected and os.path.exists(expected):
- expected = open(expected).read()
- # fix it up, our pretty (i32.const 83) must become compared to a homely 83 : i32
- def fix(x):
- x = x.strip()
- if not x: return x
- v, t = x.split(' : ')
- if v.endswith('.'): v = v[:-1] # remove trailing '.'
- return '(' + t + '.const ' + v + ')'
- expected = '\n'.join(map(fix, expected.split('\n')))
- print ' (using expected output)'
- actual = actual.strip()
- expected = expected.strip()
- if actual != expected:
- fail(actual, expected)
+ def run_spec_test(wast):
+ cmd = WASM_SHELL + [wast]
+ # we must skip the stack machine portions of spec tests or apply other extra args
+ extra = {
+ }
+ cmd = cmd + (extra.get(os.path.basename(wast)) or [])
+ return run_command(cmd, stderr=subprocess.PIPE)
+
+ def run_opt_test(wast):
+ # check optimization validation
+ cmd = WASM_OPT + [wast, '-O']
+ run_command(cmd)
+
+ def check_expected(actual, expected):
+ if expected and os.path.exists(expected):
+ expected = open(expected).read()
+ # fix it up, our pretty (i32.const 83) must become compared to a homely 83 : i32
+ def fix(x):
+ x = x.strip()
+ if not x: return x
+ v, t = x.split(' : ')
+ if v.endswith('.'): v = v[:-1] # remove trailing '.'
+ return '(' + t + '.const ' + v + ')'
+ expected = '\n'.join(map(fix, expected.split('\n')))
+ print ' (using expected output)'
+ actual = actual.strip()
+ expected = expected.strip()
+ if actual != expected:
+ fail(actual, expected)
- expected = os.path.join(options.binaryen_test, 'spec', 'expected-output', os.path.basename(wast) + '.log')
+ expected = os.path.join(options.binaryen_test, 'spec', 'expected-output', os.path.basename(wast) + '.log')
- # some spec tests should fail (actual process failure, not just assert_invalid)
- try:
- actual = run_spec_test(wast)
- except Exception, e:
- if ('wasm-validator error' in str(e) or 'parse exception' in str(e)) and '.fail.' in t:
- print '<< test failed as expected >>'
- continue # don't try all the binary format stuff TODO
- else:
- fail_with_error(str(e))
+ # some spec tests should fail (actual process failure, not just assert_invalid)
+ try:
+ actual = run_spec_test(wast)
+ except Exception, e:
+ if ('wasm-validator error' in str(e) or 'parse exception' in str(e)) and '.fail.' in t:
+ print '<< test failed as expected >>'
+ continue # don't try all the binary format stuff TODO
+ else:
+ fail_with_error(str(e))
- check_expected(actual, expected)
+ check_expected(actual, expected)
- # skip binary checks for tests that reuse previous modules by name, as that's a wast-only feature
- if os.path.basename(wast) in ['exports.wast']: # FIXME
- continue
+ # skip binary checks for tests that reuse previous modules by name, as that's a wast-only feature
+ if os.path.basename(wast) in ['exports.wast']: # FIXME
+ continue
- # we must ignore some binary format splits
- splits_to_skip = {
- 'func.wast': [2],
- 'return.wast': [2]
- }
+ # we must ignore some binary format splits
+ splits_to_skip = {
+ 'func.wast': [2],
+ 'return.wast': [2]
+ }
- # check binary format. here we can verify execution of the final result, no need for an output verification
- split_num = 0
- if os.path.basename(wast) not in []: # avoid some tests with things still being sorted out in the spec
- actual = ''
- for module, asserts in split_wast(wast):
- skip = splits_to_skip.get(os.path.basename(wast)) or []
- if split_num in skip:
- print ' skipping split module', split_num - 1
+ # check binary format. here we can verify execution of the final result, no need for an output verification
+ split_num = 0
+ if os.path.basename(wast) not in []: # avoid some tests with things still being sorted out in the spec
+ actual = ''
+ for module, asserts in split_wast(wast):
+ skip = splits_to_skip.get(os.path.basename(wast)) or []
+ if split_num in skip:
+ print ' skipping split module', split_num - 1
+ split_num += 1
+ continue
+ print ' testing split module', split_num
split_num += 1
- continue
- print ' testing split module', split_num
- split_num += 1
- with open('split.wast', 'w') as o: o.write(module + '\n' + '\n'.join(asserts))
- run_spec_test('split.wast') # before binary stuff - just check it's still ok split out
- run_opt_test('split.wast') # also that our optimizer doesn't break on it
- result_wast = binary_format_check('split.wast', verify_final_result=False)
- # add the asserts, and verify that the test still passes
- open(result_wast, 'a').write('\n' + '\n'.join(asserts))
- actual += run_spec_test(result_wast)
- # compare all the outputs to the expected output
- check_expected(actual, os.path.join(options.binaryen_test, 'spec', 'expected-output', os.path.basename(wast) + '.log'))
-
-if MOZJS:
+ with open('split.wast', 'w') as o: o.write(module + '\n' + '\n'.join(asserts))
+ run_spec_test('split.wast') # before binary stuff - just check it's still ok split out
+ run_opt_test('split.wast') # also that our optimizer doesn't break on it
+ result_wast = binary_format_check('split.wast', verify_final_result=False)
+ # add the asserts, and verify that the test still passes
+ open(result_wast, 'a').write('\n' + '\n'.join(asserts))
+ actual += run_spec_test(result_wast)
+ # compare all the outputs to the expected output
+ check_expected(actual, os.path.join(options.binaryen_test, 'spec', 'expected-output', os.path.basename(wast) + '.log'))
+
+def run_binaryen_js_tests():
print '\n[ checking binaryen.js testcases... ]\n'
for s in sorted(os.listdir(os.path.join(options.binaryen_test, 'binaryen.js'))):
@@ -447,25 +454,21 @@ if MOZJS:
if expected not in out:
fail(out, expected)
-s2wasm.test_s2wasm()
-s2wasm.test_linker()
-wasm2asm.test_wasm2asm()
-
-print '\n[ running validation tests... ]\n'
-# Ensure the tests validate by default
-cmd = WASM_AS + [os.path.join(options.binaryen_test, 'validator', 'invalid_export.wast')]
-run_command(cmd)
-cmd = WASM_AS + [os.path.join(options.binaryen_test, 'validator', 'invalid_import.wast')]
-run_command(cmd)
-cmd = WASM_AS + ['--validate=web', os.path.join(options.binaryen_test, 'validator', 'invalid_export.wast')]
-run_command(cmd, expected_status=1)
-cmd = WASM_AS + ['--validate=web', os.path.join(options.binaryen_test, 'validator', 'invalid_import.wast')]
-run_command(cmd, expected_status=1)
-cmd = WASM_AS + ['--validate=none', os.path.join(options.binaryen_test, 'validator', 'invalid_return.wast')]
-run_command(cmd)
-
-if options.torture and options.test_waterfall:
-
+def run_validator_tests():
+ print '\n[ running validation tests... ]\n'
+ # Ensure the tests validate by default
+ cmd = WASM_AS + [os.path.join(options.binaryen_test, 'validator', 'invalid_export.wast')]
+ run_command(cmd)
+ cmd = WASM_AS + [os.path.join(options.binaryen_test, 'validator', 'invalid_import.wast')]
+ run_command(cmd)
+ cmd = WASM_AS + ['--validate=web', os.path.join(options.binaryen_test, 'validator', 'invalid_export.wast')]
+ run_command(cmd, expected_status=1)
+ cmd = WASM_AS + ['--validate=web', os.path.join(options.binaryen_test, 'validator', 'invalid_import.wast')]
+ run_command(cmd, expected_status=1)
+ cmd = WASM_AS + ['--validate=none', os.path.join(options.binaryen_test, 'validator', 'invalid_return.wast')]
+ run_command(cmd)
+
+def run_torture_tests():
print '\n[ checking torture testcases... ]\n'
unexpected_result_count = 0
@@ -494,8 +497,7 @@ if options.torture and options.test_waterfall:
if unexpected_result_count:
fail('%s failures' % unexpected_result_count, '0 failures')
-if has_vanilla_emcc and has_vanilla_llvm and 0:
-
+def run_vanilla_tests():
print '\n[ checking emcc WASM_BACKEND testcases...]\n'
try:
@@ -535,9 +537,7 @@ if has_vanilla_emcc and has_vanilla_llvm and 0:
else:
del os.environ['EMCC_WASM_BACKEND']
-print '\n[ checking example testcases... ]\n'
-
-if options.run_gcc_tests:
+def run_gcc_torture_tests():
print '\n[ checking native gcc testcases...]\n'
if not NATIVECC or not NATIVEXX:
fail_with_error('Native compiler (e.g. gcc/g++) was not found in PATH!')
@@ -590,8 +590,7 @@ if options.run_gcc_tests:
if actual != expected:
fail(actual, expected)
-if EMCC:
-
+def run_emscripten_tests():
if MOZJS and 0:
print '\n[ checking native wasm support ]\n'
@@ -714,6 +713,35 @@ if EMCC:
shutil.copyfile(recreated, 'a.wasm.wast')
execute()
+
+# Run all the tests
+run_help_tests()
+run_wasm_opt_tests()
+run_asm2wasm_tests()
+run_wasm_dis_tests()
+run_wasm_merge_tests()
+run_ctor_eval_tests()
+if has_shell_timeout():
+ run_wasm_reduce_tests()
+
+run_spec_tests()
+if MOZJS:
+ run_binaryen_js_tests()
+s2wasm.test_s2wasm()
+s2wasm.test_linker()
+wasm2asm.test_wasm2asm()
+run_validator_tests()
+if options.torture and options.test_waterfall:
+ run_torture_tests()
+if has_vanilla_emcc and has_vanilla_llvm and 0:
+ run_vanilla_tests()
+print '\n[ checking example testcases... ]\n'
+if options.run_gcc_tests:
+ run_gcc_torture_tests()
+if EMCC:
+ run_emscripten_tests()
+
+# Check/display the results
if num_failures == 0:
print '\n[ success! ]'
diff --git a/src/ast/branch-utils.h b/src/ast/branch-utils.h
index 05ead8571..d574945ef 100644
--- a/src/ast/branch-utils.h
+++ b/src/ast/branch-utils.h
@@ -24,24 +24,23 @@ namespace wasm {
namespace BranchUtils {
-// branches not actually taken (e.g. (br $out (unreachable)))
-// are trivially ignored in our type system
+// Some branches are obviously not actually reachable (e.g. (br $out (unreachable)))
-inline bool isBranchTaken(Break* br) {
+inline bool isBranchReachable(Break* br) {
return !(br->value && br->value->type == unreachable) &&
!(br->condition && br->condition->type == unreachable);
}
-inline bool isBranchTaken(Switch* sw) {
+inline bool isBranchReachable(Switch* sw) {
return !(sw->value && sw->value->type == unreachable) &&
sw->condition->type != unreachable;
}
-inline bool isBranchTaken(Expression* expr) {
+inline bool isBranchReachable(Expression* expr) {
if (auto* br = expr->dynCast<Break>()) {
- return isBranchTaken(br);
+ return isBranchReachable(br);
} else if (auto* sw = expr->dynCast<Switch>()) {
- return isBranchTaken(sw);
+ return isBranchReachable(sw);
}
WASM_UNREACHABLE();
}
@@ -103,8 +102,9 @@ inline std::set<Name> getBranchTargets(Expression* ast) {
// Finds if there are branches targeting a name. Note that since names are
// unique in our IR, we just need to look for the name, and do not need
// to analyze scoping.
-// By default we consider untaken branches (so any named use). You can unset named to
-// avoid that (and only note branches that are not obviously unreachable)
+// By default we consider all branches, so any place there is a branch that
+// names the target. You can unset 'named' to only note branches that appear
+// reachable (i.e., are not obviously unreachable).
struct BranchSeeker : public PostWalker<BranchSeeker> {
Name target;
bool named = true;
@@ -144,7 +144,7 @@ struct BranchSeeker : public PostWalker<BranchSeeker> {
if (curr->default_ == target) noteFound(curr->value);
}
- static bool hasTaken(Expression* tree, Name target) {
+ static bool hasReachable(Expression* tree, Name target) {
if (!target.is()) return false;
BranchSeeker seeker(target);
seeker.named = false;
@@ -152,7 +152,7 @@ struct BranchSeeker : public PostWalker<BranchSeeker> {
return seeker.found > 0;
}
- static Index countTaken(Expression* tree, Name target) {
+ static Index countReachable(Expression* tree, Name target) {
if (!target.is()) return 0;
BranchSeeker seeker(target);
seeker.named = false;
diff --git a/src/ast_utils.h b/src/ast_utils.h
index 852cf89a3..95f725636 100644
--- a/src/ast_utils.h
+++ b/src/ast_utils.h
@@ -62,7 +62,7 @@ struct ExpressionAnalyzer {
if (auto* br = curr->dynCast<Break>()) {
if (!br->condition) return true;
} else if (auto* block = curr->dynCast<Block>()) {
- if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::hasTaken(block, block->name)) return true;
+ if (block->list.size() > 0 && obviouslyDoesNotFlowOut(block->list.back()) && !BranchUtils::BranchSeeker::hasReachable(block, block->name)) return true;
}
return false;
}
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 0b47d3398..ad667f888 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -31,6 +31,8 @@
#include <ast/properties.h>
#include <ast/literal-utils.h>
+// TODO: Use the new sign-extension opcodes where appropriate. This needs to be conditionalized on the availability of atomics.
+
namespace wasm {
Name I32_EXPR = "i32.expr",
diff --git a/src/passes/Print.cpp b/src/passes/Print.cpp
index 3c538a40e..04b649ff6 100644
--- a/src/passes/Print.cpp
+++ b/src/passes/Print.cpp
@@ -474,6 +474,11 @@ struct PrintSExpression : public Visitor<PrintSExpression> {
case DemoteFloat64: o << "f32.demote/f64"; break;
case ReinterpretInt32: o << "f32.reinterpret/i32"; break;
case ReinterpretInt64: o << "f64.reinterpret/i64"; break;
+ case ExtendS8Int32: o << "i32.extend8_s"; break;
+ case ExtendS16Int32: o << "i32.extend16_s"; break;
+ case ExtendS8Int64: o << "i64.extend8_s"; break;
+ case ExtendS16Int64: o << "i64.extend16_s"; break;
+ case ExtendS32Int64: o << "i64.extend32_s"; break;
default: abort();
}
incIndent();
diff --git a/src/passes/RemoveUnusedBrs.cpp b/src/passes/RemoveUnusedBrs.cpp
index ec7809f48..7e122b806 100644
--- a/src/passes/RemoveUnusedBrs.cpp
+++ b/src/passes/RemoveUnusedBrs.cpp
@@ -31,7 +31,7 @@ namespace wasm {
// condition and possible value, and the possible value must
// not have side effects (as they would run unconditionally)
static bool canTurnIfIntoBrIf(Expression* ifCondition, Expression* brValue, PassOptions& options) {
- // if the if isn't even taken, this is all dead code anyhow
+ // if the if isn't even reached, this is all dead code anyhow
if (ifCondition->type == unreachable) return false;
if (!brValue) return true;
EffectAnalyzer value(options, brValue);
@@ -512,7 +512,7 @@ struct RemoveUnusedBrs : public WalkerPass<PostWalker<RemoveUnusedBrs>> {
if (curr->name.is()) {
auto* br = list[0]->dynCast<Break>();
// we seek a regular br_if; if the type is unreachable that means it is not
- // actually taken, so ignore
+ // actually reached, so ignore
if (br && br->condition && br->name == curr->name && br->type != unreachable) {
assert(!br->value); // can't, it would be dropped or last in the block
if (BranchUtils::BranchSeeker::countNamed(curr, curr->name) == 1) {
diff --git a/src/passes/SimplifyLocals.cpp b/src/passes/SimplifyLocals.cpp
index 919784cdf..618f8dead 100644
--- a/src/passes/SimplifyLocals.cpp
+++ b/src/passes/SimplifyLocals.cpp
@@ -348,7 +348,7 @@ struct SimplifyLocals : public WalkerPass<LinearExecutionWalker<SimplifyLocals>>
auto* brp = breaks[j].brp;
auto* br = (*brp)->cast<Break>();
assert(!br->value);
- // if the break is conditional, then we must set the value here - if the break is not taken, we must still have the new value in the local
+ // if the break is conditional, then we must set the value here - if the break is not reached, we must still have the new value in the local
auto* set = (*breakSetLocalPointer)->cast<SetLocal>();
if (br->condition) {
br->value = set;
diff --git a/src/wasm-binary.h b/src/wasm-binary.h
index 6df49b162..256cea75c 100644
--- a/src/wasm-binary.h
+++ b/src/wasm-binary.h
@@ -531,6 +531,12 @@ enum ASTNodes {
F32ReinterpretI32 = 0xbe,
F64ReinterpretI64 = 0xbf,
+ I32ExtendS8 = 0xc0,
+ I32ExtendS16 = 0xc1,
+ I64ExtendS8 = 0xc2,
+ I64ExtendS16 = 0xc3,
+ I64ExtendS32 = 0xc4,
+
AtomicPrefix = 0xfe
};
diff --git a/src/wasm-validator.h b/src/wasm-validator.h
index 35304d1c9..0851d33e8 100644
--- a/src/wasm-validator.h
+++ b/src/wasm-validator.h
@@ -78,7 +78,6 @@ struct WasmValidator : public PostWalker<WasmValidator> {
std::map<Name, Expression*> breakTargets;
std::map<Expression*, BreakInfo> breakInfos;
- std::set<Name> namedBreakTargets; // even breaks not taken must not be named if they go to a place that does not exist
WasmType returnType = unreachable; // type used in returns
diff --git a/src/wasm.h b/src/wasm.h
index 0cd5b05e8..d2bea45a1 100644
--- a/src/wasm.h
+++ b/src/wasm.h
@@ -98,6 +98,10 @@ enum UnaryOp {
PromoteFloat32, // f32 to f64
DemoteFloat64, // f64 to f32
ReinterpretInt32, ReinterpretInt64, // reinterpret bits to float
+ // The following sign-extention operators go along with wasm atomics support.
+ // Extend signed subword-sized integer. This differs from e.g. ExtendSInt32
+ // because the input integer is in an i64 value insetad of an i32 value.
+ ExtendS8Int32, ExtendS16Int32, ExtendS8Int64, ExtendS16Int64, ExtendS32Int64,
};
enum BinaryOp {
diff --git a/src/wasm/wasm-binary.cpp b/src/wasm/wasm-binary.cpp
index f0787600e..68fb293e8 100644
--- a/src/wasm/wasm-binary.cpp
+++ b/src/wasm/wasm-binary.cpp
@@ -670,7 +670,7 @@ void WasmBinaryWriter::visitBreak(Break *curr) {
// then either the condition or the value is unreachable, which is
// extremely rare, and may require us to make the stack polymorphic
// (if the block we branch to has a value, we may lack one as we
- // are not a taken branch; the wasm spec on the other hand does
+ // are not a reachable branch; the wasm spec on the other hand does
// presume the br_if emits a value of the right type, even if it
// popped unreachable)
o << int8_t(BinaryConsts::Unreachable);
@@ -683,11 +683,10 @@ void WasmBinaryWriter::visitSwitch(Switch *curr) {
recurse(curr->value);
}
recurse(curr->condition);
- if (!BranchUtils::isBranchTaken(curr)) {
- // if the branch is not taken, then it's dangerous to emit it, as
- // wasm type checking rules are stricter than ours - we tolerate
- // an untaken branch to a target with a different value, but not
- // wasm. so just don't emit it
+ if (!BranchUtils::isBranchReachable(curr)) {
+ // if the branch is not reachable, then it's dangerous to emit it, as
+ // wasm type checking rules are different, especially in unreachable
+ // code. so just don't emit that unreachable code.
o << int8_t(BinaryConsts::Unreachable);
return;
}
@@ -1049,6 +1048,11 @@ void WasmBinaryWriter::visitUnary(Unary *curr) {
case ReinterpretFloat64: o << int8_t(BinaryConsts::I64ReinterpretF64); break;
case ReinterpretInt32: o << int8_t(BinaryConsts::F32ReinterpretI32); break;
case ReinterpretInt64: o << int8_t(BinaryConsts::F64ReinterpretI64); break;
+ case ExtendS8Int32: o << int8_t(BinaryConsts::I32ExtendS8); break;
+ case ExtendS16Int32: o << int8_t(BinaryConsts::I32ExtendS16); break;
+ case ExtendS8Int64: o << int8_t(BinaryConsts::I64ExtendS8); break;
+ case ExtendS16Int64: o << int8_t(BinaryConsts::I64ExtendS16); break;
+ case ExtendS32Int64: o << int8_t(BinaryConsts::I64ExtendS32); break;
default: abort();
}
if (curr->type == unreachable) {
@@ -2679,6 +2683,12 @@ bool WasmBinaryBuilder::maybeVisitUnary(Expression*& out, uint8_t code) {
case BinaryConsts::F32ReinterpretI32: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt32; curr->type = f32; break;
case BinaryConsts::F64ReinterpretI64: curr = allocator.alloc<Unary>(); curr->op = ReinterpretInt64; curr->type = f64; break;
+ case BinaryConsts::I32ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int32; curr->type = i32; break;
+ case BinaryConsts::I32ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int32; curr->type = i32; break;
+ case BinaryConsts::I64ExtendS8: curr = allocator.alloc<Unary>(); curr->op = ExtendS8Int64; curr->type = i64; break;
+ case BinaryConsts::I64ExtendS16: curr = allocator.alloc<Unary>(); curr->op = ExtendS16Int64; curr->type = i64; break;
+ case BinaryConsts::I64ExtendS32: curr = allocator.alloc<Unary>(); curr->op = ExtendS32Int64; curr->type = i64; break;
+
default: return false;
}
if (debug) std::cerr << "zz node: Unary" << std::endl;
diff --git a/src/wasm/wasm-s-parser.cpp b/src/wasm/wasm-s-parser.cpp
index fd7c7e7ae..a95b551ea 100644
--- a/src/wasm/wasm-s-parser.cpp
+++ b/src/wasm/wasm-s-parser.cpp
@@ -699,7 +699,12 @@ Expression* SExpressionWasmBuilder::makeExpression(Element& s) {
if (op[2] == 0) return makeBinary(s, BINARY_INT_OR_FLOAT(Eq), type);
if (op[2] == 'z') return makeUnary(s, type == i32 ? UnaryOp::EqZInt32 : UnaryOp::EqZInt64, type);
}
- if (op[1] == 'x') return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type);
+ if (op[1] == 'x') {
+ if (op[6] == '8') return makeUnary(s, type == i32 ? UnaryOp::ExtendS8Int32 : UnaryOp::ExtendS8Int64, type);
+ if (op[6] == '1') return makeUnary(s, type == i32 ? UnaryOp::ExtendS16Int32 : UnaryOp::ExtendS16Int64, type);
+ if (op[6] == '3') return makeUnary(s, UnaryOp::ExtendS32Int64, type);
+ return makeUnary(s, op[7] == 'u' ? UnaryOp::ExtendUInt32 : UnaryOp::ExtendSInt32, type);
+ }
abort_on(op);
}
case 'f': {
@@ -920,6 +925,8 @@ Expression* SExpressionWasmBuilder::makeUnary(Element& s, UnaryOp op, WasmType t
break;
}
case ExtendSInt32: case ExtendUInt32:
+ case ExtendS8Int32: case ExtendS16Int32:
+ case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64:
case WrapInt64:
case PromoteFloat32:
case DemoteFloat64:
diff --git a/src/wasm/wasm-validator.cpp b/src/wasm/wasm-validator.cpp
index 877232521..5534eb7b9 100644
--- a/src/wasm/wasm-validator.cpp
+++ b/src/wasm/wasm-validator.cpp
@@ -58,7 +58,6 @@ void WasmValidator::visitBlock(Block *curr) {
}
}
breakTargets.erase(curr->name);
- namedBreakTargets.erase(curr->name);
}
if (curr->list.size() > 1) {
for (Index i = 0; i < curr->list.size() - 1; i++) {
@@ -88,7 +87,6 @@ void WasmValidator::visitLoop(Loop *curr) {
if (curr->name.is()) {
noteLabelName(curr->name);
breakTargets.erase(curr->name);
- namedBreakTargets.erase(curr->name);
if (breakInfos.count(curr) > 0) {
auto& info = breakInfos[curr];
shouldBeEqual(info.arity, Index(0), curr, "breaks to a loop cannot pass a value");
@@ -128,7 +126,6 @@ void WasmValidator::visitIf(If *curr) {
}
void WasmValidator::noteBreak(Name name, Expression* value, Expression* curr) {
- namedBreakTargets.insert(name);
WasmType valueType = none;
Index arity = 0;
if (value) {
@@ -433,8 +430,17 @@ void WasmValidator::visitUnary(Unary *curr) {
shouldBeTrue(curr->value->type == i64, curr, "i64.eqz input must be i64");
break;
}
- case ExtendSInt32: shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break;
- case ExtendUInt32: shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break;
+ case ExtendSInt32:
+ case ExtendUInt32:
+ case ExtendS8Int32:
+ case ExtendS16Int32: {
+ shouldBeEqual(curr->value->type, i32, curr, "extend type must be correct"); break;
+ }
+ case ExtendS8Int64:
+ case ExtendS16Int64:
+ case ExtendS32Int64: {
+ shouldBeEqual(curr->value->type, i64, curr, "extend type must be correct"); break;
+ }
case WrapInt64: shouldBeEqual(curr->value->type, i64, curr, "wrap type must be correct"); break;
case TruncSFloat32ToInt32: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break;
case TruncSFloat32ToInt64: shouldBeEqual(curr->value->type, f32, curr, "trunc type must be correct"); break;
@@ -550,9 +556,7 @@ void WasmValidator::visitFunction(Function *curr) {
if (returnType != unreachable) {
shouldBeEqual(curr->result, returnType, curr->body, "function result must match, if function has returns");
}
- if (!shouldBeTrue(namedBreakTargets.empty(), curr->body, "all named break targets must exist") && !quiet) {
- std::cerr << "(on label " << *namedBreakTargets.begin() << ")\n";
- }
+ shouldBeTrue(breakTargets.empty(), curr->body, "all named break targets must exist");
returnType = unreachable;
labelNames.clear();
}
diff --git a/src/wasm/wasm.cpp b/src/wasm/wasm.cpp
index a781e0fa3..6db11cc7d 100644
--- a/src/wasm/wasm.cpp
+++ b/src/wasm/wasm.cpp
@@ -443,7 +443,8 @@ void Unary::finalize() {
case SqrtFloat64: type = value->type; break;
case EqZInt32:
case EqZInt64: type = i32; break;
- case ExtendSInt32: case ExtendUInt32: type = i64; break;
+ case ExtendS8Int32: case ExtendS16Int32: type = i32; break;
+ case ExtendSInt32: case ExtendUInt32: case ExtendS8Int64: case ExtendS16Int64: case ExtendS32Int64: type = i64; break;
case WrapInt64: type = i32; break;
case PromoteFloat32: type = f64; break;
case DemoteFloat64: type = f32; break;
diff --git a/test/signext.wast b/test/signext.wast
new file mode 100644
index 000000000..3370e9b16
--- /dev/null
+++ b/test/signext.wast
@@ -0,0 +1,12 @@
+(module
+ (type $0 (func))
+ (func $signext (type $0)
+ (local $0 i32)
+ (local $1 i64)
+ (drop (i32.extend8_s (get_local $0)))
+ (drop (i32.extend16_s (get_local $0)))
+ (drop (i64.extend8_s (get_local $1)))
+ (drop (i64.extend16_s (get_local $1)))
+ (drop (i64.extend32_s (get_local $1)))
+ )
+)
diff --git a/test/signext.wast.from-wast b/test/signext.wast.from-wast
new file mode 100644
index 000000000..6828723b5
--- /dev/null
+++ b/test/signext.wast.from-wast
@@ -0,0 +1,33 @@
+(module
+ (type $0 (func))
+ (memory $0 0)
+ (func $signext (type $0)
+ (local $0 i32)
+ (local $1 i64)
+ (drop
+ (i32.extend8_s
+ (get_local $0)
+ )
+ )
+ (drop
+ (i32.extend16_s
+ (get_local $0)
+ )
+ )
+ (drop
+ (i64.extend8_s
+ (get_local $1)
+ )
+ )
+ (drop
+ (i64.extend16_s
+ (get_local $1)
+ )
+ )
+ (drop
+ (i64.extend32_s
+ (get_local $1)
+ )
+ )
+ )
+)
diff --git a/test/signext.wast.fromBinary b/test/signext.wast.fromBinary
new file mode 100644
index 000000000..ec9b50aa2
--- /dev/null
+++ b/test/signext.wast.fromBinary
@@ -0,0 +1,36 @@
+(module
+ (type $0 (func))
+ (memory $0 0)
+ (func $signext (type $0)
+ (local $var$0 i32)
+ (local $var$1 i64)
+ (block $label$0
+ (drop
+ (i32.extend8_s
+ (get_local $var$0)
+ )
+ )
+ (drop
+ (i32.extend16_s
+ (get_local $var$0)
+ )
+ )
+ (drop
+ (i64.extend8_s
+ (get_local $var$1)
+ )
+ )
+ (drop
+ (i64.extend16_s
+ (get_local $var$1)
+ )
+ )
+ (drop
+ (i64.extend32_s
+ (get_local $var$1)
+ )
+ )
+ )
+ )
+)
+
diff --git a/test/signext.wast.fromBinary.noDebugInfo b/test/signext.wast.fromBinary.noDebugInfo
new file mode 100644
index 000000000..04ada421c
--- /dev/null
+++ b/test/signext.wast.fromBinary.noDebugInfo
@@ -0,0 +1,36 @@
+(module
+ (type $0 (func))
+ (memory $0 0)
+ (func $0 (type $0)
+ (local $var$0 i32)
+ (local $var$1 i64)
+ (block $label$0
+ (drop
+ (i32.extend8_s
+ (get_local $var$0)
+ )
+ )
+ (drop
+ (i32.extend16_s
+ (get_local $var$0)
+ )
+ )
+ (drop
+ (i64.extend8_s
+ (get_local $var$1)
+ )
+ )
+ (drop
+ (i64.extend16_s
+ (get_local $var$1)
+ )
+ )
+ (drop
+ (i64.extend32_s
+ (get_local $var$1)
+ )
+ )
+ )
+ )
+)
+