LibWasm: Return canonical NaN for min/max/floor/ceil/truncate operations

Instead of returning whichever argument was NaN, return the canonical
NaN instead. The spec allows the old behavior:

  "Following the recommendation that operators propagate NaN payloads
   from their operands is permitted but not required."

But Chrome, Firefox and Safari do not propagate the operand payloads.

Fixes 448 WPT subtests in `wasm/core`.

Co-authored-by: Ali Mohammad Pur <ali.mpfard@gmail.com>
This commit is contained in:
Jelle Raaijmakers 2025-07-27 01:01:36 +02:00 committed by Ali Mohammad Pur
parent a6857a6ce1
commit ed94381209
2 changed files with 22 additions and 14 deletions

View File

@ -8,6 +8,7 @@
#include <AK/BitCast.h>
#include <AK/BuiltinWrappers.h>
#include <AK/Math.h>
#include <AK/Result.h>
#include <AK/SIMD.h>
#include <AK/SIMDExtras.h>
@ -395,12 +396,10 @@ struct Minimum {
auto operator()(Lhs lhs, Rhs rhs) const
{
if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
if (isnan(lhs) || isnan(rhs)) {
return isnan(lhs) ? lhs : rhs;
}
if (lhs == 0 && rhs == 0) {
if (isnan(lhs) || isnan(rhs))
return AK::NaN<Lhs>;
if (lhs == 0 && rhs == 0)
return signbit(lhs) ? lhs : rhs;
}
}
return min(lhs, rhs);
}
@ -413,12 +412,10 @@ struct Maximum {
auto operator()(Lhs lhs, Rhs rhs) const
{
if constexpr (IsFloatingPoint<Lhs> || IsFloatingPoint<Rhs>) {
if (isnan(lhs) || isnan(rhs)) {
return isnan(lhs) ? lhs : rhs;
}
if (lhs == 0 && rhs == 0) {
if (isnan(lhs) || isnan(rhs))
return AK::NaN<Lhs>;
if (lhs == 0 && rhs == 0)
return signbit(lhs) ? rhs : lhs;
}
}
return max(lhs, rhs);
}
@ -555,6 +552,9 @@ struct Ceil {
template<typename Lhs>
auto operator()(Lhs lhs) const
{
if (isnan(lhs))
return AK::NaN<Lhs>;
if constexpr (IsSame<Lhs, float>)
return ceilf(lhs);
else if constexpr (IsSame<Lhs, double>)
@ -923,6 +923,9 @@ struct Floor {
template<typename Lhs>
auto operator()(Lhs lhs) const
{
if (isnan(lhs))
return AK::NaN<Lhs>;
if constexpr (IsSame<Lhs, float>)
return floorf(lhs);
else if constexpr (IsSame<Lhs, double>)
@ -938,6 +941,9 @@ struct Truncate {
template<typename Lhs>
auto operator()(Lhs lhs) const
{
if (isnan(lhs))
return AK::NaN<Lhs>;
if constexpr (IsSame<Lhs, float>)
return truncf(lhs);
else if constexpr (IsSame<Lhs, double>)

View File

@ -224,14 +224,16 @@ TESTJS_GLOBAL_FUNCTION(is_canonical_nan64, isCanonicalNaN64)
TESTJS_GLOBAL_FUNCTION(is_arithmetic_nan32, isArithmeticNaN32)
{
auto value = bit_cast<float>(TRY(vm.argument(0).to_u32(vm)));
return isnan(value);
auto const bits = TRY(vm.argument(0).to_u32(vm));
auto const payload = bits & 0x007FFFFF;
return (bits & 0x7F800000) == 0x7F800000 && payload >= 0x00400000;
}
TESTJS_GLOBAL_FUNCTION(is_arithmetic_nan64, isArithmeticNaN64)
{
auto value = bit_cast<double>(TRY(vm.argument(0).to_bigint_uint64(vm)));
return isnan(value);
auto const bits = TRY(vm.argument(0).to_bigint_uint64(vm));
auto const payload = bits & 0x000FFFFFFFFFFFFFULL;
return (bits & 0x7FF0000000000000ull) == 0x7FF0000000000000ull && payload >= 0x0008000000000000ull;
}
TESTJS_GLOBAL_FUNCTION(test_simd_vector, testSIMDVector)