diff options
author | Keith Winstein <keithw@cs.stanford.edu> | 2022-07-11 17:12:49 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-11 17:12:49 -0700 |
commit | a7d484ef3d1f64649dd9f48cb1d5434f0fda561b (patch) | |
tree | 817862cfd2bbf1ac5290b9d0a55087ad6ccf8ebc /wasm2c | |
parent | 4132e35b51849678ac9e5592089e00057f260ccf (diff) | |
download | wabt-a7d484ef3d1f64649dd9f48cb1d5434f0fda561b.tar.gz wabt-a7d484ef3d1f64649dd9f48cb1d5434f0fda561b.tar.bz2 wabt-a7d484ef3d1f64649dd9f48cb1d5434f0fda561b.zip |
wasm2c: run tests with -O2 on non-Windows (#1939)
Enable optimization when compiling the wasm2c output on non-Windows platforms (effectively GCC and clang). This required:
- Preventing load instructions from being optimized away if their value is unused (using inline assembly with an input operand and empty code). This is necessary to force an OOB trap on platforms that use mprotect and the signal handler to detect OOB.
- Disabling tail-call optimization in the compiler, to make sure that infinite recursion traps. (This required bumping the version of macOS in GitHub Actions to get a new-enough AppleClang. We should revert this back to 'macos-latest' as soon as that becomes the default.)
- Using NaN-quieting versions of a bunch of FP ops that were previously only used on Windows, and adding floor/ceil and promotion/demotion.
- Using the '-frounding-math' and '-fsignaling-nans' compiler flags to tell GCC and clang not to fold certain FP ops (e.g. subtracting zero, multiplying by 1).
Fixes #1925.
Diffstat (limited to 'wasm2c')
-rw-r--r-- | wasm2c/README.md | 13 | ||||
-rw-r--r-- | wasm2c/examples/fac/fac.c | 130 | ||||
-rw-r--r-- | wasm2c/wasm-rt-impl.c | 68 | ||||
-rw-r--r-- | wasm2c/wasm-rt.h | 16 |
4 files changed, 143 insertions, 84 deletions
diff --git a/wasm2c/README.md b/wasm2c/README.md index 6d68626e..6850b62c 100644 --- a/wasm2c/README.md +++ b/wasm2c/README.md @@ -92,6 +92,8 @@ int main(int argc, char** argv) { } ``` +## Compiling the wasm2c output + To compile the executable, we need to use `main.c` and the generated `fac.c`. We'll also include `wasm-rt-impl.c` which has implementations of the various `wasm_rt_*` functions used by `fac.c` and `fac.h`. @@ -100,6 +102,17 @@ We'll also include `wasm-rt-impl.c` which has implementations of the various $ cc -o fac main.c fac.c wasm-rt-impl.c ``` +A note on compiling with optimization: wasm2c relies on certain +behavior from the C compiler to maintain conformance with the +WebAssembly specification, especially with regards to requirements to +convert "signaling" to "quiet" floating-point NaN values and for +infinite recursion to produce a trap. When compiling with optimization +(e.g. `-O2` or `-O3`), it's necessary to disable some optimizations to +preserve conformance. With GCC 11, adding the command-line arguments +`-fno-optimize-sibling-calls -frounding-math -fsignaling-nans` appears +to be sufficient. With clang 14, just `-fno-optimize-sibling-calls +-frounding-math` appears to be sufficient. + Now let's test it out! ```sh diff --git a/wasm2c/examples/fac/fac.c b/wasm2c/examples/fac/fac.c index 30f1e9bb..106d7768 100644 --- a/wasm2c/examples/fac/fac.c +++ b/wasm2c/examples/fac/fac.c @@ -36,6 +36,12 @@ #define MEMCHECK(mem, a, t) RANGE_CHECK(mem, a, sizeof(t)) #endif +#ifdef __GNUC__ +#define wasm_asm __asm__ +#else +#define wasm_asm(X) +#endif + #if WABT_BIG_ENDIAN static inline void load_data(void* dest, const void* src, size_t n) { size_t i = 0; @@ -58,6 +64,7 @@ static inline void load_data(void* dest, const void* src, size_t n) { t1 result; \ wasm_rt_memcpy(&result, &mem->data[mem->size - addr - sizeof(t1)], \ sizeof(t1)); \ + wasm_asm("" ::"r"(result)); \ return (t3)(t2)result; \ } @@ -82,6 +89,7 @@ static inline void load_data(void* dest, const void* src, size_t n) { MEMCHECK(mem, addr, t1); \ t1 result; \ wasm_rt_memcpy(&result, &mem->data[addr], sizeof(t1)); \ + wasm_asm("" ::"r"(result)); \ return (t3)(t2)result; \ } @@ -319,6 +327,128 @@ DEFINE_REINTERPRET(i32_reinterpret_f32, f32, u32) DEFINE_REINTERPRET(f64_reinterpret_i64, u64, f64) DEFINE_REINTERPRET(i64_reinterpret_f64, f64, u64) +static float quiet_nanf(float x) { + uint32_t tmp; + memcpy(&tmp, &x, 4); + tmp |= 0x7fc00000lu; + memcpy(&x, &tmp, 4); + return x; +} + +static double quiet_nan(double x) { + uint64_t tmp; + memcpy(&tmp, &x, 8); + tmp |= 0x7ff8000000000000llu; + memcpy(&x, &tmp, 8); + return x; +} + +static double wasm_quiet(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return x; +} + +static float wasm_quietf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return x; +} + +static double wasm_floor(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return floor(x); +} + +static float wasm_floorf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return floorf(x); +} + +static double wasm_ceil(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return ceil(x); +} + +static float wasm_ceilf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return ceilf(x); +} + +static double wasm_trunc(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return trunc(x); +} + +static float wasm_truncf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return truncf(x); +} + +static float wasm_nearbyintf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return nearbyintf(x); +} + +static double wasm_nearbyint(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return nearbyint(x); +} + +static float wasm_fabsf(float x) { + if (UNLIKELY(isnan(x))) { + uint32_t tmp; + memcpy(&tmp, &x, 4); + tmp = tmp & ~(1 << 31); + memcpy(&x, &tmp, 4); + return x; + } + return fabsf(x); +} + +static double wasm_fabs(double x) { + if (UNLIKELY(isnan(x))) { + uint64_t tmp; + memcpy(&tmp, &x, 8); + tmp = tmp & ~(1ll << 63); + memcpy(&x, &tmp, 8); + return x; + } + return fabs(x); +} + +static double wasm_sqrt(double x) { + if (UNLIKELY(isnan(x))) { + return quiet_nan(x); + } + return sqrt(x); +} + +static float wasm_sqrtf(float x) { + if (UNLIKELY(isnan(x))) { + return quiet_nanf(x); + } + return sqrtf(x); +} + static u32 func_types[1]; static void init_func_types(void) { func_types[0] = wasm_rt_register_func_type(1, 1, WASM_RT_I32, WASM_RT_I32); diff --git a/wasm2c/wasm-rt-impl.c b/wasm2c/wasm-rt-impl.c index 7d70597c..14ca0e6f 100644 --- a/wasm2c/wasm-rt-impl.c +++ b/wasm2c/wasm-rt-impl.c @@ -290,74 +290,6 @@ void wasm_rt_free_memory(wasm_rt_memory_t* memory) { #endif } -#ifdef _WIN32 -static float quiet_nanf(float x) { - uint32_t tmp; - memcpy(&tmp, &x, 4); - tmp |= 0x7fc00000lu; - memcpy(&x, &tmp, 4); - return x; -} - -static double quiet_nan(double x) { - uint64_t tmp; - memcpy(&tmp, &x, 8); - tmp |= 0x7ff8000000000000llu; - memcpy(&x, &tmp, 8); - return x; -} - -double wasm_rt_trunc(double x) { - if (isnan(x)) { - return quiet_nan(x); - } - return trunc(x); -} - -float wasm_rt_truncf(float x) { - if (isnan(x)) { - return quiet_nanf(x); - } - return truncf(x); -} - -float wasm_rt_nearbyintf(float x) { - if (isnan(x)) { - return quiet_nanf(x); - } - return nearbyintf(x); -} - -double wasm_rt_nearbyint(double x) { - if (isnan(x)) { - return quiet_nan(x); - } - return nearbyint(x); -} - -float wasm_rt_fabsf(float x) { - if (isnan(x)) { - uint32_t tmp; - memcpy(&tmp, &x, 4); - tmp = tmp & ~(1 << 31); - memcpy(&x, &tmp, 4); - return x; - } - return fabsf(x); -} - -double wasm_rt_fabs(double x) { - if (isnan(x)) { - uint64_t tmp; - memcpy(&tmp, &x, 8); - tmp = tmp & ~(1ll << 63); - memcpy(&x, &tmp, 8); - return x; - } - return fabs(x); -} -#endif - void wasm_rt_allocate_table(wasm_rt_table_t* table, uint32_t elements, uint32_t max_elements) { diff --git a/wasm2c/wasm-rt.h b/wasm2c/wasm-rt.h index 167c4c40..fed82a27 100644 --- a/wasm2c/wasm-rt.h +++ b/wasm2c/wasm-rt.h @@ -264,22 +264,6 @@ void wasm_rt_allocate_table(wasm_rt_table_t*, */ void wasm_rt_free_table(wasm_rt_table_t*); -#ifdef _WIN32 -float wasm_rt_truncf(float x); -double wasm_rt_trunc(double x); -float wasm_rt_nearbyintf(float x); -double wasm_rt_nearbyint(double x); -float wasm_rt_fabsf(float x); -double wasm_rt_fabs(double x); -#else -#define wasm_rt_truncf(x) truncf(x) -#define wasm_rt_trunc(x) trunc(x) -#define wasm_rt_nearbyintf(x) nearbyintf(x) -#define wasm_rt_nearbyint(x) nearbyint(x) -#define wasm_rt_fabsf(x) fabsf(x) -#define wasm_rt_fabs(x) fabs(x) -#endif - #ifdef __cplusplus } #endif |