summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjgravelle-google <jgravelle@google.com>2017-10-02 13:51:55 -0700
committerGitHub <noreply@github.com>2017-10-02 13:51:55 -0700
commita9f91b9774d117a13c231ef0f40861372456878f (patch)
tree1bda2f8fed8a3affe5538732f4ac11c6b8b2599a
parent28d670ade33ab7a1d091bafee243a2c5ffc93bc9 (diff)
downloadbinaryen-a9f91b9774d117a13c231ef0f40861372456878f.tar.gz
binaryen-a9f91b9774d117a13c231ef0f40861372456878f.tar.bz2
binaryen-a9f91b9774d117a13c231ef0f40861372456878f.zip
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
-rw-r--r--.gitignore1
-rwxr-xr-xcheck.py127
-rw-r--r--scripts/test/asm2wasm.py160
-rwxr-xr-xscripts/test/s2wasm.py62
-rw-r--r--src/asm2wasm.h357
-rw-r--r--src/asm_v_wasm.h3
-rw-r--r--src/asmjs/asm_v_wasm.cpp12
-rw-r--r--src/asmjs/shared-constants.cpp6
-rw-r--r--src/asmjs/shared-constants.h6
-rw-r--r--src/ast/trapping.h100
-rw-r--r--src/passes/CMakeLists.txt1
-rw-r--r--src/passes/TrapMode.cpp318
-rw-r--r--src/passes/pass.cpp2
-rw-r--r--src/passes/passes.h2
-rw-r--r--src/shared-constants.h1
-rw-r--r--src/tools/asm2wasm.cpp11
-rw-r--r--src/tools/s2wasm.cpp27
-rw-r--r--test/dot_s/traps.clamp.wast113
-rw-r--r--test/dot_s/traps.js.wast65
-rw-r--r--test/dot_s/traps.s14
-rw-r--r--test/dot_s/traps.wast49
-rw-r--r--test/emcc_hello_world.fromasm.clamp28
-rw-r--r--test/emcc_hello_world.fromasm.clamp.no-opts28
-rw-r--r--test/passes/trap-mode-clamp.txt432
-rw-r--r--test/passes/trap-mode-clamp.wast26
-rw-r--r--test/passes/trap-mode-js.txt334
-rw-r--r--test/passes/trap-mode-js.wast26
-rw-r--r--test/unit.fromasm.clamp63
-rw-r--r--test/unit.fromasm.clamp.no-opts60
-rw-r--r--test/wasm-only.fromasm102
-rw-r--r--test/wasm-only.fromasm.clamp102
-rw-r--r--test/wasm-only.fromasm.clamp.no-opts92
-rw-r--r--test/wasm-only.fromasm.no-opts92
33 files changed, 2312 insertions, 510 deletions
diff --git a/.gitignore b/.gitignore
index 28042929a..b6219851a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,6 +30,7 @@ trace.cpp
test/wasm-binaries-*.tbz2
test/wasm-torture-s-*.tbz2
test/wasm-install/
+test/validator/*.wasm
*.pyc
CMakeFiles/
diff --git a/check.py b/check.py
index 1cadd2386..36e642ae1 100755
--- a/check.py
+++ b/check.py
@@ -22,7 +22,7 @@ import sys
from scripts.test.support import run_command, split_wast
from scripts.test.shared import (
- ASM2WASM, BIN_DIR, EMCC, MOZJS, NATIVECC, NATIVEXX, NODEJS, S2WASM_EXE,
+ BIN_DIR, EMCC, MOZJS, NATIVECC, NATIVEXX, NODEJS, S2WASM_EXE,
WASM_AS, WASM_CTOR_EVAL, WASM_OPT, WASM_SHELL, WASM_MERGE, WASM_SHELL_EXE,
WASM_DIS, WASM_REDUCE, binary_format_check, delete_from_orbit, fail, fail_with_error,
fail_if_not_identical, fail_if_not_contained, has_vanilla_emcc,
@@ -30,6 +30,7 @@ from scripts.test.shared import (
requested, warnings, has_shell_timeout
)
+import scripts.test.asm2wasm as asm2wasm
import scripts.test.s2wasm as s2wasm
import scripts.test.wasm2asm as wasm2asm
@@ -144,6 +145,7 @@ def run_wasm_opt_tests():
actual = actual.replace('printing before:\n', '')
expected = open(f, 'rb').read()
+
if actual != expected:
fail(actual, expected)
@@ -152,126 +154,6 @@ def run_wasm_opt_tests():
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'
@@ -728,7 +610,8 @@ def run_emscripten_tests():
# Run all the tests
run_help_tests()
run_wasm_opt_tests()
-run_asm2wasm_tests()
+asm2wasm.test_asm2wasm()
+asm2wasm.test_asm2wasm_binary()
run_wasm_dis_tests()
run_wasm_merge_tests()
run_ctor_eval_tests()
diff --git a/scripts/test/asm2wasm.py b/scripts/test/asm2wasm.py
new file mode 100644
index 000000000..db37a237a
--- /dev/null
+++ b/scripts/test/asm2wasm.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+# Copyright 2017 WebAssembly Community Group participants
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import subprocess
+
+from support import run_command
+from shared import (
+ ASM2WASM, WASM_OPT, binary_format_check, delete_from_orbit,
+ fail, fail_with_error, fail_if_not_identical, options, tests
+)
+
+
+def test_asm2wasm():
+ print '[ checking asm2wasm testcases... ]\n'
+
+ for asm in tests:
+ if not asm.endswith('.asm.js'):
+ continue
+ 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'
+ print "With BINARYEN_PASS_DEBUG=1:"
+ do_asm2wasm_test()
+ del os.environ['BINARYEN_PASS_DEBUG']
+ print "With BINARYEN_PASS_DEBUG disabled:"
+ 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:
+ # failed to pretty-print
+ fail_with_error('wasm interpreter error: ' + err)
+ 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')
+ url = 'http://example.org/' + jsmap
+ assert len(url) < 256, 'name too long'
+ url_section_contents = bytearray([len(url)]) + bytearray(url)
+ 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')
+ url_section_index = binary_contents.index(url_section_name)
+ if url_section_contents not in binary_contents[url_section_index:]:
+ fail_with_error('source map url not found in url section')
+
+
+def test_asm2wasm_binary():
+ 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'
+
+
+if __name__ == '__main__':
+ test_asm2wasm()
+ test_asm2wasm_binary()
diff --git a/scripts/test/s2wasm.py b/scripts/test/s2wasm.py
index 3f38acbac..fcba17d18 100755
--- a/scripts/test/s2wasm.py
+++ b/scripts/test/s2wasm.py
@@ -32,38 +32,48 @@ def test_s2wasm():
fail_if_not_contained(
output, '(import "env" "memory" (memory $0 1))')
+ extension_arg_map = {
+ '.wast': [],
+ '.clamp.wast': ['--emit-clamped-potential-traps'],
+ '.js.wast': ['--emit-jsified-potential-traps'],
+ }
for dot_s_dir in ['dot_s', 'llvm_autogenerated']:
dot_s_path = os.path.join(options.binaryen_test, dot_s_dir)
for s in sorted(os.listdir(dot_s_path)):
if not s.endswith('.s'):
continue
print '..', s
- wasm = s.replace('.s', '.wast')
- full = os.path.join(options.binaryen_test, dot_s_dir, s)
- stack_alloc = (['--allocate-stack=1024']
- if dot_s_dir == 'llvm_autogenerated'
- else [])
- cmd = S2WASM + [full, '--emscripten-glue'] + stack_alloc
- if s.startswith('start_'):
- cmd.append('--start')
- actual = run_command(cmd)
-
- # verify output
- expected_file = os.path.join(options.binaryen_test, dot_s_dir, wasm)
- if not os.path.exists(expected_file):
- print actual
- fail_with_error('output ' + expected_file + ' does not exist')
- expected = open(expected_file, 'rb').read()
- if actual != expected:
- fail(actual, expected)
-
- # verify with options
- cmd = S2WASM + [full, '--global-base=1024'] + stack_alloc
- run_command(cmd)
-
- # run wasm-shell on the .wast to verify that it parses
- cmd = WASM_SHELL + [expected_file]
- run_command(cmd)
+ for ext, ext_args in extension_arg_map.iteritems():
+ wasm = s.replace('.s', ext)
+ expected_file = os.path.join(options.binaryen_test, dot_s_dir, wasm)
+ expected_exists = os.path.exists(expected_file)
+ if ext != '.wast' and not expected_exists:
+ continue
+
+ full = os.path.join(options.binaryen_test, dot_s_dir, s)
+ stack_alloc = (['--allocate-stack=1024']
+ if dot_s_dir == 'llvm_autogenerated'
+ else [])
+ cmd = S2WASM + [full, '--emscripten-glue'] + stack_alloc + ext_args
+ if s.startswith('start_'):
+ cmd.append('--start')
+ actual = run_command(cmd)
+
+ # verify output
+ if not expected_exists:
+ print actual
+ fail_with_error('output ' + expected_file + ' does not exist')
+ expected = open(expected_file, 'rb').read()
+ if actual != expected:
+ fail(actual, expected)
+
+ # verify with options
+ cmd = S2WASM + [full, '--global-base=1024'] + stack_alloc
+ run_command(cmd)
+
+ # run wasm-shell on the .wast to verify that it parses
+ cmd = WASM_SHELL + [expected_file]
+ run_command(cmd)
def test_linker():
diff --git a/src/asm2wasm.h b/src/asm2wasm.h
index 044517d12..7d4ce5fbe 100644
--- a/src/asm2wasm.h
+++ b/src/asm2wasm.h
@@ -33,6 +33,7 @@
#include "parsing.h"
#include "ast_utils.h"
#include "ast/branch-utils.h"
+#include "ast/trapping.h"
#include "wasm-builder.h"
#include "wasm-emscripten.h"
#include "wasm-module-building.h"
@@ -88,10 +89,6 @@ Name I32_CTTZ("i32_cttz"),
I64_CTTZ("i64_cttz"),
I64_CTLZ("i64_ctlz"),
I64_CTPOP("i64_ctpop"),
- I64S_REM("i64s-rem"),
- I64U_REM("i64u-rem"),
- I64S_DIV("i64s-div"),
- I64U_DIV("i64u-div"),
F32_COPYSIGN("f32_copysign"),
F64_COPYSIGN("f64_copysign"),
LOAD1("load1"),
@@ -356,12 +353,6 @@ struct AdjustDebugInfo : public WalkerPass<PostWalker<AdjustDebugInfo, Visitor<A
class Asm2WasmBuilder {
public:
- enum class TrapMode {
- Allow,
- Clamp,
- JS
- };
-
Module& wasm;
MixedArena &allocator;
@@ -388,6 +379,7 @@ public:
bool debug;
TrapMode trapMode;
+ TrappingFunctionContainer trappingFunctions;
PassOptions passOptions;
bool legalizeJavaScriptFFI;
bool runOptimizationPasses;
@@ -522,6 +514,7 @@ public:
preprocessor(preprocessor),
debug(debug),
trapMode(trapMode),
+ trappingFunctions(trapMode, wasm, /* immediate = */ true),
passOptions(passOptions),
legalizeJavaScriptFFI(legalizeJavaScriptFFI),
runOptimizationPasses(runOptimizationPasses),
@@ -701,277 +694,8 @@ private:
return ret;
}
- // converts an f32 to an f64 if necessary
Expression* ensureDouble(Expression* expr) {
- if (expr->type == f32) {
- auto conv = allocator.alloc<Unary>();
- conv->op = PromoteFloat32;
- conv->value = expr;
- conv->type = WasmType::f64;
- return conv;
- }
- assert(expr->type == f64);
- return expr;
- }
-
- // Some binary opts might trap, so emit them safely if necessary
- Expression* makeTrappingI32Binary(BinaryOp op, Expression* left, Expression* right) {
- if (trapMode == TrapMode::Allow) return builder.makeBinary(op, left, right);
- // the wasm operation might trap if done over 0, so generate a safe call
- auto *call = allocator.alloc<Call>();
- switch (op) {
- case BinaryOp::RemSInt32: call->target = I32S_REM; break;
- case BinaryOp::RemUInt32: call->target = I32U_REM; break;
- case BinaryOp::DivSInt32: call->target = I32S_DIV; break;
- case BinaryOp::DivUInt32: call->target = I32U_DIV; break;
- default: WASM_UNREACHABLE();
- }
- call->operands.push_back(left);
- call->operands.push_back(right);
- call->type = i32;
- static std::set<Name> addedFunctions;
- if (addedFunctions.count(call->target) == 0) {
- Expression* result = builder.makeBinary(op,
- builder.makeGetLocal(0, i32),
- builder.makeGetLocal(1, i32)
- );
- if (op == DivSInt32) {
- // guard against signed division overflow
- result = builder.makeIf(
- builder.makeBinary(AndInt32,
- builder.makeBinary(EqInt32,
- builder.makeGetLocal(0, i32),
- builder.makeConst(Literal(std::numeric_limits<int32_t>::min()))
- ),
- builder.makeBinary(EqInt32,
- builder.makeGetLocal(1, i32),
- builder.makeConst(Literal(int32_t(-1)))
- )
- ),
- builder.makeConst(Literal(int32_t(0))),
- result
- );
- }
- addedFunctions.insert(call->target);
- auto func = new Function;
- func->name = call->target;
- func->params.push_back(i32);
- func->params.push_back(i32);
- func->result = i32;
- func->body = builder.makeIf(
- builder.makeUnary(EqZInt32,
- builder.makeGetLocal(1, i32)
- ),
- builder.makeConst(Literal(int32_t(0))),
- result
- );
- wasm.addFunction(func);
- }
- return call;
- }
-
- // Some binary opts might trap, so emit them safely if necessary
- Expression* makeTrappingI64Binary(BinaryOp op, Expression* left, Expression* right) {
- if (trapMode == TrapMode::Allow) return builder.makeBinary(op, left, right);
- // wasm operation might trap if done over 0, so generate a safe call
- auto *call = allocator.alloc<Call>();
- switch (op) {
- case BinaryOp::RemSInt64: call->target = I64S_REM; break;
- case BinaryOp::RemUInt64: call->target = I64U_REM; break;
- case BinaryOp::DivSInt64: call->target = I64S_DIV; break;
- case BinaryOp::DivUInt64: call->target = I64U_DIV; break;
- default: WASM_UNREACHABLE();
- }
- call->operands.push_back(left);
- call->operands.push_back(right);
- call->type = i64;
- static std::set<Name> addedFunctions;
- if (addedFunctions.count(call->target) == 0) {
- Expression* result = builder.makeBinary(op,
- builder.makeGetLocal(0, i64),
- builder.makeGetLocal(1, i64)
- );
- if (op == DivSInt64) {
- // guard against signed division overflow
- result = builder.makeIf(
- builder.makeBinary(AndInt32,
- builder.makeBinary(EqInt64,
- builder.makeGetLocal(0, i64),
- builder.makeConst(Literal(std::numeric_limits<int64_t>::min()))
- ),
- builder.makeBinary(EqInt64,
- builder.makeGetLocal(1, i64),
- builder.makeConst(Literal(int64_t(-1)))
- )
- ),
- builder.makeConst(Literal(int64_t(0))),
- result
- );
- }
- addedFunctions.insert(call->target);
- auto func = new Function;
- func->name = call->target;
- func->params.push_back(i64);
- func->params.push_back(i64);
- func->result = i64;
- func->body = builder.makeIf(
- builder.makeUnary(EqZInt64,
- builder.makeGetLocal(1, i64)
- ),
- builder.makeConst(Literal(int64_t(0))),
- result
- );
- wasm.addFunction(func);
- }
- return call;
- }
-
- // Some conversions might trap, so emit them safely if necessary
- Expression* makeTrappingFloatToInt32(bool signed_, Expression* value) {
- if (trapMode == TrapMode::Allow) {
- auto ret = allocator.alloc<Unary>();
- ret->value = value;
- bool isF64 = ret->value->type == f64;
- if (signed_) {
- ret->op = isF64 ? TruncSFloat64ToInt32 : TruncSFloat32ToInt32;
- } else {
- ret->op = isF64 ? TruncUFloat64ToInt32 : TruncUFloat32ToInt32;
- }
- ret->type = WasmType::i32;
- return ret;
- }
- // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must do something
- // First, normalize input to f64
- auto input = ensureDouble(value);
- // We can handle this in one of two ways: clamping, which is fast, or JS, which
- // is precisely like JS but in order to do that we do a slow ffi
- if (trapMode == TrapMode::JS) {
- // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must emulate that
- CallImport *ret = allocator.alloc<CallImport>();
- ret->target = F64_TO_INT;
- ret->operands.push_back(input);
- ret->type = i32;
- static bool addedImport = false;
- if (!addedImport) {
- addedImport = true;
- auto import = new Import; // f64-to-int = asm2wasm.f64-to-int;
- import->name = F64_TO_INT;
- import->module = ASM2WASM;
- import->base = F64_TO_INT;
- import->functionType = ensureFunctionType("id", &wasm)->name;
- import->kind = ExternalKind::Function;
- wasm.addImport(import);
- }
- return ret;
- }
- assert(trapMode == TrapMode::Clamp);
- Call *ret = allocator.alloc<Call>();
- ret->target = F64_TO_INT;
- ret->operands.push_back(input);
- ret->type = i32;
- static bool added = false;
- if (!added) {
- added = true;
- auto func = new Function;
- func->name = ret->target;
- func->params.push_back(f64);
- func->result = i32;
- func->body = builder.makeUnary(TruncSFloat64ToInt32,
- builder.makeGetLocal(0, f64)
- );
- // too small XXX this is different than asm.js, which does frem. here we clamp, which is much simpler/faster, and similar to native builds
- func->body = builder.makeIf(
- builder.makeBinary(LeFloat64,
- builder.makeGetLocal(0, f64),
- builder.makeConst(Literal(double(std::numeric_limits<int32_t>::min()) - 1))
- ),
- builder.makeConst(Literal(int32_t(std::numeric_limits<int32_t>::min()))),
- func->body
- );
- // too big XXX see above
- func->body = builder.makeIf(
- builder.makeBinary(GeFloat64,
- builder.makeGetLocal(0, f64),
- builder.makeConst(Literal(double(std::numeric_limits<int32_t>::max()) + 1))
- ),
- builder.makeConst(Literal(int32_t(std::numeric_limits<int32_t>::min()))), // NB: min here as well. anything out of range => to the min
- func->body
- );
- // nan
- func->body = builder.makeIf(
- builder.makeBinary(NeFloat64,
- builder.makeGetLocal(0, f64),
- builder.makeGetLocal(0, f64)
- ),
- builder.makeConst(Literal(int32_t(std::numeric_limits<int32_t>::min()))), // NB: min here as well. anything invalid => to the min
- func->body
- );
- wasm.addFunction(func);
- }
- return ret;
- }
-
- Expression* makeTrappingFloatToInt64(bool signed_, Expression* value) {
- if (trapMode == TrapMode::Allow) {
- auto ret = allocator.alloc<Unary>();
- ret->value = value;
- bool isF64 = ret->value->type == f64;
- if (signed_) {
- ret->op = isF64 ? TruncSFloat64ToInt64 : TruncSFloat32ToInt64;
- } else {
- ret->op = isF64 ? TruncUFloat64ToInt64 : TruncUFloat32ToInt64;
- }
- ret->type = WasmType::i64;
- return ret;
- }
- // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must do something
- // First, normalize input to f64
- auto input = ensureDouble(value);
- // There is no "JS" way to handle this, as no i64s in JS, so always clamp if we don't allow traps
- Call *ret = allocator.alloc<Call>();
- ret->target = F64_TO_INT64;
- ret->operands.push_back(input);
- ret->type = i64;
- static bool added = false;
- if (!added) {
- added = true;
- auto func = new Function;
- func->name = ret->target;
- func->params.push_back(f64);
- func->result = i64;
- func->body = builder.makeUnary(TruncSFloat64ToInt64,
- builder.makeGetLocal(0, f64)
- );
- // too small
- func->body = builder.makeIf(
- builder.makeBinary(LeFloat64,
- builder.makeGetLocal(0, f64),
- builder.makeConst(Literal(double(std::numeric_limits<int64_t>::min()) - 1))
- ),
- builder.makeConst(Literal(int64_t(std::numeric_limits<int64_t>::min()))),
- func->body
- );
- // too big
- func->body = builder.makeIf(
- builder.makeBinary(GeFloat64,
- builder.makeGetLocal(0, f64),
- builder.makeConst(Literal(double(std::numeric_limits<int64_t>::max()) + 1))
- ),
- builder.makeConst(Literal(int64_t(std::numeric_limits<int64_t>::min()))), // NB: min here as well. anything out of range => to the min
- func->body
- );
- // nan
- func->body = builder.makeIf(
- builder.makeBinary(NeFloat64,
- builder.makeGetLocal(0, f64),
- builder.makeGetLocal(0, f64)
- ),
- builder.makeConst(Literal(int64_t(std::numeric_limits<int64_t>::min()))), // NB: min here as well. anything invalid => to the min
- func->body
- );
- wasm.addFunction(func);
- }
- return ret;
+ return wasm::ensureDouble(expr, allocator);
}
Expression* truncateToInt32(Expression* value) {
@@ -1433,15 +1157,23 @@ void Asm2WasmBuilder::processAsm(Ref ast) {
}
}
}
- auto importResult = getModule()->getFunctionType(getModule()->getImport(curr->target)->functionType)->result;
+ Module* wasm = getModule();
+ auto importResult = wasm->getFunctionType(wasm->getImport(curr->target)->functionType)->result;
if (curr->type != importResult) {
auto old = curr->type;
curr->type = importResult;
if (importResult == f64) {
// we use a JS f64 value which is the most general, and convert to it
switch (old) {
- case i32: replaceCurrent(parent->makeTrappingFloatToInt32(true /* signed, asm.js ffi */, curr)); break;
- case f32: replaceCurrent(parent->builder.makeUnary(DemoteFloat64, curr)); break;
+ case i32: {
+ Unary* trunc = parent->builder.makeUnary(TruncSFloat64ToInt32, curr);
+ replaceCurrent(makeTrappingUnary(trunc, parent->trappingFunctions));
+ break;
+ }
+ case f32: {
+ replaceCurrent(parent->builder.makeUnary(DemoteFloat64, curr));
+ break;
+ }
case none: {
// this function returns a value, but we are not using it, so it must be dropped.
// autodrop will do that for us.
@@ -1896,12 +1628,8 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
wasm.addImport(import);
}
return call;
- } else if (trapMode != TrapMode::Allow &&
- (ret->op == BinaryOp::RemSInt32 || ret->op == BinaryOp::RemUInt32 ||
- ret->op == BinaryOp::DivSInt32 || ret->op == BinaryOp::DivUInt32)) {
- return makeTrappingI32Binary(ret->op, ret->left, ret->right);
}
- return ret;
+ return makeTrappingBinary(ret, trappingFunctions);
} else if (what == SUB) {
Ref target = ast[1];
assert(target->isString());
@@ -1969,7 +1697,20 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
// ~, might be ~~ as a coercion or just a not
if (ast[2]->isArray(UNARY_PREFIX) && ast[2][1] == B_NOT) {
// if we have an unsigned coercion on us, it is an unsigned op
- return makeTrappingFloatToInt32(!isParentUnsignedCoercion(astStackHelper.getParent()), process(ast[2][2]));
+ Expression* expr = process(ast[2][2]);
+ bool isSigned = !isParentUnsignedCoercion(astStackHelper.getParent());
+ bool isF64 = expr->type == f64;
+ UnaryOp op;
+ if (isSigned && isF64) {
+ op = UnaryOp::TruncSFloat64ToInt32;
+ } else if (isSigned && !isF64) {
+ op = UnaryOp::TruncSFloat32ToInt32;
+ } else if (!isSigned && isF64) {
+ op = UnaryOp::TruncUFloat64ToInt32;
+ } else { // !isSigned && !isF64
+ op = UnaryOp::TruncUFloat32ToInt32;
+ }
+ return makeTrappingUnary(builder.makeUnary(op, expr), trappingFunctions);
}
// no bitwise unary not, so do xor with -1
auto ret = allocator.alloc<Binary>();
@@ -2216,8 +1957,22 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
if (name == I64_S2D) return builder.makeUnary(UnaryOp::ConvertSInt64ToFloat64, value);
if (name == I64_U2F) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat32, value);
if (name == I64_U2D) return builder.makeUnary(UnaryOp::ConvertUInt64ToFloat64, value);
- if (name == I64_F2S || name == I64_D2S) return makeTrappingFloatToInt64(true /* signed */, value);
- if (name == I64_F2U || name == I64_D2U) return makeTrappingFloatToInt64(false /* unsigned */, value);
+ if (name == I64_F2S) {
+ Unary* conv = builder.makeUnary(UnaryOp::TruncSFloat32ToInt64, value);
+ return makeTrappingUnary(conv, trappingFunctions);
+ }
+ if (name == I64_D2S) {
+ Unary* conv = builder.makeUnary(UnaryOp::TruncSFloat64ToInt64, value);
+ return makeTrappingUnary(conv, trappingFunctions);
+ }
+ if (name == I64_F2U) {
+ Unary* conv = builder.makeUnary(UnaryOp::TruncUFloat32ToInt64, value);
+ return makeTrappingUnary(conv, trappingFunctions);
+ }
+ if (name == I64_D2U) {
+ Unary* conv = builder.makeUnary(UnaryOp::TruncUFloat64ToInt64, value);
+ return makeTrappingUnary(conv, trappingFunctions);
+ }
if (name == I64_BC2D) return builder.makeUnary(UnaryOp::ReinterpretInt64, value);
if (name == I64_BC2I) return builder.makeUnary(UnaryOp::ReinterpretFloat64, value);
if (name == I64_CTTZ) return builder.makeUnary(UnaryOp::CtzInt64, value);
@@ -2231,10 +1986,22 @@ Function* Asm2WasmBuilder::processFunction(Ref ast) {
if (name == I64_ADD) return builder.makeBinary(BinaryOp::AddInt64, left, right);
if (name == I64_SUB) return builder.makeBinary(BinaryOp::SubInt64, left, right);
if (name == I64_MUL) return builder.makeBinary(BinaryOp::MulInt64, left, right);
- if (name == I64_UDIV) return makeTrappingI64Binary(BinaryOp::DivUInt64, left, right);
- if (name == I64_SDIV) return makeTrappingI64Binary(BinaryOp::DivSInt64, left, right);
- if (name == I64_UREM) return makeTrappingI64Binary(BinaryOp::RemUInt64, left, right);
- if (name == I64_SREM) return makeTrappingI64Binary(BinaryOp::RemSInt64, left, right);
+ if (name == I64_UDIV) {
+ Binary* div = builder.makeBinary(BinaryOp::DivUInt64, left, right);
+ return makeTrappingBinary(div, trappingFunctions);
+ }
+ if (name == I64_SDIV) {
+ Binary* div = builder.makeBinary(BinaryOp::DivSInt64, left, right);
+ return makeTrappingBinary(div, trappingFunctions);
+ }
+ if (name == I64_UREM) {
+ Binary* rem = builder.makeBinary(BinaryOp::RemUInt64, left, right);
+ return makeTrappingBinary(rem, trappingFunctions);
+ }
+ if (name == I64_SREM) {
+ Binary* rem = builder.makeBinary(BinaryOp::RemSInt64, left, right);
+ return makeTrappingBinary(rem, trappingFunctions);
+ }
if (name == I64_AND) return builder.makeBinary(BinaryOp::AndInt64, left, right);
if (name == I64_OR) return builder.makeBinary(BinaryOp::OrInt64, left, right);
if (name == I64_XOR) return builder.makeBinary(BinaryOp::XorInt64, left, right);
diff --git a/src/asm_v_wasm.h b/src/asm_v_wasm.h
index 53881861c..d42a1082a 100644
--- a/src/asm_v_wasm.h
+++ b/src/asm_v_wasm.h
@@ -70,6 +70,9 @@ FunctionType* sigToFunctionType(std::string sig);
FunctionType* ensureFunctionType(std::string sig, Module* wasm);
+// converts an f32 to an f64 if necessary
+Expression* ensureDouble(Expression* expr, MixedArena& allocator);
+
} // namespace wasm
#endif // wasm_asm_v_wasm_h
diff --git a/src/asmjs/asm_v_wasm.cpp b/src/asmjs/asm_v_wasm.cpp
index ae7d320ca..bfb04a9fd 100644
--- a/src/asmjs/asm_v_wasm.cpp
+++ b/src/asmjs/asm_v_wasm.cpp
@@ -109,4 +109,16 @@ FunctionType* ensureFunctionType(std::string sig, Module* wasm) {
return type;
}
+Expression* ensureDouble(Expression* expr, MixedArena& allocator) {
+ if (expr->type == f32) {
+ auto conv = allocator.alloc<Unary>();
+ conv->op = PromoteFloat32;
+ conv->value = expr;
+ conv->type = WasmType::f64;
+ return conv;
+ }
+ assert(expr->type == f64);
+ return expr;
+}
+
} // namespace wasm
diff --git a/src/asmjs/shared-constants.cpp b/src/asmjs/shared-constants.cpp
index 43c3d1065..f62be6168 100644
--- a/src/asmjs/shared-constants.cpp
+++ b/src/asmjs/shared-constants.cpp
@@ -42,7 +42,13 @@ cashew::IString GLOBAL("global"),
ASM2WASM("asm2wasm"),
F64_REM("f64-rem"),
F64_TO_INT("f64-to-int"),
+ F64_TO_UINT("f64-to-uint"),
F64_TO_INT64("f64-to-int64"),
+ F64_TO_UINT64("f64-to-uint64"),
+ F32_TO_INT("f32-to-int"),
+ F32_TO_UINT("f32-to-uint"),
+ F32_TO_INT64("f32-to-int64"),
+ F32_TO_UINT64("f32-to-uint64"),
I32S_DIV("i32s-div"),
I32U_DIV("i32u-div"),
I32S_REM("i32s-rem"),
diff --git a/src/asmjs/shared-constants.h b/src/asmjs/shared-constants.h
index ce5cd95a6..7e4b27c85 100644
--- a/src/asmjs/shared-constants.h
+++ b/src/asmjs/shared-constants.h
@@ -45,7 +45,13 @@ extern cashew::IString GLOBAL,
ASM2WASM,
F64_REM,
F64_TO_INT,
+ F64_TO_UINT,
F64_TO_INT64,
+ F64_TO_UINT64,
+ F32_TO_INT,
+ F32_TO_UINT,
+ F32_TO_INT64,
+ F32_TO_UINT64,
I32S_DIV,
I32U_DIV,
I32S_REM,
diff --git a/src/ast/trapping.h b/src/ast/trapping.h
new file mode 100644
index 000000000..80cc14da9
--- /dev/null
+++ b/src/ast/trapping.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_ast_trapping_h
+#define wasm_ast_trapping_h
+
+#include "pass.h"
+
+namespace wasm {
+
+enum class TrapMode {
+ Allow,
+ Clamp,
+ JS
+};
+
+inline void addTrapModePass(PassRunner& runner, TrapMode trapMode) {
+ if (trapMode == TrapMode::Clamp) {
+ runner.add("trap-mode-clamp");
+ } else if (trapMode == TrapMode::JS) {
+ runner.add("trap-mode-js");
+ }
+}
+
+class TrappingFunctionContainer {
+public:
+ TrappingFunctionContainer(TrapMode mode, Module &wasm, bool immediate = false)
+ : mode(mode),
+ wasm(wasm),
+ immediate(immediate) { }
+
+ bool hasFunction(Name name) {
+ return functions.find(name) != functions.end();
+ }
+ bool hasImport(Name name) {
+ return imports.find(name) != imports.end();
+ }
+
+ void addFunction(Function* function) {
+ functions[function->name] = function;
+ if (immediate) {
+ wasm.addFunction(function);
+ }
+ }
+ void addImport(Import* import) {
+ imports[import->name] = import;
+ if (immediate) {
+ wasm.addImport(import);
+ }
+ }
+
+ void addToModule() {
+ if (!immediate) {
+ for (auto &pair : functions) {
+ wasm.addFunction(pair.second);
+ }
+ for (auto &pair : imports) {
+ wasm.addImport(pair.second);
+ }
+ }
+ functions.clear();
+ imports.clear();
+ }
+
+ TrapMode getMode() {
+ return mode;
+ }
+
+ Module& getModule() {
+ return wasm;
+ }
+
+private:
+ std::map<Name, Function*> functions;
+ std::map<Name, Import*> imports;
+
+ TrapMode mode;
+ Module& wasm;
+ bool immediate;
+};
+
+Expression* makeTrappingBinary(Binary* curr, TrappingFunctionContainer &trappingFunctions);
+Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer &trappingFunctions);
+
+} // wasm
+
+#endif // wasm_ast_trapping_h
diff --git a/src/passes/CMakeLists.txt b/src/passes/CMakeLists.txt
index 6870259c4..3cb00b796 100644
--- a/src/passes/CMakeLists.txt
+++ b/src/passes/CMakeLists.txt
@@ -34,6 +34,7 @@ SET(passes_SOURCES
RemoveUnusedModuleElements.cpp
ReorderLocals.cpp
ReorderFunctions.cpp
+ TrapMode.cpp
SafeHeap.cpp
SimplifyLocals.cpp
SSAify.cpp
diff --git a/src/passes/TrapMode.cpp b/src/passes/TrapMode.cpp
new file mode 100644
index 000000000..e648f66a5
--- /dev/null
+++ b/src/passes/TrapMode.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright 2017 WebAssembly Community Group participants
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//
+// Pass that supports potentially-trapping wasm operations.
+// For example, integer division traps when dividing by zero, so this pass
+// generates a check and replaces the result with zero in that case.
+//
+
+#include "asm_v_wasm.h"
+#include "asmjs/shared-constants.h"
+#include "ast/trapping.h"
+#include "mixed_arena.h"
+#include "pass.h"
+#include "wasm.h"
+#include "wasm-builder.h"
+#include "wasm-printing.h"
+#include "wasm-type.h"
+#include "support/name.h"
+
+namespace wasm {
+
+Name I64S_REM("i64s-rem"),
+ I64U_REM("i64u-rem"),
+ I64S_DIV("i64s-div"),
+ I64U_DIV("i64u-div");
+
+Name getBinaryFuncName(Binary* curr) {
+ switch (curr->op) {
+ case RemSInt32: return I32S_REM;
+ case RemUInt32: return I32U_REM;
+ case DivSInt32: return I32S_DIV;
+ case DivUInt32: return I32U_DIV;
+ case RemSInt64: return I64S_REM;
+ case RemUInt64: return I64U_REM;
+ case DivSInt64: return I64S_DIV;
+ case DivUInt64: return I64U_DIV;
+ default: return Name();
+ }
+}
+
+Name getUnaryFuncName(Unary* curr) {
+ switch (curr->op) {
+ case TruncSFloat32ToInt32: return F32_TO_INT;
+ case TruncUFloat32ToInt32: return F32_TO_UINT;
+ case TruncSFloat32ToInt64: return F32_TO_INT64;
+ case TruncUFloat32ToInt64: return F32_TO_UINT64;
+ case TruncSFloat64ToInt32: return F64_TO_INT;
+ case TruncUFloat64ToInt32: return F64_TO_UINT;
+ case TruncSFloat64ToInt64: return F64_TO_INT64;
+ case TruncUFloat64ToInt64: return F64_TO_UINT64;
+ default: return Name();
+ }
+}
+
+bool isTruncOpSigned(UnaryOp op) {
+ switch (op) {
+ case TruncUFloat32ToInt32:
+ case TruncUFloat32ToInt64:
+ case TruncUFloat64ToInt32:
+ case TruncUFloat64ToInt64: return false;
+ default: return true;
+ }
+}
+
+Function* generateBinaryFunc(Module& wasm, Binary *curr) {
+ BinaryOp op = curr->op;
+ WasmType type = curr->type;
+ bool isI64 = type == i64;
+ Builder builder(wasm);
+ Expression* result = builder.makeBinary(op,
+ builder.makeGetLocal(0, type),
+ builder.makeGetLocal(1, type)
+ );
+ BinaryOp divSIntOp = isI64 ? DivSInt64 : DivSInt32;
+ UnaryOp eqZOp = isI64 ? EqZInt64 : EqZInt32;
+ Literal minLit = isI64 ? Literal(std::numeric_limits<int64_t>::min())
+ : Literal(std::numeric_limits<int32_t>::min());
+ Literal zeroLit = isI64 ? Literal(int64_t(0)) : Literal(int32_t(0));
+ if (op == divSIntOp) {
+ // guard against signed division overflow
+ BinaryOp eqOp = isI64 ? EqInt64 : EqInt32;
+ Literal negLit = isI64 ? Literal(int64_t(-1)) : Literal(int32_t(-1));
+ result = builder.makeIf(
+ builder.makeBinary(AndInt32,
+ builder.makeBinary(eqOp,
+ builder.makeGetLocal(0, type),
+ builder.makeConst(minLit)
+ ),
+ builder.makeBinary(eqOp,
+ builder.makeGetLocal(1, type),
+ builder.makeConst(negLit)
+ )
+ ),
+ builder.makeConst(zeroLit),
+ result
+ );
+ }
+ auto func = new Function;
+ func->name = getBinaryFuncName(curr);
+ func->params.push_back(type);
+ func->params.push_back(type);
+ func->result = type;
+ func->body = builder.makeIf(
+ builder.makeUnary(eqZOp,
+ builder.makeGetLocal(1, type)
+ ),
+ builder.makeConst(zeroLit),
+ result
+ );
+ return func;
+}
+
+template <typename IntType, typename FloatType>
+void makeClampLimitLiterals(Literal& iMin, Literal& fMin, Literal& fMax) {
+ IntType minVal = std::numeric_limits<IntType>::min();
+ IntType maxVal = std::numeric_limits<IntType>::max();
+ iMin = Literal(minVal);
+ fMin = Literal(FloatType(minVal) - 1);
+ fMax = Literal(FloatType(maxVal) + 1);
+}
+
+Function* generateUnaryFunc(Module& wasm, Unary *curr) {
+ WasmType type = curr->value->type;
+ WasmType retType = curr->type;
+ UnaryOp truncOp = curr->op;
+ bool isF64 = type == f64;
+
+ Builder builder(wasm);
+
+ BinaryOp leOp = isF64 ? LeFloat64 : LeFloat32;
+ BinaryOp geOp = isF64 ? GeFloat64 : GeFloat32;
+ BinaryOp neOp = isF64 ? NeFloat64 : NeFloat32;
+
+ Literal iMin, fMin, fMax;
+ switch (truncOp) {
+ case TruncSFloat32ToInt32: makeClampLimitLiterals< int32_t, float>(iMin, fMin, fMax); break;
+ case TruncUFloat32ToInt32: makeClampLimitLiterals<uint32_t, float>(iMin, fMin, fMax); break;
+ case TruncSFloat32ToInt64: makeClampLimitLiterals< int64_t, float>(iMin, fMin, fMax); break;
+ case TruncUFloat32ToInt64: makeClampLimitLiterals<uint64_t, float>(iMin, fMin, fMax); break;
+ case TruncSFloat64ToInt32: makeClampLimitLiterals< int32_t, double>(iMin, fMin, fMax); break;
+ case TruncUFloat64ToInt32: makeClampLimitLiterals<uint32_t, double>(iMin, fMin, fMax); break;
+ case TruncSFloat64ToInt64: makeClampLimitLiterals< int64_t, double>(iMin, fMin, fMax); break;
+ case TruncUFloat64ToInt64: makeClampLimitLiterals<uint64_t, double>(iMin, fMin, fMax); break;
+ default: WASM_UNREACHABLE();
+ }
+
+ auto func = new Function;
+ func->name = getUnaryFuncName(curr);
+ func->params.push_back(type);
+ func->result = retType;
+ func->body = builder.makeUnary(truncOp,
+ builder.makeGetLocal(0, type)
+ );
+ // too small XXX this is different than asm.js, which does frem. here we
+ // clamp, which is much simpler/faster, and similar to native builds
+ func->body = builder.makeIf(
+ builder.makeBinary(leOp,
+ builder.makeGetLocal(0, type),
+ builder.makeConst(fMin)
+ ),
+ builder.makeConst(iMin),
+ func->body
+ );
+ // too big XXX see above
+ func->body = builder.makeIf(
+ builder.makeBinary(geOp,
+ builder.makeGetLocal(0, type),
+ builder.makeConst(fMax)
+ ),
+ // NB: min here as well. anything out of range => to the min
+ builder.makeConst(iMin),
+ func->body
+ );
+ // nan
+ func->body = builder.makeIf(
+ builder.makeBinary(neOp,
+ builder.makeGetLocal(0, type),
+ builder.makeGetLocal(0, type)
+ ),
+ // NB: min here as well. anything invalid => to the min
+ builder.makeConst(iMin),
+ func->body
+ );
+ return func;
+}
+
+void ensureBinaryFunc(Binary* curr, Module& wasm,
+ TrappingFunctionContainer &trappingFunctions) {
+ Name name = getBinaryFuncName(curr);
+ if (trappingFunctions.hasFunction(name)) {
+ return;
+ }
+ trappingFunctions.addFunction(generateBinaryFunc(wasm, curr));
+}
+
+void ensureUnaryFunc(Unary *curr, Module& wasm,
+ TrappingFunctionContainer &trappingFunctions) {
+ Name name = getUnaryFuncName(curr);
+ if (trappingFunctions.hasFunction(name)) {
+ return;
+ }
+ trappingFunctions.addFunction(generateUnaryFunc(wasm, curr));
+}
+
+void ensureF64ToI64JSImport(TrappingFunctionContainer &trappingFunctions) {
+ if (trappingFunctions.hasImport(F64_TO_INT)) {
+ return;
+ }
+
+ Module& wasm = trappingFunctions.getModule();
+ auto import = new Import; // f64-to-int = asm2wasm.f64-to-int;
+ import->name = F64_TO_INT;
+ import->module = ASM2WASM;
+ import->base = F64_TO_INT;
+ import->functionType = ensureFunctionType("id", &wasm)->name;
+ import->kind = ExternalKind::Function;
+ trappingFunctions.addImport(import);
+}
+
+Expression* makeTrappingBinary(Binary* curr, TrappingFunctionContainer &trappingFunctions) {
+ Name name = getBinaryFuncName(curr);
+ if (!name.is() || trappingFunctions.getMode() == TrapMode::Allow) {
+ return curr;
+ }
+
+ // the wasm operation might trap if done over 0, so generate a safe call
+ WasmType type = curr->type;
+ Module& wasm = trappingFunctions.getModule();
+ Builder builder(wasm);
+ ensureBinaryFunc(curr, wasm, trappingFunctions);
+ return builder.makeCall(name, {curr->left, curr->right}, type);
+}
+
+Expression* makeTrappingUnary(Unary* curr, TrappingFunctionContainer &trappingFunctions) {
+ Name name = getUnaryFuncName(curr);
+ TrapMode mode = trappingFunctions.getMode();
+ if (!name.is() || mode == TrapMode::Allow) {
+ return curr;
+ }
+
+ Module& wasm = trappingFunctions.getModule();
+ Builder builder(wasm);
+ // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must do something
+ // We can handle this in one of two ways: clamping, which is fast, or JS, which
+ // is precisely like JS but in order to do that we do a slow ffi
+ // If i64, there is no "JS" way to handle this, as no i64s in JS, so always clamp if we don't allow traps
+ // asm.js doesn't have unsigned f64-to-int, so just use the signed one.
+ if (curr->type != i64 && mode == TrapMode::JS) {
+ // WebAssembly traps on float-to-int overflows, but asm.js wouldn't, so we must emulate that
+ ensureF64ToI64JSImport(trappingFunctions);
+ Expression* f64Value = ensureDouble(curr->value, wasm.allocator);
+ return builder.makeCallImport(F64_TO_INT, {f64Value}, i32);
+ }
+
+ ensureUnaryFunc(curr, wasm, trappingFunctions);
+ return builder.makeCall(name, {curr->value}, curr->type);
+}
+
+struct TrapModePass : public WalkerPass<PostWalker<TrapModePass>> {
+public:
+
+ // Needs to be non-parallel so that visitModule gets called after visiting
+ // each node in the module, so we can add the functions that we created.
+ bool isFunctionParallel() override { return false; }
+
+ TrapModePass(TrapMode mode) : mode(mode) {
+ assert(mode != TrapMode::Allow);
+ }
+
+ Pass* create() override { return new TrapModePass(mode); }
+
+ void visitUnary(Unary* curr) {
+ replaceCurrent(makeTrappingUnary(curr, *trappingFunctions));
+ }
+
+ void visitBinary(Binary* curr) {
+ replaceCurrent(makeTrappingBinary(curr, *trappingFunctions));
+ }
+
+ void visitModule(Module* curr) {
+ trappingFunctions->addToModule();
+ }
+
+ void doWalkModule(Module* module) {
+ trappingFunctions = make_unique<TrappingFunctionContainer>(mode, *module);
+ WalkerPass<PostWalker<TrapModePass>>::doWalkModule(module);
+ }
+
+private:
+ TrapMode mode;
+ // Need to defer adding generated functions because adding functions while
+ // iterating over existing functions causes problems.
+ std::unique_ptr<TrappingFunctionContainer> trappingFunctions;
+};
+
+Pass *createTrapModeClamp() {
+ return new TrapModePass(TrapMode::Clamp);
+}
+
+Pass *createTrapModeJS() {
+ return new TrapModePass(TrapMode::JS);
+}
+
+} // namespace wasm
diff --git a/src/passes/pass.cpp b/src/passes/pass.cpp
index a208a03dd..6cfe77a01 100644
--- a/src/passes/pass.cpp
+++ b/src/passes/pass.cpp
@@ -109,6 +109,8 @@ void PassRegistry::registerPasses() {
registerPass("simplify-locals-nostructure", "miscellaneous locals-related optimizations", createSimplifyLocalsNoStructurePass);
registerPass("simplify-locals-notee-nostructure", "miscellaneous locals-related optimizations", createSimplifyLocalsNoTeeNoStructurePass);
registerPass("ssa", "ssa-ify variables so that they have a single assignment", createSSAifyPass);
+ registerPass("trap-mode-clamp", "replace trapping operations with clamping semantics", createTrapModeClamp);
+ registerPass("trap-mode-js", "replace trapping operations with js semantics", createTrapModeJS);
registerPass("untee", "removes tee_locals, replacing them with sets and gets", createUnteePass);
registerPass("vacuum", "removes obviously unneeded code", createVacuumPass);
// registerPass("lower-i64", "lowers i64 into pairs of i32s", createLowerInt64Pass);
diff --git a/src/passes/passes.h b/src/passes/passes.h
index a02216083..58a9e2b27 100644
--- a/src/passes/passes.h
+++ b/src/passes/passes.h
@@ -67,6 +67,8 @@ Pass *createSimplifyLocalsNoTeePass();
Pass *createSimplifyLocalsNoStructurePass();
Pass *createSimplifyLocalsNoTeeNoStructurePass();
Pass *createSSAifyPass();
+Pass *createTrapModeClamp();
+Pass *createTrapModeJS();
Pass *createUnteePass();
Pass *createVacuumPass();
diff --git a/src/shared-constants.h b/src/shared-constants.h
index 31b5b7c18..e8f98edb7 100644
--- a/src/shared-constants.h
+++ b/src/shared-constants.h
@@ -15,6 +15,7 @@
*/
#ifndef wasm_shared_constants_h
+#define wasm_shared_constants_h
#include "wasm.h"
diff --git a/src/tools/asm2wasm.cpp b/src/tools/asm2wasm.cpp
index 3fa97d981..cbc3c486b 100644
--- a/src/tools/asm2wasm.cpp
+++ b/src/tools/asm2wasm.cpp
@@ -18,6 +18,7 @@
// asm2wasm console tool
//
+#include "ast/trapping.h"
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
@@ -34,7 +35,7 @@ using namespace wasm;
int main(int argc, const char *argv[]) {
bool legalizeJavaScriptFFI = true;
- Asm2WasmBuilder::TrapMode trapMode = Asm2WasmBuilder::TrapMode::JS;
+ TrapMode trapMode = TrapMode::JS;
bool wasmOnly = false;
std::string sourceMapFilename;
std::string sourceMapUrl;
@@ -79,19 +80,19 @@ int main(int argc, const char *argv[]) {
})
.add("--emit-potential-traps", "-i", "Emit instructions that might trap, like div/rem of 0", Options::Arguments::Zero,
[&trapMode](Options *o, const std::string &) {
- trapMode = Asm2WasmBuilder::TrapMode::Allow;
+ trapMode = TrapMode::Allow;
})
.add("--emit-clamped-potential-traps", "-i", "Clamp instructions that might trap, like float => int", Options::Arguments::Zero,
[&trapMode](Options *o, const std::string &) {
- trapMode = Asm2WasmBuilder::TrapMode::Clamp;
+ trapMode = TrapMode::Clamp;
})
.add("--emit-jsified-potential-traps", "-i", "Avoid instructions that might trap, handling them exactly like JS would", Options::Arguments::Zero,
[&trapMode](Options *o, const std::string &) {
- trapMode = Asm2WasmBuilder::TrapMode::JS;
+ trapMode = TrapMode::JS;
})
.add("--imprecise", "-i", "Imprecise optimizations (old name for --emit-potential-traps)", Options::Arguments::Zero,
[&trapMode](Options *o, const std::string &) {
- trapMode = Asm2WasmBuilder::TrapMode::Allow;
+ trapMode = TrapMode::Allow;
})
.add("--wasm-only", "-w", "Input is in WebAssembly-only format, and not actually valid asm.js", Options::Arguments::Zero,
[&wasmOnly](Options *o, const std::string &) {
diff --git a/src/tools/s2wasm.cpp b/src/tools/s2wasm.cpp
index a35783479..559188d1f 100644
--- a/src/tools/s2wasm.cpp
+++ b/src/tools/s2wasm.cpp
@@ -18,6 +18,7 @@
// wasm2asm console tool
//
+#include "ast/trapping.h"
#include "support/colors.h"
#include "support/command-line.h"
#include "support/file.h"
@@ -37,6 +38,7 @@ int main(int argc, const char *argv[]) {
bool importMemory = false;
std::string startFunction;
std::vector<std::string> archiveLibraries;
+ TrapMode trapMode = TrapMode::Allow;
Options options("s2wasm", "Link .s file into .wast");
options.extra["validate"] = "wasm";
options
@@ -81,6 +83,24 @@ int main(int argc, const char *argv[]) {
[&allowMemoryGrowth](Options *, const std::string &) {
allowMemoryGrowth = true;
})
+ .add("--emit-potential-traps", "",
+ "Emit instructions that might trap, like div/rem of 0",
+ Options::Arguments::Zero,
+ [&trapMode](Options *o, const std::string &) {
+ trapMode = TrapMode::Allow;
+ })
+ .add("--emit-clamped-potential-traps", "",
+ "Clamp instructions that might trap, like float => int",
+ Options::Arguments::Zero,
+ [&trapMode](Options *o, const std::string &) {
+ trapMode = TrapMode::Clamp;
+ })
+ .add("--emit-jsified-potential-traps", "",
+ "Avoid instructions that might trap, handling them exactly like JS would",
+ Options::Arguments::Zero,
+ [&trapMode](Options *o, const std::string &) {
+ trapMode = TrapMode::JS;
+ })
.add("--emscripten-glue", "-e", "Generate emscripten glue",
Options::Arguments::Zero,
[&generateEmscriptenGlue](Options *, const std::string &) {
@@ -144,6 +164,13 @@ int main(int argc, const char *argv[]) {
S2WasmBuilder mainbuilder(input.c_str(), options.debug);
linker.linkObject(mainbuilder);
+ if (trapMode != TrapMode::Allow) {
+ Module* wasm = &(linker.getOutput().wasm);
+ PassRunner runner(wasm);
+ addTrapModePass(runner, trapMode);
+ runner.run();
+ }
+
for (const auto& m : archiveLibraries) {
auto archiveFile(read_file<std::vector<char>>(m, Flags::Binary, debugFlag));
bool error;
diff --git a/test/dot_s/traps.clamp.wast b/test/dot_s/traps.clamp.wast
new file mode 100644
index 000000000..3cab71072
--- /dev/null
+++ b/test/dot_s/traps.clamp.wast
@@ -0,0 +1,113 @@
+(module
+ (import "env" "memory" (memory $0 1))
+ (table 0 anyfunc)
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "test_traps" (func $test_traps))
+ (func $test_traps (param $0 f32) (param $1 f64) (result i32)
+ (call $i32u-div
+ (call $f32-to-int
+ (get_local $0)
+ )
+ (call $f64-to-uint
+ (get_local $1)
+ )
+ )
+ )
+ (func $f32-to-int (param $0 f32) (result i32)
+ (if (result i32)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.ge
+ (get_local $0)
+ (f32.const 2147483648)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.le
+ (get_local $0)
+ (f32.const -2147483648)
+ )
+ (i32.const -2147483648)
+ (i32.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $i32u-div (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.div_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $stackSave (result i32)
+ (i32.load offset=4
+ (i32.const 0)
+ )
+ )
+ (func $stackAlloc (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_local $1
+ (i32.load offset=4
+ (i32.const 0)
+ )
+ )
+ (i32.store offset=4
+ (i32.const 0)
+ (i32.and
+ (i32.sub
+ (get_local $1)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (param $0 i32)
+ (i32.store offset=4
+ (i32.const 0)
+ (get_local $0)
+ )
+ )
+)
+;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] }
diff --git a/test/dot_s/traps.js.wast b/test/dot_s/traps.js.wast
new file mode 100644
index 000000000..39d08f4c0
--- /dev/null
+++ b/test/dot_s/traps.js.wast
@@ -0,0 +1,65 @@
+(module
+ (type $FUNCSIG$id (func (param f64) (result i32)))
+ (import "asm2wasm" "f64-to-int" (func $f64-to-int (param f64) (result i32)))
+ (import "env" "memory" (memory $0 1))
+ (table 0 anyfunc)
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "test_traps" (func $test_traps))
+ (func $test_traps (param $0 f32) (param $1 f64) (result i32)
+ (call $i32u-div
+ (call $f64-to-int
+ (f64.promote/f32
+ (get_local $0)
+ )
+ )
+ (call $f64-to-int
+ (get_local $1)
+ )
+ )
+ )
+ (func $i32u-div (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.div_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $stackSave (result i32)
+ (i32.load offset=4
+ (i32.const 0)
+ )
+ )
+ (func $stackAlloc (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_local $1
+ (i32.load offset=4
+ (i32.const 0)
+ )
+ )
+ (i32.store offset=4
+ (i32.const 0)
+ (i32.and
+ (i32.sub
+ (get_local $1)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (param $0 i32)
+ (i32.store offset=4
+ (i32.const 0)
+ (get_local $0)
+ )
+ )
+)
+;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] }
diff --git a/test/dot_s/traps.s b/test/dot_s/traps.s
new file mode 100644
index 000000000..b75cf8d00
--- /dev/null
+++ b/test/dot_s/traps.s
@@ -0,0 +1,14 @@
+ .text
+ .file ""
+ .globl test_traps
+ .type test_traps,@function
+test_traps:
+ .param f32, f64
+ .result i32
+# BB#0:
+ i32.trunc_s/f32 $push0=, $0
+ i32.trunc_u/f64 $push1=, $1
+ i32.div_u $push2=, $pop0, $pop1
+ .endfunc
+.Lfunc_end0:
+ .size test_traps, .Lfunc_end0-test_traps
diff --git a/test/dot_s/traps.wast b/test/dot_s/traps.wast
new file mode 100644
index 000000000..2ade5362a
--- /dev/null
+++ b/test/dot_s/traps.wast
@@ -0,0 +1,49 @@
+(module
+ (import "env" "memory" (memory $0 1))
+ (table 0 anyfunc)
+ (export "stackSave" (func $stackSave))
+ (export "stackAlloc" (func $stackAlloc))
+ (export "stackRestore" (func $stackRestore))
+ (export "test_traps" (func $test_traps))
+ (func $test_traps (param $0 f32) (param $1 f64) (result i32)
+ (i32.div_u
+ (i32.trunc_s/f32
+ (get_local $0)
+ )
+ (i32.trunc_u/f64
+ (get_local $1)
+ )
+ )
+ )
+ (func $stackSave (result i32)
+ (i32.load offset=4
+ (i32.const 0)
+ )
+ )
+ (func $stackAlloc (param $0 i32) (result i32)
+ (local $1 i32)
+ (set_local $1
+ (i32.load offset=4
+ (i32.const 0)
+ )
+ )
+ (i32.store offset=4
+ (i32.const 0)
+ (i32.and
+ (i32.sub
+ (get_local $1)
+ (get_local $0)
+ )
+ (i32.const -16)
+ )
+ )
+ (get_local $1)
+ )
+ (func $stackRestore (param $0 i32)
+ (i32.store offset=4
+ (i32.const 0)
+ (get_local $0)
+ )
+ )
+)
+;; METADATA: { "asmConsts": {},"staticBump": 12, "initializers": [] }
diff --git a/test/emcc_hello_world.fromasm.clamp b/test/emcc_hello_world.fromasm.clamp
index b074afa30..e483ed89c 100644
--- a/test/emcc_hello_world.fromasm.clamp
+++ b/test/emcc_hello_world.fromasm.clamp
@@ -2214,6 +2214,32 @@
)
)
)
+ (func $f64-to-uint (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $i32s-div (param $0 i32) (param $1 i32) (result i32)
(if (result i32)
(get_local $1)
@@ -4587,7 +4613,7 @@
(i32.store
(get_local $7)
(tee_local $5
- (call $f64-to-int
+ (call $f64-to-uint
(get_local $15)
)
)
diff --git a/test/emcc_hello_world.fromasm.clamp.no-opts b/test/emcc_hello_world.fromasm.clamp.no-opts
index 3662e5d36..36d34018c 100644
--- a/test/emcc_hello_world.fromasm.clamp.no-opts
+++ b/test/emcc_hello_world.fromasm.clamp.no-opts
@@ -4658,6 +4658,32 @@
)
)
)
+ (func $f64-to-uint (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $i32s-div (param $0 i32) (param $1 i32) (result i32)
(if (result i32)
(i32.eqz
@@ -9934,7 +9960,7 @@
(loop $while-in60
(block $while-out59
(set_local $$conv216$i
- (call $f64-to-int
+ (call $f64-to-uint
(get_local $$y$addr$4$i)
)
)
diff --git a/test/passes/trap-mode-clamp.txt b/test/passes/trap-mode-clamp.txt
new file mode 100644
index 000000000..91ba19203
--- /dev/null
+++ b/test/passes/trap-mode-clamp.txt
@@ -0,0 +1,432 @@
+(module
+ (type $0 (func (param i32 i64)))
+ (type $1 (func (param f32)))
+ (type $2 (func (param f64)))
+ (memory $0 0)
+ (func $test_div (type $0) (param $0 i32) (param $1 i64)
+ (drop
+ (call $i32s-div
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i32u-div
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i64s-div
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ (drop
+ (call $i64u-div
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ )
+ (func $test_rem (type $0) (param $0 i32) (param $1 i64)
+ (drop
+ (call $i32s-rem
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i32u-rem
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i64s-rem
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ (drop
+ (call $i64u-rem
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ )
+ (func $test_f32_to_int (type $1) (param $0 f32)
+ (drop
+ (call $f32-to-int
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f32-to-uint
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f32-to-int64
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f32-to-uint64
+ (get_local $0)
+ )
+ )
+ )
+ (func $test_f64_to_int (type $2) (param $0 f64)
+ (drop
+ (call $f64-to-int
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f64-to-uint
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f64-to-int64
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f64-to-uint64
+ (get_local $0)
+ )
+ )
+ )
+ (func $f32-to-int (param $0 f32) (result i32)
+ (if (result i32)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.ge
+ (get_local $0)
+ (f32.const 2147483648)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.le
+ (get_local $0)
+ (f32.const -2147483648)
+ )
+ (i32.const -2147483648)
+ (i32.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f32-to-int64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f32-to-uint (param $0 f32) (result i32)
+ (if (result i32)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f32.ge
+ (get_local $0)
+ (f32.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f32-to-uint64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-int (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 2147483648)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -2147483649)
+ )
+ (i32.const -2147483648)
+ (i32.trunc_s/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-int64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $i32s-div (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (i32.and
+ (i32.eq
+ (get_local $0)
+ (i32.const -2147483648)
+ )
+ (i32.eq
+ (get_local $1)
+ (i32.const -1)
+ )
+ )
+ (i32.const 0)
+ (i32.div_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $i32s-rem (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.rem_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i32u-div (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.div_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i32u-rem (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.rem_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i64s-div (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (i32.and
+ (i64.eq
+ (get_local $0)
+ (i64.const -9223372036854775808)
+ )
+ (i64.eq
+ (get_local $1)
+ (i64.const -1)
+ )
+ )
+ (i64.const 0)
+ (i64.div_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $i64s-rem (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.rem_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i64u-div (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.div_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i64u-rem (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.rem_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+)
diff --git a/test/passes/trap-mode-clamp.wast b/test/passes/trap-mode-clamp.wast
new file mode 100644
index 000000000..5a44e50ec
--- /dev/null
+++ b/test/passes/trap-mode-clamp.wast
@@ -0,0 +1,26 @@
+(module
+ (func $test_div (param $0 i32) (param $1 i64)
+ (drop (i32.div_s (get_local $0) (get_local $0)))
+ (drop (i32.div_u (get_local $0) (get_local $0)))
+ (drop (i64.div_s (get_local $1) (get_local $1)))
+ (drop (i64.div_u (get_local $1) (get_local $1)))
+ )
+ (func $test_rem (param $0 i32) (param $1 i64)
+ (drop (i32.rem_s (get_local $0) (get_local $0)))
+ (drop (i32.rem_u (get_local $0) (get_local $0)))
+ (drop (i64.rem_s (get_local $1) (get_local $1)))
+ (drop (i64.rem_u (get_local $1) (get_local $1)))
+ )
+ (func $test_f32_to_int (param $0 f32)
+ (drop (i32.trunc_s/f32 (get_local $0)))
+ (drop (i32.trunc_u/f32 (get_local $0)))
+ (drop (i64.trunc_s/f32 (get_local $0)))
+ (drop (i64.trunc_u/f32 (get_local $0)))
+ )
+ (func $test_f64_to_int (param $0 f64)
+ (drop (i32.trunc_s/f64 (get_local $0)))
+ (drop (i32.trunc_u/f64 (get_local $0)))
+ (drop (i64.trunc_s/f64 (get_local $0)))
+ (drop (i64.trunc_u/f64 (get_local $0)))
+ )
+)
diff --git a/test/passes/trap-mode-js.txt b/test/passes/trap-mode-js.txt
new file mode 100644
index 000000000..57ef09f91
--- /dev/null
+++ b/test/passes/trap-mode-js.txt
@@ -0,0 +1,334 @@
+(module
+ (type $0 (func (param i32 i64)))
+ (type $1 (func (param f32)))
+ (type $2 (func (param f64)))
+ (type $FUNCSIG$id (func (param f64) (result i32)))
+ (import "asm2wasm" "f64-to-int" (func $f64-to-int (param f64) (result i32)))
+ (memory $0 0)
+ (func $test_div (type $0) (param $0 i32) (param $1 i64)
+ (drop
+ (call $i32s-div
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i32u-div
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i64s-div
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ (drop
+ (call $i64u-div
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ )
+ (func $test_rem (type $0) (param $0 i32) (param $1 i64)
+ (drop
+ (call $i32s-rem
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i32u-rem
+ (get_local $0)
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $i64s-rem
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ (drop
+ (call $i64u-rem
+ (get_local $1)
+ (get_local $1)
+ )
+ )
+ )
+ (func $test_f32_to_int (type $1) (param $0 f32)
+ (drop
+ (call $f64-to-int
+ (f64.promote/f32
+ (get_local $0)
+ )
+ )
+ )
+ (drop
+ (call $f64-to-int
+ (f64.promote/f32
+ (get_local $0)
+ )
+ )
+ )
+ (drop
+ (call $f32-to-int64
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f32-to-uint64
+ (get_local $0)
+ )
+ )
+ )
+ (func $test_f64_to_int (type $2) (param $0 f64)
+ (drop
+ (call $f64-to-int
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f64-to-int
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f64-to-int64
+ (get_local $0)
+ )
+ )
+ (drop
+ (call $f64-to-uint64
+ (get_local $0)
+ )
+ )
+ )
+ (func $f32-to-int64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f32-to-uint64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-int64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $i32s-div (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (i32.and
+ (i32.eq
+ (get_local $0)
+ (i32.const -2147483648)
+ )
+ (i32.eq
+ (get_local $1)
+ (i32.const -1)
+ )
+ )
+ (i32.const 0)
+ (i32.div_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $i32s-rem (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.rem_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i32u-div (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.div_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i32u-rem (param $0 i32) (param $1 i32) (result i32)
+ (if (result i32)
+ (i32.eqz
+ (get_local $1)
+ )
+ (i32.const 0)
+ (i32.rem_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i64s-div (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (i32.and
+ (i64.eq
+ (get_local $0)
+ (i64.const -9223372036854775808)
+ )
+ (i64.eq
+ (get_local $1)
+ (i64.const -1)
+ )
+ )
+ (i64.const 0)
+ (i64.div_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ )
+ (func $i64s-rem (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.rem_s
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i64u-div (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.div_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+ (func $i64u-rem (param $0 i64) (param $1 i64) (result i64)
+ (if (result i64)
+ (i64.eqz
+ (get_local $1)
+ )
+ (i64.const 0)
+ (i64.rem_u
+ (get_local $0)
+ (get_local $1)
+ )
+ )
+ )
+)
diff --git a/test/passes/trap-mode-js.wast b/test/passes/trap-mode-js.wast
new file mode 100644
index 000000000..5a44e50ec
--- /dev/null
+++ b/test/passes/trap-mode-js.wast
@@ -0,0 +1,26 @@
+(module
+ (func $test_div (param $0 i32) (param $1 i64)
+ (drop (i32.div_s (get_local $0) (get_local $0)))
+ (drop (i32.div_u (get_local $0) (get_local $0)))
+ (drop (i64.div_s (get_local $1) (get_local $1)))
+ (drop (i64.div_u (get_local $1) (get_local $1)))
+ )
+ (func $test_rem (param $0 i32) (param $1 i64)
+ (drop (i32.rem_s (get_local $0) (get_local $0)))
+ (drop (i32.rem_u (get_local $0) (get_local $0)))
+ (drop (i64.rem_s (get_local $1) (get_local $1)))
+ (drop (i64.rem_u (get_local $1) (get_local $1)))
+ )
+ (func $test_f32_to_int (param $0 f32)
+ (drop (i32.trunc_s/f32 (get_local $0)))
+ (drop (i32.trunc_u/f32 (get_local $0)))
+ (drop (i64.trunc_s/f32 (get_local $0)))
+ (drop (i64.trunc_u/f32 (get_local $0)))
+ )
+ (func $test_f64_to_int (param $0 f64)
+ (drop (i32.trunc_s/f64 (get_local $0)))
+ (drop (i32.trunc_u/f64 (get_local $0)))
+ (drop (i64.trunc_s/f64 (get_local $0)))
+ (drop (i64.trunc_u/f64 (get_local $0)))
+ )
+)
diff --git a/test/unit.fromasm.clamp b/test/unit.fromasm.clamp
index 4393c362c..c4f133481 100644
--- a/test/unit.fromasm.clamp
+++ b/test/unit.fromasm.clamp
@@ -168,6 +168,32 @@
)
)
)
+ (func $f32-to-int (param $0 f32) (result i32)
+ (if (result i32)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.ge
+ (get_local $0)
+ (f32.const 2147483648)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.le
+ (get_local $0)
+ (f32.const -2147483648)
+ )
+ (i32.const -2147483648)
+ (i32.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $conversions (param $0 i32) (param $1 f64) (param $2 f32)
(drop
(call $f64-to-int
@@ -175,10 +201,8 @@
)
)
(drop
- (call $f64-to-int
- (f64.promote/f32
- (get_local $2)
- )
+ (call $f32-to-int
+ (get_local $2)
)
)
)
@@ -1186,9 +1210,30 @@
)
)
)
- (func $f2u (param $0 f64) (result i32)
- (call $f64-to-int
- (get_local $0)
+ (func $f64-to-uint (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
)
)
(func $keepAlive
@@ -1203,12 +1248,12 @@
)
)
(drop
- (call $f2u
+ (call $f64-to-uint
(f64.const 100)
)
)
(drop
- (call $f2u
+ (call $f64-to-int
(f64.const 100)
)
)
diff --git a/test/unit.fromasm.clamp.no-opts b/test/unit.fromasm.clamp.no-opts
index 00b1814c4..b1b22cf66 100644
--- a/test/unit.fromasm.clamp.no-opts
+++ b/test/unit.fromasm.clamp.no-opts
@@ -234,6 +234,32 @@
)
)
)
+ (func $f32-to-int (param $0 f32) (result i32)
+ (if (result i32)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.ge
+ (get_local $0)
+ (f32.const 2147483648)
+ )
+ (i32.const -2147483648)
+ (if (result i32)
+ (f32.le
+ (get_local $0)
+ (f32.const -2147483648)
+ )
+ (i32.const -2147483648)
+ (i32.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $conversions (param $i i32) (param $d f64) (param $f f32)
(set_local $i
(call $f64-to-int
@@ -241,10 +267,8 @@
)
)
(set_local $i
- (call $f64-to-int
- (f64.promote/f32
- (get_local $f)
- )
+ (call $f32-to-int
+ (get_local $f)
)
)
(set_local $d
@@ -1972,9 +1996,35 @@
)
)
)
+ (func $f64-to-uint (param $0 f64) (result i32)
+ (if (result i32)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.ge
+ (get_local $0)
+ (f64.const 4294967296)
+ )
+ (i32.const 0)
+ (if (result i32)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i32.const 0)
+ (i32.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $f2u (param $x f64) (result i32)
(return
- (call $f64-to-int
+ (call $f64-to-uint
(get_local $x)
)
)
diff --git a/test/wasm-only.fromasm b/test/wasm-only.fromasm
index 2ac96b8ae..6eb8bb5fe 100644
--- a/test/wasm-only.fromasm
+++ b/test/wasm-only.fromasm
@@ -266,6 +266,32 @@
)
)
)
+ (func $f32-to-int64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $f64-to-int64 (param $0 f64) (result i64)
(if (result i64)
(f64.ne
@@ -292,6 +318,58 @@
)
)
)
+ (func $f32-to-uint64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $test64
(local $0 i64)
(local $1 f32)
@@ -376,15 +454,13 @@
(get_local $0)
)
(drop
- (call $f64-to-int64
- (f64.promote/f32
- (tee_local $1
- (f32.convert_u/i64
- (tee_local $0
- (i64.extend_u/i32
- (i32.wrap/i64
- (get_local $0)
- )
+ (call $f32-to-int64
+ (tee_local $1
+ (f32.convert_u/i64
+ (tee_local $0
+ (i64.extend_u/i32
+ (i32.wrap/i64
+ (get_local $0)
)
)
)
@@ -402,14 +478,12 @@
)
)
(drop
- (call $f64-to-int64
- (f64.promote/f32
- (get_local $1)
- )
+ (call $f32-to-uint64
+ (get_local $1)
)
)
(drop
- (call $f64-to-int64
+ (call $f64-to-uint64
(get_local $2)
)
)
diff --git a/test/wasm-only.fromasm.clamp b/test/wasm-only.fromasm.clamp
index 2ac96b8ae..6eb8bb5fe 100644
--- a/test/wasm-only.fromasm.clamp
+++ b/test/wasm-only.fromasm.clamp
@@ -266,6 +266,32 @@
)
)
)
+ (func $f32-to-int64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $f64-to-int64 (param $0 f64) (result i64)
(if (result i64)
(f64.ne
@@ -292,6 +318,58 @@
)
)
)
+ (func $f32-to-uint64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $test64
(local $0 i64)
(local $1 f32)
@@ -376,15 +454,13 @@
(get_local $0)
)
(drop
- (call $f64-to-int64
- (f64.promote/f32
- (tee_local $1
- (f32.convert_u/i64
- (tee_local $0
- (i64.extend_u/i32
- (i32.wrap/i64
- (get_local $0)
- )
+ (call $f32-to-int64
+ (tee_local $1
+ (f32.convert_u/i64
+ (tee_local $0
+ (i64.extend_u/i32
+ (i32.wrap/i64
+ (get_local $0)
)
)
)
@@ -402,14 +478,12 @@
)
)
(drop
- (call $f64-to-int64
- (f64.promote/f32
- (get_local $1)
- )
+ (call $f32-to-uint64
+ (get_local $1)
)
)
(drop
- (call $f64-to-int64
+ (call $f64-to-uint64
(get_local $2)
)
)
diff --git a/test/wasm-only.fromasm.clamp.no-opts b/test/wasm-only.fromasm.clamp.no-opts
index c0cfa4676..bbb32c5f3 100644
--- a/test/wasm-only.fromasm.clamp.no-opts
+++ b/test/wasm-only.fromasm.clamp.no-opts
@@ -343,6 +343,32 @@
)
)
)
+ (func $f32-to-int64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $f64-to-int64 (param $0 f64) (result i64)
(if (result i64)
(f64.ne
@@ -369,6 +395,58 @@
)
)
)
+ (func $f32-to-uint64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $test64
(local $x i64)
(local $y i64)
@@ -601,10 +679,8 @@
)
)
(set_local $x
- (call $f64-to-int64
- (f64.promote/f32
- (get_local $float32)
- )
+ (call $f32-to-int64
+ (get_local $float32)
)
)
(set_local $x
@@ -613,14 +689,12 @@
)
)
(set_local $x
- (call $f64-to-int64
- (f64.promote/f32
- (get_local $float32)
- )
+ (call $f32-to-uint64
+ (get_local $float32)
)
)
(set_local $x
- (call $f64-to-int64
+ (call $f64-to-uint64
(get_local $float64)
)
)
diff --git a/test/wasm-only.fromasm.no-opts b/test/wasm-only.fromasm.no-opts
index c0cfa4676..bbb32c5f3 100644
--- a/test/wasm-only.fromasm.no-opts
+++ b/test/wasm-only.fromasm.no-opts
@@ -343,6 +343,32 @@
)
)
)
+ (func $f32-to-int64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -9223372036854775808)
+ )
+ (i64.const -9223372036854775808)
+ (i64.trunc_s/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $f64-to-int64 (param $0 f64) (result i64)
(if (result i64)
(f64.ne
@@ -369,6 +395,58 @@
)
)
)
+ (func $f32-to-uint64 (param $0 f32) (result i64)
+ (if (result i64)
+ (f32.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.ge
+ (get_local $0)
+ (f32.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f32.le
+ (get_local $0)
+ (f32.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f32
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
+ (func $f64-to-uint64 (param $0 f64) (result i64)
+ (if (result i64)
+ (f64.ne
+ (get_local $0)
+ (get_local $0)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.ge
+ (get_local $0)
+ (f64.const 18446744073709551615)
+ )
+ (i64.const 0)
+ (if (result i64)
+ (f64.le
+ (get_local $0)
+ (f64.const -1)
+ )
+ (i64.const 0)
+ (i64.trunc_u/f64
+ (get_local $0)
+ )
+ )
+ )
+ )
+ )
(func $test64
(local $x i64)
(local $y i64)
@@ -601,10 +679,8 @@
)
)
(set_local $x
- (call $f64-to-int64
- (f64.promote/f32
- (get_local $float32)
- )
+ (call $f32-to-int64
+ (get_local $float32)
)
)
(set_local $x
@@ -613,14 +689,12 @@
)
)
(set_local $x
- (call $f64-to-int64
- (f64.promote/f32
- (get_local $float32)
- )
+ (call $f32-to-uint64
+ (get_local $float32)
)
)
(set_local $x
- (call $f64-to-int64
+ (call $f64-to-uint64
(get_local $float64)
)
)