diff options
-rw-r--r-- | .github/workflows/build.yml | 4 | ||||
-rw-r--r-- | .github/workflows/build_release.yml | 4 | ||||
-rw-r--r-- | src/c-writer.cc | 34 | ||||
-rw-r--r-- | src/template/wasm2c.declarations.c | 130 | ||||
-rwxr-xr-x | test/run-spec-wasm2c.py | 9 | ||||
-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 |
9 files changed, 305 insertions, 103 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 18f923e6..98793089 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -30,7 +30,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-12, windows-latest] steps: - uses: actions/setup-python@v1 with: @@ -43,7 +43,7 @@ jobs: if: matrix.os == 'ubuntu-latest' - name: install ninja (osx) run: brew install ninja - if: matrix.os == 'macos-latest' + if: matrix.os == 'macos-12' - name: install ninja (win) run: choco install ninja if: matrix.os == 'windows-latest' diff --git a/.github/workflows/build_release.yml b/.github/workflows/build_release.yml index 1c75638e..285423fb 100644 --- a/.github/workflows/build_release.yml +++ b/.github/workflows/build_release.yml @@ -15,7 +15,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-latest, macos-latest, windows-latest] + os: [ubuntu-latest, macos-12, windows-latest] defaults: run: shell: bash @@ -33,7 +33,7 @@ jobs: - name: install ninja (osx) run: brew install ninja - if: matrix.os == 'macos-latest' + if: matrix.os == 'macos-12' - name: install ninja (win) run: choco install ninja diff --git a/src/c-writer.cc b/src/c-writer.cc index e11d5a49..92d55ac7 100644 --- a/src/c-writer.cc +++ b/src/c-writer.cc @@ -2067,10 +2067,13 @@ void CWriter::Write(const ConvertExpr& expr) { break; case Opcode::F32ConvertI32U: - case Opcode::F32DemoteF64: WriteSimpleUnaryExpr(expr.opcode, "(f32)"); break; + case Opcode::F32DemoteF64: + WriteSimpleUnaryExpr(expr.opcode, "(f32)wasm_quiet"); + break; + case Opcode::F32ConvertI64U: // TODO(binji): This needs to be handled specially (see // wabt_convert_uint64_to_float). @@ -2086,10 +2089,13 @@ void CWriter::Write(const ConvertExpr& expr) { break; case Opcode::F64ConvertI32U: - case Opcode::F64PromoteF32: WriteSimpleUnaryExpr(expr.opcode, "(f64)"); break; + case Opcode::F64PromoteF32: + WriteSimpleUnaryExpr(expr.opcode, "(f64)wasm_quietf"); + break; + case Opcode::F64ConvertI64U: // TODO(binji): This needs to be handled specially (see // wabt_convert_uint64_to_double). @@ -2209,51 +2215,51 @@ void CWriter::Write(const UnaryExpr& expr) { break; case Opcode::F32Abs: - WriteSimpleUnaryExpr(expr.opcode, "wasm_rt_fabsf"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_fabsf"); break; case Opcode::F64Abs: - WriteSimpleUnaryExpr(expr.opcode, "wasm_rt_fabs"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_fabs"); break; case Opcode::F32Sqrt: - WriteSimpleUnaryExpr(expr.opcode, "sqrtf"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_sqrtf"); break; case Opcode::F64Sqrt: - WriteSimpleUnaryExpr(expr.opcode, "sqrt"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_sqrt"); break; case Opcode::F32Ceil: - WriteSimpleUnaryExpr(expr.opcode, "ceilf"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_ceilf"); break; case Opcode::F64Ceil: - WriteSimpleUnaryExpr(expr.opcode, "ceil"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_ceil"); break; case Opcode::F32Floor: - WriteSimpleUnaryExpr(expr.opcode, "floorf"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_floorf"); break; case Opcode::F64Floor: - WriteSimpleUnaryExpr(expr.opcode, "floor"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_floor"); break; case Opcode::F32Trunc: - WriteSimpleUnaryExpr(expr.opcode, "wasm_rt_truncf"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_truncf"); break; case Opcode::F64Trunc: - WriteSimpleUnaryExpr(expr.opcode, "wasm_rt_trunc"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_trunc"); break; case Opcode::F32Nearest: - WriteSimpleUnaryExpr(expr.opcode, "wasm_rt_nearbyintf"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_nearbyintf"); break; case Opcode::F64Nearest: - WriteSimpleUnaryExpr(expr.opcode, "wasm_rt_nearbyint"); + WriteSimpleUnaryExpr(expr.opcode, "wasm_nearbyint"); break; case Opcode::I32Extend8S: diff --git a/src/template/wasm2c.declarations.c b/src/template/wasm2c.declarations.c index bd66cedb..3861c52d 100644 --- a/src/template/wasm2c.declarations.c +++ b/src/template/wasm2c.declarations.c @@ -31,6 +31,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; @@ -53,6 +59,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; \ } @@ -77,6 +84,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; \ } @@ -313,3 +321,125 @@ DEFINE_REINTERPRET(f32_reinterpret_i32, u32, f32) 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); +} diff --git a/test/run-spec-wasm2c.py b/test/run-spec-wasm2c.py index 8362ce7b..5a246650 100755 --- a/test/run-spec-wasm2c.py +++ b/test/run-spec-wasm2c.py @@ -341,10 +341,17 @@ def Compile(cc, c_filename, out_dir, *args): if IS_WINDOWS: args += ['/nologo', '/MDd', '/c', c_filename, '/Fo' + o_filename] else: - args += ['-c', c_filename, '-o', o_filename, + # See "Compiling the wasm2c output" section of wasm2c/README.md + # When compiling with -O2, GCC and clang require '-fno-optimize-sibling-calls' + # and '-frounding-math' to maintain conformance with the spec tests + # (GCC also requires '-fsignaling-nans') + args += ['-c', c_filename, '-o', o_filename, '-O2', '-Wall', '-Werror', '-Wno-unused', + '-Wno-ignored-optimization-argument', '-Wno-tautological-constant-out-of-range-compare', '-Wno-infinite-recursion', + '-fno-optimize-sibling-calls', + '-frounding-math', '-fsignaling-nans', '-std=c99', '-D_DEFAULT_SOURCE'] # Use RunWithArgsForStdout and discard stdout because cl.exe # unconditionally prints the name of input files on stdout 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 |