Implement the relaxed SIMD proposal (#5892)
* Initial support for the Relaxed SIMD proposal This commit adds initial scaffolding and support for the Relaxed SIMD proposal for WebAssembly. Codegen support is supported on the x64 and AArch64 backends on this time. The purpose of this commit is to get all the boilerplate out of the way in terms of plumbing through a new feature, adding tests, etc. The tests are copied from the upstream repository at this time while the WebAssembly/testsuite repository hasn't been updated. A summary of changes made in this commit are: * Lowerings for all relaxed simd opcodes have been added, currently all exhibiting deterministic behavior. This means that few lowerings are optimal on the x86 backend, but on the AArch64 backend, for example, all lowerings should be optimal. * Support is added to codegen to, eventually, conditionally generate different code based on input codegen flags. This is intended to enable codegen to more efficient instructions on x86 by default, for example, while still allowing embedders to force architecture-independent semantics and behavior. One good example of this is the `f32x4.relaxed_fmadd` instruction which when deterministic forces the `fma` instruction, but otherwise if the backend doesn't have support for `fma` then intermediate operations are performed instead. * Lowerings of `iadd_pairwise` for `i16x8` and `i32x4` were added to the x86 backend as they're now exercised by the deterministic lowerings of relaxed simd instructions. * Sample codegen tests for added for x86 and aarch64 for some relaxed simd instructions. * Wasmtime embedder support for the relaxed-simd proposal and forcing determinism have been added to `Config` and the CLI. * Support has been added to the `*.wast` runtime execution for the `(either ...)` matcher used in the relaxed-simd proposal. * Tests for relaxed-simd are run both with a default `Engine` as well as a "force deterministic" `Engine` to test both configurations. * All tests from the upstream repository were copied into Wasmtime. These tests should be deleted when WebAssembly/testsuite is updated. * x64: Add x86-specific lowerings for relaxed simd This commit builds on the prior commit and adds an array of `x86_*` instructions to Cranelift which have semantics that match their corresponding x86 equivalents. Translation for relaxed simd is then additionally updated to conditionally generate different CLIF for relaxed simd instructions depending on whether the target is x86 or not. This means that for AArch64 no changes are made but for x86 most relaxed instructions now lower to some x86-equivalent with slightly different semantics than the "deterministic" lowering. * Add libcall support for fma to Wasmtime This will be required to implement the `f32x4.relaxed_madd` instruction (and others) when an x86 host doesn't specify the `has_fma` feature. * Ignore relaxed-simd tests on s390x and riscv64 * Enable relaxed-simd tests on s390x * Update cranelift/codegen/meta/src/shared/instructions.rs Co-authored-by: Andrew Brown <andrew.brown@intel.com> * Add a FIXME from review * Add notes about deterministic semantics * Don't default `has_native_fma` to `true` * Review comments and rebase fixes --------- Co-authored-by: Andrew Brown <andrew.brown@intel.com>
This commit is contained in:
@@ -30,6 +30,7 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
|
||||
let multi_memory = feature_found(wast, "multi-memory");
|
||||
let threads = feature_found(wast, "threads");
|
||||
let reference_types = !(threads && feature_found(wast, "proposals"));
|
||||
let relaxed_simd = feature_found(wast, "relaxed-simd");
|
||||
let use_shared_memory = feature_found_src(&wast_bytes, "shared_memory")
|
||||
|| feature_found_src(&wast_bytes, "shared)");
|
||||
|
||||
@@ -43,6 +44,7 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
|
||||
.wasm_threads(threads)
|
||||
.wasm_memory64(memory64)
|
||||
.wasm_reference_types(reference_types)
|
||||
.wasm_relaxed_simd(relaxed_simd)
|
||||
.cranelift_debug_verifier(true);
|
||||
|
||||
cfg.wasm_component_model(feature_found(wast, "component-model"));
|
||||
@@ -108,11 +110,26 @@ fn run_wast(wast: &str, strategy: Strategy, pooling: bool) -> anyhow::Result<()>
|
||||
None
|
||||
};
|
||||
|
||||
let store = Store::new(&Engine::new(&cfg)?, ());
|
||||
let mut wast_context = WastContext::new(store);
|
||||
let mut engines = vec![(Engine::new(&cfg)?, "default")];
|
||||
|
||||
wast_context.register_spectest(use_shared_memory)?;
|
||||
wast_context.run_buffer(wast.to_str().unwrap(), &wast_bytes)?;
|
||||
// For tests that use relaxed-simd test both the default engine and the
|
||||
// guaranteed-deterministic engine to ensure that both the 'native'
|
||||
// semantics of the instructions plus the canonical semantics work.
|
||||
if relaxed_simd {
|
||||
engines.push((
|
||||
Engine::new(cfg.relaxed_simd_deterministic(true))?,
|
||||
"deterministic",
|
||||
));
|
||||
}
|
||||
|
||||
for (engine, desc) in engines {
|
||||
let store = Store::new(&engine, ());
|
||||
let mut wast_context = WastContext::new(store);
|
||||
wast_context.register_spectest(use_shared_memory)?;
|
||||
wast_context
|
||||
.run_buffer(wast.to_str().unwrap(), &wast_bytes)
|
||||
.with_context(|| format!("failed to run spec test with {desc} engine"))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
;; Tests for i16x8.relaxed_q15mulr_s.
|
||||
(module
|
||||
(func (export "i16x8.relaxed_q15mulr_s") (param v128 v128) (result v128) (i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1)))
|
||||
|
||||
(func (export "i16x8.relaxed_q15mulr_s_cmp") (param v128 v128) (result v128)
|
||||
(i16x8.eq
|
||||
(i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))
|
||||
(i16x8.relaxed_q15mulr_s (local.get 0) (local.get 1))))
|
||||
)
|
||||
|
||||
;; INT16_MIN = -32768
|
||||
(assert_return (invoke "i16x8.relaxed_q15mulr_s"
|
||||
(v128.const i16x8 -32768 -32767 32767 0 0 0 0 0)
|
||||
(v128.const i16x8 -32768 -32768 32767 0 0 0 0 0))
|
||||
;; overflows, return either INT16_MIN or INT16_MAX
|
||||
(either (v128.const i16x8 -32768 32767 32766 0 0 0 0 0)
|
||||
(v128.const i16x8 32767 32767 32766 0 0 0 0 0)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
(assert_return (invoke "i16x8.relaxed_q15mulr_s_cmp"
|
||||
(v128.const i16x8 -32768 -32767 32767 0 0 0 0 0)
|
||||
(v128.const i16x8 -32768 -32768 32767 0 0 0 0 0))
|
||||
;; overflows, return either INT16_MIN or INT16_MAX
|
||||
(v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
|
||||
123
tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast
Normal file
123
tests/misc_testsuite/relaxed-simd/i32x4_relaxed_trunc.wast
Normal file
@@ -0,0 +1,123 @@
|
||||
;; Tests for i32x4.relaxed_trunc_f32x4_s, i32x4.relaxed_trunc_f32x4_u, i32x4.relaxed_trunc_f64x2_s_zero, and i32x4.relaxed_trunc_f64x2_u_zero.
|
||||
|
||||
(module
|
||||
(func (export "i32x4.relaxed_trunc_f32x4_s") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_s (local.get 0)))
|
||||
(func (export "i32x4.relaxed_trunc_f32x4_u") (param v128) (result v128) (i32x4.relaxed_trunc_f32x4_u (local.get 0)))
|
||||
(func (export "i32x4.relaxed_trunc_f64x2_s_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_s_zero (local.get 0)))
|
||||
(func (export "i32x4.relaxed_trunc_f64x2_u_zero") (param v128) (result v128) (i32x4.relaxed_trunc_f64x2_u_zero (local.get 0)))
|
||||
|
||||
(func (export "i32x4.relaxed_trunc_f32x4_s_cmp") (param v128) (result v128)
|
||||
(i32x4.eq
|
||||
(i32x4.relaxed_trunc_f32x4_s (local.get 0))
|
||||
(i32x4.relaxed_trunc_f32x4_s (local.get 0))))
|
||||
(func (export "i32x4.relaxed_trunc_f32x4_u_cmp") (param v128) (result v128)
|
||||
(i32x4.eq
|
||||
(i32x4.relaxed_trunc_f32x4_u (local.get 0))
|
||||
(i32x4.relaxed_trunc_f32x4_u (local.get 0))))
|
||||
(func (export "i32x4.relaxed_trunc_f64x2_s_zero_cmp") (param v128) (result v128)
|
||||
(i32x4.eq
|
||||
(i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))
|
||||
(i32x4.relaxed_trunc_f64x2_s_zero (local.get 0))))
|
||||
(func (export "i32x4.relaxed_trunc_f64x2_u_zero_cmp") (param v128) (result v128)
|
||||
(i32x4.eq
|
||||
(i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))
|
||||
(i32x4.relaxed_trunc_f64x2_u_zero (local.get 0))))
|
||||
)
|
||||
|
||||
;; Test some edge cases around min/max to ensure that the instruction either
|
||||
;; saturates correctly or returns INT_MIN.
|
||||
;;
|
||||
;; Note, though, that INT_MAX itself is not tested. The value for INT_MAX is
|
||||
;; 2147483647 but that is not representable in a `f32` since it requires 31 bits
|
||||
;; when a f32 has only 24 bits available. This means that the closest integers
|
||||
;; to INT_MAX which can be represented are 2147483520 and 2147483648, meaning
|
||||
;; that the INT_MAX test case cannot be tested.
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
|
||||
;; INT32_MIN <INT32_MIN >INT32_MAX
|
||||
(v128.const f32x4 -2147483648.0 -2147483904.0 2.0 2147483904.0))
|
||||
;; out of range -> saturate or INT32_MIN
|
||||
(either (v128.const i32x4 -2147483648 -2147483648 2 2147483647)
|
||||
(v128.const i32x4 -2147483648 -2147483648 2 -2147483648)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s"
|
||||
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
|
||||
;; nans -> 0 or INT32_MIN
|
||||
(either (v128.const i32x4 0 0 0 0)
|
||||
(v128.const i32x4 0x80000000 0x80000000 0x80000000 0x80000000)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
|
||||
;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
|
||||
(v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
|
||||
;; out of range -> saturate or UINT32_MAX
|
||||
(either (v128.const i32x4 0 0 4294967040 0xffffffff)
|
||||
(v128.const i32x4 0 0xffffffff 4294967040 0xffffffff)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u"
|
||||
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
|
||||
;; nans -> 0 or UINT32_MAX
|
||||
(either (v128.const i32x4 0 0 0 0)
|
||||
(v128.const i32x4 0xffffffff 0xffffffff 0xffffffff 0xffffffff)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
|
||||
(v128.const f64x2 -2147483904.0 2147483904.0))
|
||||
;; out of range -> saturate or INT32_MIN
|
||||
(either (v128.const i32x4 -2147483648 2147483647 0 0)
|
||||
(v128.const i32x4 -2147483648 -2147483648 0 0)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero"
|
||||
(v128.const f64x2 nan -nan))
|
||||
(either (v128.const i32x4 0 0 0 0)
|
||||
(v128.const i32x4 0x80000000 0x80000000 0 0)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
|
||||
(v128.const f64x2 -1.0 4294967296.0))
|
||||
;; out of range -> saturate or UINT32_MAX
|
||||
(either (v128.const i32x4 0 0xffffffff 0 0)
|
||||
(v128.const i32x4 0xffffffff 0xffffffff 0 0)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero"
|
||||
(v128.const f64x2 nan -nan))
|
||||
(either (v128.const i32x4 0 0 0 0)
|
||||
(v128.const i32x4 0 0 0xffffffff 0xffffffff)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
|
||||
;; INT32_MIN <INT32_MIN INT32_MAX >INT32_MAX
|
||||
(v128.const f32x4 -2147483648.0 -2147483904.0 2147483647.0 2147483904.0))
|
||||
;; out of range -> saturate or INT32_MIN
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_s_cmp"
|
||||
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
|
||||
;; nans -> 0 or INT32_MIN
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
|
||||
;; UINT32_MIN UINT32_MIN-1 <UINT32_MAX UINT32_MAX+1
|
||||
(v128.const f32x4 0 -1.0 4294967040.0 4294967296.0))
|
||||
;; out of range -> saturate or UINT32_MAX
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f32x4_u_cmp"
|
||||
(v128.const f32x4 nan -nan nan:0x444444 -nan:0x444444))
|
||||
;; nans -> 0 or UINT32_MAX
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
|
||||
(v128.const f64x2 -2147483904.0 2147483904.0))
|
||||
;; out of range -> saturate or INT32_MIN
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_s_zero_cmp"
|
||||
(v128.const f64x2 nan -nan))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
|
||||
(v128.const f64x2 -1.0 4294967296.0))
|
||||
;; out of range -> saturate or UINT32_MAX
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_trunc_f64x2_u_zero_cmp"
|
||||
(v128.const f64x2 nan -nan))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
44
tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast
Normal file
44
tests/misc_testsuite/relaxed-simd/i8x16_relaxed_swizzle.wast
Normal file
@@ -0,0 +1,44 @@
|
||||
;; Tests for relaxed i8x16 swizzle.
|
||||
|
||||
(module
|
||||
(func (export "i8x16.relaxed_swizzle") (param v128 v128) (result v128) (i8x16.relaxed_swizzle (local.get 0) (local.get 1)))
|
||||
|
||||
(func (export "i8x16.relaxed_swizzle_cmp") (param v128 v128) (result v128)
|
||||
(i8x16.eq
|
||||
(i8x16.relaxed_swizzle (local.get 0) (local.get 1))
|
||||
(i8x16.relaxed_swizzle (local.get 0) (local.get 1))))
|
||||
)
|
||||
|
||||
(assert_return (invoke "i8x16.relaxed_swizzle"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))
|
||||
(either (v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
|
||||
|
||||
;; out of range, returns 0 or modulo 15 if < 128
|
||||
(assert_return (invoke "i8x16.relaxed_swizzle"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31))
|
||||
(either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
|
||||
|
||||
;; out of range, returns 0 if >= 128
|
||||
(assert_return (invoke "i8x16.relaxed_swizzle"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255))
|
||||
(either (v128.const i8x16 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
;; out of range, returns 0 or modulo 15 if < 128
|
||||
(assert_return (invoke "i8x16.relaxed_swizzle_cmp"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31))
|
||||
(v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
|
||||
;; out of range, returns 0 if >= 128
|
||||
(assert_return (invoke "i8x16.relaxed_swizzle_cmp"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 128 129 130 131 132 133 134 135 248 249 250 251 252 253 254 255))
|
||||
(v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
106
tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast
Normal file
106
tests/misc_testsuite/relaxed-simd/relaxed_dot_product.wast
Normal file
@@ -0,0 +1,106 @@
|
||||
;; Tests for relaxed dot products.
|
||||
|
||||
(module
|
||||
(func (export "i16x8.relaxed_dot_i8x16_i7x16_s") (param v128 v128) (result v128) (i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1)))
|
||||
(func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s") (param v128 v128 v128) (result v128) (i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2)))
|
||||
|
||||
(func (export "i16x8.relaxed_dot_i8x16_i7x16_s_cmp") (param v128 v128) (result v128)
|
||||
(i16x8.eq
|
||||
(i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))
|
||||
(i16x8.relaxed_dot_i8x16_i7x16_s (local.get 0) (local.get 1))))
|
||||
(func (export "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp") (param v128 v128 v128) (result v128)
|
||||
(i16x8.eq
|
||||
(i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))
|
||||
(i32x4.relaxed_dot_i8x16_i7x16_add_s (local.get 0) (local.get 1) (local.get 2))))
|
||||
)
|
||||
|
||||
;; Simple values to ensure things are functional.
|
||||
(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15))
|
||||
(v128.const i16x8 1 13 41 85 145 221 313 421))
|
||||
|
||||
;; Test max and min i8 values;
|
||||
(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
|
||||
(v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0))
|
||||
(v128.const i16x8 -32512 32258 0 0 0 0 0 0))
|
||||
|
||||
;; signed * unsigned : -128 * 129 * 2 = -33,024 saturated to -32,768
|
||||
;; signed * signed : -128 * -127 * 2 = 32,512
|
||||
;; unsigned * unsigned : 128 * 129 * 2 = 33,024
|
||||
(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s"
|
||||
(v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
|
||||
(either
|
||||
(v128.const i16x8 -32768 0 0 0 0 0 0 0)
|
||||
(v128.const i16x8 32512 0 0 0 0 0 0 0)
|
||||
(v128.const i16x8 33024 0 0 0 0 0 0 0)))
|
||||
|
||||
;; Simple values to ensure things are functional.
|
||||
(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i32x4 0 1 2 3))
|
||||
;; intermediate result is [14, 126, 366, 734]
|
||||
(v128.const i32x4 14 127 368 737))
|
||||
|
||||
;; Test max and min i8 values;
|
||||
(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
|
||||
(v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0)
|
||||
(v128.const i32x4 1 2 3 4))
|
||||
;; intermediate result is [-65024, 64516, 0, 0]
|
||||
(v128.const i32x4 -65023 64518 3 4))
|
||||
|
||||
;; signed * unsigned : -128 * 129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI
|
||||
;; signed * unsigned with intermediate saturation :
|
||||
;; (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW)
|
||||
;; -32768 + -32768 = -65536 (+ 1)
|
||||
;; signed * signed : -128 * -127 * 4 = 65,024 (+ 1)
|
||||
;; unsigned * unsigned : 128 * 129 * 2 = 66,048 (+ 1)
|
||||
(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s"
|
||||
(v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i32x4 1 2 3 4))
|
||||
(either
|
||||
(v128.const i32x4 -66047 2 3 4)
|
||||
(v128.const i32x4 -65535 2 3 4)
|
||||
(v128.const i32x4 65025 2 3 4)
|
||||
(v128.const i32x4 66049 2 3 4)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
;; Test max and min i8 values;
|
||||
(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp"
|
||||
(v128.const i8x16 -128 -128 127 127 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 127 127 127 127 0 0 0 0 0 0 0 0 0 0 0 0))
|
||||
(v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
|
||||
;; Test max and min i8 values;
|
||||
(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp"
|
||||
(v128.const i8x16 -128 -128 -128 -128 127 127 127 127 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 127 127 127 127 127 127 127 127 0 0 0 0 0 0 0 0)
|
||||
(v128.const i32x4 1 2 3 4))
|
||||
;; intermediate result is [-65024, 64516, 0, 0]
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
;; signed * unsigned : -128 * 129 * 2 = -33,024 saturated to -32,768
|
||||
;; signed * signed : -128 * -127 * 2 = 32,512
|
||||
;; unsigned * unsigned : 128 * 129 * 2 = 33,024
|
||||
(assert_return (invoke "i16x8.relaxed_dot_i8x16_i7x16_s_cmp"
|
||||
(v128.const i8x16 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0 0 0))
|
||||
(v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
|
||||
;; signed * unsigned : -128 * 129 * 4 = -66,048 (+ 1) VPDPBUSD AVX2-VNNI or AVX512-VNNI
|
||||
;; signed * unsigned with intermediate saturation :
|
||||
;; (-128 * 129) + (-128 * 129) = -33024 saturated to -32768 (PMADDUBSW)
|
||||
;; -32768 + -32768 = -65536 (+ 1)
|
||||
;; signed * signed : -128 * -127 * 4 = 65,024 (+ 1)
|
||||
;; unsigned * unsigned : 128 * 129 * 2 = 66,048 (+ 1)
|
||||
(assert_return (invoke "i32x4.relaxed_dot_i8x16_i7x16_add_s_cmp"
|
||||
(v128.const i8x16 -128 -128 -128 -128 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i8x16 -127 -127 -127 -127 0 0 0 0 0 0 0 0 0 0 0 0)
|
||||
(v128.const i32x4 1 2 3 4))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
92
tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast
Normal file
92
tests/misc_testsuite/relaxed-simd/relaxed_laneselect.wast
Normal file
@@ -0,0 +1,92 @@
|
||||
;; Tests for i8x16.relaxed_laneselect, i16x8.relaxed_laneselect, i32x4.relaxed_laneselect, and i64x2.relaxed_laneselect.
|
||||
|
||||
(module
|
||||
(func (export "i8x16.relaxed_laneselect") (param v128 v128 v128) (result v128) (i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
|
||||
(func (export "i16x8.relaxed_laneselect") (param v128 v128 v128) (result v128) (i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
|
||||
(func (export "i32x4.relaxed_laneselect") (param v128 v128 v128) (result v128) (i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
|
||||
(func (export "i64x2.relaxed_laneselect") (param v128 v128 v128) (result v128) (i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2)))
|
||||
|
||||
(func (export "i8x16.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
|
||||
(i8x16.eq
|
||||
(i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
|
||||
(i8x16.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
|
||||
(func (export "i16x8.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
|
||||
(i16x8.eq
|
||||
(i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
|
||||
(i16x8.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
|
||||
(func (export "i32x4.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
|
||||
(i32x4.eq
|
||||
(i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
|
||||
(i32x4.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
|
||||
(func (export "i64x2.relaxed_laneselect_cmp") (param v128 v128 v128) (result v128)
|
||||
(i64x2.eq
|
||||
(i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))
|
||||
(i64x2.relaxed_laneselect (local.get 0) (local.get 1) (local.get 2))))
|
||||
)
|
||||
|
||||
(assert_return (invoke "i8x16.relaxed_laneselect"
|
||||
(v128.const i8x16 0 1 0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 16 17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31)
|
||||
(v128.const i8x16 0xff 0 0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0))
|
||||
(either (v128.const i8x16 0 17 0x14 0x32 20 21 22 23 24 25 26 27 28 29 30 31)
|
||||
(v128.const i8x16 0 17 0x12 0x34 20 21 22 23 24 25 26 27 28 29 30 31)))
|
||||
|
||||
(assert_return (invoke "i16x8.relaxed_laneselect"
|
||||
(v128.const i16x8 0 1 0x1234 0x1234 4 5 6 7)
|
||||
(v128.const i16x8 8 9 0x5678 0x5678 12 13 14 15)
|
||||
(v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0))
|
||||
(either (v128.const i16x8 0 9 0x1278 0x5634 12 13 14 15)
|
||||
(v128.const i16x8 0 9 0x1234 0x5678 12 13 14 15)))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_laneselect"
|
||||
(v128.const i32x4 0 1 0x12341234 0x12341234)
|
||||
(v128.const i32x4 4 5 0x56785678 0x56785678)
|
||||
(v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff))
|
||||
(either (v128.const i32x4 0 5 0x12345678 0x56781234)
|
||||
(v128.const i32x4 0 5 0x12341234 0x56785678)))
|
||||
|
||||
(assert_return (invoke "i64x2.relaxed_laneselect"
|
||||
(v128.const i64x2 0 1)
|
||||
(v128.const i64x2 2 3)
|
||||
(v128.const i64x2 0xffffffffffffffff 0))
|
||||
(either (v128.const i64x2 0 3)
|
||||
(v128.const i64x2 0 3)))
|
||||
|
||||
(assert_return (invoke "i64x2.relaxed_laneselect"
|
||||
(v128.const i64x2 0x1234123412341234 0x1234123412341234)
|
||||
(v128.const i64x2 0x5678567856785678 0x5678567856785678)
|
||||
(v128.const i64x2 0xffffffff00000000 0x00000000ffffffff))
|
||||
(either (v128.const i64x2 0x1234123456785678 0x5678567812341234)
|
||||
(v128.const i64x2 0x1234123412341234 0x5678567856785678)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
(assert_return (invoke "i8x16.relaxed_laneselect_cmp"
|
||||
(v128.const i8x16 0 1 0x12 0x12 4 5 6 7 8 9 10 11 12 13 14 15)
|
||||
(v128.const i8x16 16 17 0x34 0x34 20 21 22 23 24 25 26 27 28 29 30 31)
|
||||
(v128.const i8x16 0xff 0 0xf0 0x0f 0 0 0 0 0 0 0 0 0 0 0 0))
|
||||
(v128.const i8x16 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i16x8.relaxed_laneselect_cmp"
|
||||
(v128.const i16x8 0 1 0x1234 0x1234 4 5 6 7)
|
||||
(v128.const i16x8 8 9 0x5678 0x5678 12 13 14 15)
|
||||
(v128.const i16x8 0xffff 0 0xff00 0x00ff 0 0 0 0))
|
||||
(v128.const i16x8 -1 -1 -1 -1 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i32x4.relaxed_laneselect_cmp"
|
||||
(v128.const i32x4 0 1 0x12341234 0x12341234)
|
||||
(v128.const i32x4 4 5 0x56785678 0x56785678)
|
||||
(v128.const i32x4 0xffffffff 0 0xffff0000 0x0000ffff))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "i64x2.relaxed_laneselect_cmp"
|
||||
(v128.const i64x2 0 1)
|
||||
(v128.const i64x2 2 3)
|
||||
(v128.const i64x2 0xffffffffffffffff 0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "i64x2.relaxed_laneselect_cmp"
|
||||
(v128.const i64x2 0x1234123412341234 0x1234123412341234)
|
||||
(v128.const i64x2 0x5678567856785678 0x5678567856785678)
|
||||
(v128.const i64x2 0xffffffff00000000 0x00000000ffffffff))
|
||||
(v128.const i64x2 -1 -1))
|
||||
190
tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast
Normal file
190
tests/misc_testsuite/relaxed-simd/relaxed_madd_nmadd.wast
Normal file
@@ -0,0 +1,190 @@
|
||||
;; Tests for f32x4.relaxed_madd, f32x4.relaxed_nmadd, f64x2.relaxed_madd, and f64x2.relaxed_nmadd.
|
||||
|
||||
(module
|
||||
(func (export "f32x4.relaxed_madd") (param v128 v128 v128) (result v128) (f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))
|
||||
(func (export "f32x4.relaxed_nmadd") (param v128 v128 v128) (result v128) (f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))
|
||||
(func (export "f64x2.relaxed_nmadd") (param v128 v128 v128) (result v128) (f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2)))
|
||||
(func (export "f64x2.relaxed_madd") (param v128 v128 v128) (result v128) (f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2)))
|
||||
|
||||
(func (export "f32x4.relaxed_madd_cmp") (param v128 v128 v128) (result v128)
|
||||
(f32x4.eq
|
||||
(f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))
|
||||
(f32x4.relaxed_madd (local.get 0) (local.get 1) (local.get 2))))
|
||||
(func (export "f32x4.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128)
|
||||
(f32x4.eq
|
||||
(f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))
|
||||
(f32x4.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))))
|
||||
(func (export "f64x2.relaxed_nmadd_cmp") (param v128 v128 v128) (result v128)
|
||||
(f64x2.eq
|
||||
(f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))
|
||||
(f64x2.relaxed_nmadd (local.get 0) (local.get 1) (local.get 2))))
|
||||
(func (export "f64x2.relaxed_madd_cmp") (param v128 v128 v128) (result v128)
|
||||
(f64x2.eq
|
||||
(f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))
|
||||
(f64x2.relaxed_madd (local.get 0) (local.get 1) (local.get 2))))
|
||||
)
|
||||
|
||||
|
||||
;; FLT_MAX == 0x1.fffffep+127
|
||||
;; FLT_MAX * 2 - FLT_MAX ==
|
||||
;; FLT_MAX (if fma)
|
||||
;; 0 (if no fma)
|
||||
;; from https://www.vinc17.net/software/fma-tests.c
|
||||
(assert_return (invoke "f32x4.relaxed_madd"
|
||||
(v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 )
|
||||
(v128.const f32x4 2.0 2.0 2.0 2.0)
|
||||
(v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127))
|
||||
(either (v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127)
|
||||
(v128.const f32x4 inf inf inf inf)))
|
||||
|
||||
;; Special values for float:
|
||||
;; x = 0x1.000004p+0 (1 + 2^-22)
|
||||
;; y = 0x1.0002p+0 (1 + 2^-15)
|
||||
;; z = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0)
|
||||
;; = -0x1.000204p+0
|
||||
;; x.y = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit)
|
||||
;; x.y+z = 0 (2 roundings)
|
||||
;; fma(x, y, z) = (0x1p-37) 2^-37
|
||||
;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
|
||||
(assert_return (invoke "f32x4.relaxed_madd"
|
||||
(v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
|
||||
(v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
|
||||
(v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
|
||||
(either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
|
||||
(v128.const f32x4 0 0 0 0)))
|
||||
;; fnma tests with negated x, same answers are expected.
|
||||
(assert_return (invoke "f32x4.relaxed_nmadd"
|
||||
(v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0)
|
||||
(v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
|
||||
(v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
|
||||
(either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
|
||||
(v128.const f32x4 0 0 0 0)))
|
||||
;; fnma tests with negated y, same answers are expected.
|
||||
(assert_return (invoke "f32x4.relaxed_nmadd"
|
||||
(v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
|
||||
(v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0)
|
||||
(v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
|
||||
(either (v128.const f32x4 0x1p-37 0x1p-37 0x1p-37 0x1p-37)
|
||||
(v128.const f32x4 0 0 0 0)))
|
||||
|
||||
;; DBL_MAX = 0x1.fffffffffffffp+1023
|
||||
;; DLB_MAX * 2 - DLB_MAX ==
|
||||
;; DLB_MAX (if fma)
|
||||
;; 0 (if no fma)
|
||||
;; form https://www.vinc17.net/software/fma-tests.c
|
||||
;; from https://www.vinc17.net/software/fma-tests.c
|
||||
(assert_return (invoke "f64x2.relaxed_madd"
|
||||
(v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
|
||||
(v128.const f64x2 2.0 2.0)
|
||||
(v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023))
|
||||
(either (v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
|
||||
(v128.const f64x2 inf inf)))
|
||||
|
||||
;; Special values for double:
|
||||
;; x = 0x1.00000004p+0 (1 + 2^-30)
|
||||
;; y = 0x1.000002p+0 (1 + 2^-23)
|
||||
;; z = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0)
|
||||
;; = -0x1.00000204p+0
|
||||
;; x.y = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit)
|
||||
;; x.y+z = 0 (2 roundings)
|
||||
;; fma(x, y, z) = 0x1p-53
|
||||
;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
|
||||
(assert_return (invoke "f64x2.relaxed_madd"
|
||||
(v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
|
||||
(v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
|
||||
(v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
|
||||
(either (v128.const f64x2 0x1p-53 0x1p-53)
|
||||
(v128.const f64x2 0 0)))
|
||||
;; fnma tests with negated x, same answers are expected.
|
||||
(assert_return (invoke "f64x2.relaxed_nmadd"
|
||||
(v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0)
|
||||
(v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
|
||||
(v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
|
||||
(either (v128.const f64x2 0x1p-53 0x1p-53)
|
||||
(v128.const f64x2 0 0)))
|
||||
;; fnma tests with negated y, same answers are expected.
|
||||
(assert_return (invoke "f64x2.relaxed_nmadd"
|
||||
(v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
|
||||
(v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0)
|
||||
(v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
|
||||
(either (v128.const f64x2 0x1p-53 0x1p-53)
|
||||
(v128.const f64x2 0 0)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
;; FLT_MAX == 0x1.fffffep+127
|
||||
;; FLT_MAX * 2 - FLT_MAX ==
|
||||
;; FLT_MAX (if fma)
|
||||
;; 0 (if no fma)
|
||||
;; from https://www.vinc17.net/software/fma-tests.c
|
||||
(assert_return (invoke "f32x4.relaxed_madd_cmp"
|
||||
(v128.const f32x4 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 0x1.fffffep+127 )
|
||||
(v128.const f32x4 2.0 2.0 2.0 2.0)
|
||||
(v128.const f32x4 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127 -0x1.fffffep+127))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
;; Special values for float:
|
||||
;; x = 0x1.000004p+0 (1 + 2^-22)
|
||||
;; y = 0x1.0002p+0 (1 + 2^-15)
|
||||
;; z = -(1.0 + 0x0.0002p+0 + 0x0.000004p+0)
|
||||
;; = -0x1.000204p+0
|
||||
;; x.y = 1.0 + 0x0.0002p+0 + 0x0.000004p+0 + 0x1p-37 (round bit)
|
||||
;; x.y+z = 0 (2 roundings)
|
||||
;; fma(x, y, z) = (0x1p-37) 2^-37
|
||||
;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
|
||||
(assert_return (invoke "f32x4.relaxed_madd_cmp"
|
||||
(v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
|
||||
(v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
|
||||
(v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
;; fnma tests with negated x, same answers are expected.
|
||||
(assert_return (invoke "f32x4.relaxed_nmadd_cmp"
|
||||
(v128.const f32x4 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0 -0x1.000004p+0)
|
||||
(v128.const f32x4 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0 0x1.0002p+0)
|
||||
(v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
;; fnma tests with negated y, same answers are expected.
|
||||
(assert_return (invoke "f32x4.relaxed_nmadd_cmp"
|
||||
(v128.const f32x4 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0 0x1.000004p+0)
|
||||
(v128.const f32x4 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0 -0x1.0002p+0)
|
||||
(v128.const f32x4 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0 -0x1.000204p+0))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
;; DBL_MAX = 0x1.fffffffffffffp+1023
|
||||
;; DLB_MAX * 2 - DLB_MAX ==
|
||||
;; DLB_MAX (if fma)
|
||||
;; 0 (if no fma)
|
||||
;; form https://www.vinc17.net/software/fma-tests.c
|
||||
;; from https://www.vinc17.net/software/fma-tests.c
|
||||
(assert_return (invoke "f64x2.relaxed_madd_cmp"
|
||||
(v128.const f64x2 0x1.fffffffffffffp+1023 0x1.fffffffffffffp+1023)
|
||||
(v128.const f64x2 2.0 2.0)
|
||||
(v128.const f64x2 -0x1.fffffffffffffp+1023 -0x1.fffffffffffffp+1023))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
;; Special values for double:
|
||||
;; x = 0x1.00000004p+0 (1 + 2^-30)
|
||||
;; y = 0x1.000002p+0 (1 + 2^-23)
|
||||
;; z = -(1.0 + 0x0.000002p+0 + 0x0.00000004p+0)
|
||||
;; = -0x1.00000204p+0
|
||||
;; x.y = 1.0 + 0x0.000002p+0 + 0x0.00000004p+0 + 0x1p-53 (round bit)
|
||||
;; x.y+z = 0 (2 roundings)
|
||||
;; fma(x, y, z) = 0x1p-53
|
||||
;; from https://accurate-algorithms.readthedocs.io/en/latest/ch09appendix.html#test-system-information
|
||||
(assert_return (invoke "f64x2.relaxed_madd_cmp"
|
||||
(v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
|
||||
(v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
|
||||
(v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
;; fnma tests with negated x, same answers are expected.
|
||||
(assert_return (invoke "f64x2.relaxed_nmadd_cmp"
|
||||
(v128.const f64x2 -0x1.00000004p+0 -0x1.00000004p+0)
|
||||
(v128.const f64x2 0x1.000002p+0 0x1.000002p+0)
|
||||
(v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
;; fnma tests with negated y, same answers are expected.
|
||||
(assert_return (invoke "f64x2.relaxed_nmadd_cmp"
|
||||
(v128.const f64x2 0x1.00000004p+0 0x1.00000004p+0)
|
||||
(v128.const f64x2 -0x1.000002p+0 -0x1.000002p+0)
|
||||
(v128.const f64x2 -0x1.00000204p+0 -0x1.00000204p+0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
183
tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast
Normal file
183
tests/misc_testsuite/relaxed-simd/relaxed_min_max.wast
Normal file
@@ -0,0 +1,183 @@
|
||||
;; Tests for f32x4.min, f32x4.max, f64x2.min, and f64x2.max.
|
||||
|
||||
(module
|
||||
(func (export "f32x4.relaxed_min") (param v128 v128) (result v128) (f32x4.relaxed_min (local.get 0) (local.get 1)))
|
||||
(func (export "f32x4.relaxed_max") (param v128 v128) (result v128) (f32x4.relaxed_max (local.get 0) (local.get 1)))
|
||||
(func (export "f64x2.relaxed_min") (param v128 v128) (result v128) (f64x2.relaxed_min (local.get 0) (local.get 1)))
|
||||
(func (export "f64x2.relaxed_max") (param v128 v128) (result v128) (f64x2.relaxed_max (local.get 0) (local.get 1)))
|
||||
|
||||
(func (export "f32x4.relaxed_min_cmp") (param v128 v128) (result v128)
|
||||
(i32x4.eq
|
||||
(f32x4.relaxed_min (local.get 0) (local.get 1))
|
||||
(f32x4.relaxed_min (local.get 0) (local.get 1))))
|
||||
(func (export "f32x4.relaxed_max_cmp") (param v128 v128) (result v128)
|
||||
(i32x4.eq
|
||||
(f32x4.relaxed_max (local.get 0) (local.get 1))
|
||||
(f32x4.relaxed_max (local.get 0) (local.get 1))))
|
||||
(func (export "f64x2.relaxed_min_cmp") (param v128 v128) (result v128)
|
||||
(i64x2.eq
|
||||
(f64x2.relaxed_min (local.get 0) (local.get 1))
|
||||
(f64x2.relaxed_min (local.get 0) (local.get 1))))
|
||||
(func (export "f64x2.relaxed_max_cmp") (param v128 v128) (result v128)
|
||||
(i64x2.eq
|
||||
(f64x2.relaxed_max (local.get 0) (local.get 1))
|
||||
(f64x2.relaxed_max (local.get 0) (local.get 1))))
|
||||
)
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_min"
|
||||
(v128.const f32x4 -nan nan 0 0)
|
||||
(v128.const f32x4 0 0 -nan nan))
|
||||
(either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)
|
||||
(v128.const f32x4 nan:canonical nan:canonical 0 0)
|
||||
(v128.const f32x4 0 0 nan:canonical nan:canonical)
|
||||
(v128.const f32x4 0 0 0 0)))
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_min"
|
||||
(v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
|
||||
(either (v128.const f32x4 -0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 +0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 -0.0 +0.0 -0.0)))
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_max"
|
||||
(v128.const f32x4 -nan nan 0 0)
|
||||
(v128.const f32x4 0 0 -nan nan))
|
||||
(either (v128.const f32x4 nan:canonical nan:canonical nan:canonical nan:canonical)
|
||||
(v128.const f32x4 nan:canonical nan:canonical 0 0)
|
||||
(v128.const f32x4 0 0 nan:canonical nan:canonical)
|
||||
(v128.const f32x4 0 0 0 0)))
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_max"
|
||||
(v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
|
||||
(either (v128.const f32x4 +0.0 +0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 +0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 -0.0 +0.0 -0.0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min"
|
||||
(v128.const f64x2 -nan nan)
|
||||
(v128.const f64x2 0 0))
|
||||
(either (v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 0 0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min"
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 -nan nan))
|
||||
(either (v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 0 0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 -0.0 +0.0))
|
||||
(either (v128.const f64x2 -0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 -0.0 +0.0)
|
||||
(v128.const f64x2 -0.0 -0.0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0))
|
||||
(either (v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max"
|
||||
(v128.const f64x2 -nan nan)
|
||||
(v128.const f64x2 0 0))
|
||||
(either (v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 0 0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max"
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 -nan nan))
|
||||
(either (v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 nan:canonical nan:canonical)
|
||||
(v128.const f64x2 0 0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 -0.0 +0.0))
|
||||
(either (v128.const f64x2 +0.0 +0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 -0.0 +0.0)
|
||||
(v128.const f64x2 -0.0 -0.0)))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0))
|
||||
(either (v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0)))
|
||||
|
||||
;; Check that multiple calls to the relaxed instruction with same inputs returns same results.
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_min_cmp"
|
||||
(v128.const f32x4 -nan nan 0 0)
|
||||
(v128.const f32x4 0 0 -nan nan))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_min_cmp"
|
||||
(v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_max_cmp"
|
||||
(v128.const f32x4 -nan nan 0 0)
|
||||
(v128.const f32x4 0 0 -nan nan))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "f32x4.relaxed_max_cmp"
|
||||
(v128.const f32x4 +0.0 -0.0 +0.0 -0.0)
|
||||
(v128.const f32x4 -0.0 +0.0 +0.0 -0.0))
|
||||
(v128.const i32x4 -1 -1 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min_cmp"
|
||||
(v128.const f64x2 -nan nan)
|
||||
(v128.const f64x2 0 0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min_cmp"
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 -nan nan))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min_cmp"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 -0.0 +0.0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_min_cmp"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max_cmp"
|
||||
(v128.const f64x2 -nan nan)
|
||||
(v128.const f64x2 0 0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max_cmp"
|
||||
(v128.const f64x2 0 0)
|
||||
(v128.const f64x2 -nan nan))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max_cmp"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 -0.0 +0.0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
|
||||
(assert_return (invoke "f64x2.relaxed_max_cmp"
|
||||
(v128.const f64x2 +0.0 -0.0)
|
||||
(v128.const f64x2 +0.0 -0.0))
|
||||
(v128.const i64x2 -1 -1))
|
||||
Reference in New Issue
Block a user