summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/build.yml4
-rw-r--r--.github/workflows/build_release.yml4
-rw-r--r--src/c-writer.cc34
-rw-r--r--src/template/wasm2c.declarations.c130
-rwxr-xr-xtest/run-spec-wasm2c.py9
-rw-r--r--wasm2c/README.md13
-rw-r--r--wasm2c/examples/fac/fac.c130
-rw-r--r--wasm2c/wasm-rt-impl.c68
-rw-r--r--wasm2c/wasm-rt.h16
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