From a53643889dcb5be24b965af8fb0461fac3045344 Mon Sep 17 00:00:00 2001 From: YAMAMOTO Takashi Date: Mon, 5 Jun 2023 22:14:44 +0900 Subject: [PATCH] workaround x87 issues --- cmake/ToywasmConfig.cmake | 6 ++++++ lib/endian.c | 2 ++ lib/endian.h | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/cmake/ToywasmConfig.cmake b/cmake/ToywasmConfig.cmake index e7ed3514..a618f59f 100644 --- a/cmake/ToywasmConfig.cmake +++ b/cmake/ToywasmConfig.cmake @@ -61,6 +61,12 @@ endif() endif() endif() +if(TRIPLET MATCHES "i386") +# x87 doesn't preserve sNaN as IEEE 754 and wasm expect. +# unfortunately, clang doesn't have -mno-fp-ret-in-387. +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2 -mfpmath=sse") +endif() + if(CMAKE_C_COMPILER_TARGET MATCHES "wasm") if(TOYWASM_USE_TAILCALL) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mtail-call") diff --git a/lib/endian.c b/lib/endian.c index 65405fbe..88d5c1ea 100644 --- a/lib/endian.c +++ b/lib/endian.c @@ -179,6 +179,7 @@ lef64_encode(void *p, double v) le64_encode(p, u.i); } +#if !defined(__i386__) float lef32_decode(const void *p) { @@ -200,3 +201,4 @@ lef64_decode(const void *p) u.i = le64_decode(p); return u.f; } +#endif diff --git a/lib/endian.h b/lib/endian.h index 30f0f362..946290de 100644 --- a/lib/endian.h +++ b/lib/endian.h @@ -1,3 +1,5 @@ +#if !defined(_ENDIAN_H) +#define _ENDIAN_H #include uint8_t le8_to_host(uint8_t v); @@ -23,5 +25,44 @@ uint64_t le64_decode(const void *p); void lef32_encode(void *p, float v); void lef64_encode(void *p, double v); +/* + * x87 fld/fstp does not preserve sNaN. it breaks wasm semantics. + * + * while in later processors we can use XMM registers (eg. -msse2) which + * don't have the problem, x87 ST0 register is still used to return + * float/double function results as it's specified by the i386 ABI. + * + * while GCC has -mno-fp-ret-in-387 to alter the abi, Clang unfortunately + * doesn't seem to have an equivalent. + */ +#if defined(__i386__) +__attribute__((always_inline, used)) static float +lef32_decode(const void *p) +{ + union { + uint32_t i; + float f; + } u; + u.i = le32_decode(p); + return u.f; +} +#else float lef32_decode(const void *p); +#endif + +#if defined(__i386__) +__attribute__((always_inline, used)) static double +lef64_decode(const void *p) +{ + union { + uint64_t i; + double f; + } u; + u.i = le64_decode(p); + return u.f; +} +#else double lef64_decode(const void *p); +#endif + +#endif /* !defined(_ENDIAN_H) */