You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Float32x4 value discrepancy between ReleaseX64 AOT and other modes #39551
The divergence seems to be caused by .reciprocal(). var3 = Float32x4.splat(0.3) works fine but var3 = Float32x4.splat(0.3).reciprocal() causes a divergence.
After a quick glance, you can also get the same result as the second (faulty) code snippet with this one, which adds the intermediate print via a local variable instead of via the static field:
import'dart:typed_data';
Float32x4 var3;
main () {
final tmp =Float32x4.splat(0.1502790812204874);
print(tmp);
var3 =-tmp.reciprocal();
print(var3);
}
Given that, the difference is whether the reciprocal is recognized and replaced with a StaticCall to a recognized function, or whether it is kept as a PolymorphicInstanceCall. Looking at the recognized version of Float32x4Reciprocal now.
So the recognized version of reciprocal turns into a rcpps call in assembly, whereas the unrecognized method is basically NativeCall(Float32x4_reciprocal), so a call to the C implementation in runtime/lib/simd128.cc.
Martin mentions #37335, which is a difference when running on 32 vs. 64 bit, and was the reason for adding the --no-fp flag (c2af59f) when comparing 32-bit vs. 64-bit modes.
In this case, this is the same word-size architecture, it's just a case where we use SIMD instructions vs. falling back onto a C implementation of the same, and the SIMD instructions are not semantically equivalent. That is, here we may have a permissible difference due to floating point size, and the problem is that we're requiring strict (output) equality for floating point values instead of approximate equality.
One possibility is to try running with both --enable-simd-inline=true (the default) and --enable-simd-inline=false when FP instructions are enabled and make sure that the false version precisely matches, and allow a reasonable amount of difference for any floating point outputs on the SIMD version (instead of strict textual equality). Going to assign to Ben to consider this and whether this or a different option is the right path going forward.
I'm not surprised that we're seeing these differences if we're running through different code paths. Ideally we'll want to allow for some delta when we compare floats in the final results but we'll need to figure out how to best do that since we're doing a naive diff at the moment. Thanks for investigating @sstrickl
and @mkustermann!
The Wasm SIMD API surface is meant to be deterministic across platforms. Our reciprocal and reciprocalSqrt APIs are not. They are more efficient than the naive implementations, but at the cost of determinism.
For non-deterministic SIMD operations that trade determinism for performance we have the Relaxed SIMD API surface. However, they neither include a relaxed reciprocal nor a reciprocalSqrt. The discussion for that can be found here: WebAssembly/relaxed-simd#4
I think we have the following options here:
We do nothing and accept non-determinism in our SIMD APIs.
=> leads to inconsistent and confusing behavior as captured by this issue
We make reciprocal and reciprocalSqrt deterministic.
=> it looks like we couldn't be more efficient than what a user can write so having a dedicated API for it wouldn't carry its weight.
We deprecate and remove those APIs.
I'm in favor of deprecating and removing those APIs and moving the discussion of how to expose those more efficient non-deterministic intrinsics/operations into the future if/when we eventually discuss how to expose the relaxed SIMD API surface. @lrhn@mkustermann thoughts?
Activity
fizaaluthra commentedon Nov 27, 2019
The divergence seems to be caused by
.reciprocal().var3 = Float32x4.splat(0.3)works fine butvar3 = Float32x4.splat(0.3).reciprocal()causes a divergence.fizaaluthra commentedon Nov 27, 2019
Another one:
Reduced to the following code snippet:
Difference in output:
sstrickl commentedon Dec 16, 2019
While looking into this, I found an intra-architecture difference as well. Take this code, which adds a print between the
splatandreciprocalcalls:and the code from the bug report, with extra parentheses elided (so no semantic changes):
The diff when running on
ReleaseX64in AOT mode:This difference does not occur in JIT mode, so am now looking at the difference in AOT code generation on x64 for these two snippets.
sstrickl commentedon Dec 16, 2019
After a quick glance, you can also get the same result as the second (faulty) code snippet with this one, which adds the intermediate print via a local variable instead of via the static field:
sstrickl commentedon Dec 16, 2019
Given that, the difference is whether the
reciprocalis recognized and replaced with aStaticCallto a recognized function, or whether it is kept as aPolymorphicInstanceCall. Looking at the recognized version ofFloat32x4Reciprocalnow.sstrickl commentedon Dec 16, 2019
So the recognized version of
reciprocalturns into arcppscall in assembly, whereas the unrecognized method is basicallyNativeCall(Float32x4_reciprocal), so a call to the C implementation inruntime/lib/simd128.cc.mkustermann commentedon Dec 16, 2019
produces
with only one inversion
So the SIMD instructions simply perform the reciprocal with lower precision
sstrickl commentedon Dec 16, 2019
Martin mentions #37335, which is a difference when running on 32 vs. 64 bit, and was the reason for adding the
--no-fpflag (c2af59f) when comparing 32-bit vs. 64-bit modes.In this case, this is the same word-size architecture, it's just a case where we use SIMD instructions vs. falling back onto a C implementation of the same, and the SIMD instructions are not semantically equivalent. That is, here we may have a permissible difference due to floating point size, and the problem is that we're requiring strict (output) equality for floating point values instead of approximate equality.
One possibility is to try running with both --enable-simd-inline=true (the default) and --enable-simd-inline=false when FP instructions are enabled and make sure that the false version precisely matches, and allow a reasonable amount of difference for any floating point outputs on the SIMD version (instead of strict textual equality). Going to assign to Ben to consider this and whether this or a different option is the right path going forward.
bkonyi commentedon Dec 26, 2019
I'm not surprised that we're seeing these differences if we're running through different code paths. Ideally we'll want to allow for some delta when we compare floats in the final results but we'll need to figure out how to best do that since we're doing a naive diff at the moment. Thanks for investigating @sstrickl
and @mkustermann!
modulovalue commentedon Aug 22, 2026
The Wasm SIMD API surface is meant to be deterministic across platforms. Our reciprocal and reciprocalSqrt APIs are not. They are more efficient than the naive implementations, but at the cost of determinism.
For non-deterministic SIMD operations that trade determinism for performance we have the Relaxed SIMD API surface. However, they neither include a relaxed reciprocal nor a reciprocalSqrt. The discussion for that can be found here: WebAssembly/relaxed-simd#4
I think we have the following options here:
=> leads to inconsistent and confusing behavior as captured by this issue
=> it looks like we couldn't be more efficient than what a user can write so having a dedicated API for it wouldn't carry its weight.
I'm in favor of deprecating and removing those APIs and moving the discussion of how to expose those more efficient non-deterministic intrinsics/operations into the future if/when we eventually discuss how to expose the relaxed SIMD API surface. @lrhn @mkustermann thoughts?
modulovalue commentedon Sep 9, 2026
Looks like the divergence goes further than just precision. I'll look into this as part of #64248.
reciprocalSqrt(-0.0) returns nan unoptimized and -infinity optimized.