summaryrefslogtreecommitdiff
path: root/wasm2c
diff options
context:
space:
mode:
authorSam Clegg <sbc@chromium.org>2022-03-10 02:29:36 +0000
committerGitHub <noreply@github.com>2022-03-09 18:29:36 -0800
commit6a89e3f74560eb8f0396c24ce625de0023cb46b2 (patch)
tree163f824574bbd03d85f9bc4a17ce1886fd7936a4 /wasm2c
parentda9f07ce054c7af5061279c3d237fd64d63d9f9b (diff)
downloadwabt-6a89e3f74560eb8f0396c24ce625de0023cb46b2.tar.gz
wabt-6a89e3f74560eb8f0396c24ce625de0023cb46b2.tar.bz2
wabt-6a89e3f74560eb8f0396c24ce625de0023cb46b2.zip
Add windows implementation of wasm2c runtime (#1843)
All tests are now passing with cl.exe under x64. With x86 there are some test failure that I believe relate the use of the x87 registers to pass floating point numbers. I suggest we look into fixing those as a followup. Split out from #1833
Diffstat (limited to 'wasm2c')
-rw-r--r--wasm2c/examples/fac/Makefile2
-rw-r--r--wasm2c/examples/fac/fac.c127
-rw-r--r--wasm2c/examples/rot13/Makefile2
-rw-r--r--wasm2c/wasm-rt-impl.c140
-rw-r--r--wasm2c/wasm-rt.h44
5 files changed, 280 insertions, 35 deletions
diff --git a/wasm2c/examples/fac/Makefile b/wasm2c/examples/fac/Makefile
index caf32e24..a5179a71 100644
--- a/wasm2c/examples/fac/Makefile
+++ b/wasm2c/examples/fac/Makefile
@@ -6,7 +6,7 @@ all: fac
clean:
rm -rf fac fac.wasm fac.c *.o
-fac: main.o fac.o ../../wasm-rt-impl.o
+fac: main.o fac.o ../../wasm-rt-impl.o -lm
fac.wasm: fac.wat ../../../bin/wat2wasm
../../../bin/wat2wasm $< -o $@
diff --git a/wasm2c/examples/fac/fac.c b/wasm2c/examples/fac/fac.c
index eeb28d9f..9ac9522c 100644
--- a/wasm2c/examples/fac/fac.c
+++ b/wasm2c/examples/fac/fac.c
@@ -3,8 +3,6 @@
#include <string.h>
#include "fac.h"
-#define UNLIKELY(x) __builtin_expect(!!(x), 0)
-#define LIKELY(x) __builtin_expect(!!(x), 1)
#define TRAP(x) (wasm_rt_trap(WASM_RT_TRAP_##x), 0)
@@ -22,13 +20,13 @@
|| TRAP(CALL_INDIRECT) \
, ((t)table.data[x].func)(__VA_ARGS__))
-#define RANGE_CHECK(mem, a, t) \
- if (UNLIKELY((a) + sizeof(t) > mem->size)) TRAP(OOB)
+#define RANGE_CHECK(mem, offset, len) \
+ if (UNLIKELY(offset + (uint64_t)len > mem->size)) TRAP(OOB)
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
#define MEMCHECK(mem, a, t)
#else
-#define MEMCHECK(mem, a, t) RANGE_CHECK(mem, a, t)
+#define MEMCHECK(mem, a, t) RANGE_CHECK(mem, a, sizeof(t))
#endif
#if WABT_BIG_ENDIAN
@@ -42,45 +40,48 @@ static inline void load_data(void *dest, const void *src, size_t n) {
dest_chars[n - i - 1] = cursor;
}
}
-#define LOAD_DATA(m, o, i, s) do { \
- RANGE_CHECK((&m), m.size - o - s, char[s]); \
+#define LOAD_DATA(m, o, i, s) \
+ do { \
+ RANGE_CHECK((&m), m.size - o - s, s); \
load_data(&(m.data[m.size - o - s]), i, s); \
} while (0)
-#define DEFINE_LOAD(name, t1, t2, t3) \
- static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
- MEMCHECK(mem, addr, t1); \
- t1 result; \
- __builtin_memcpy(&result, &mem->data[mem->size - addr - sizeof(t1)], sizeof(t1)); \
- return (t3)(t2)result; \
+#define DEFINE_LOAD(name, t1, t2, t3) \
+ static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 result; \
+ wasm_rt_memcpy(&result, &mem->data[mem->size - addr - sizeof(t1)], \
+ sizeof(t1)); \
+ return (t3)(t2)result; \
}
-#define DEFINE_STORE(name, t1, t2) \
- static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
- MEMCHECK(mem, addr, t1); \
- t1 wrapped = (t1)value; \
- __builtin_memcpy(&mem->data[mem->size - addr - sizeof(t1)], &wrapped, sizeof(t1)); \
+#define DEFINE_STORE(name, t1, t2) \
+ static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 wrapped = (t1)value; \
+ wasm_rt_memcpy(&mem->data[mem->size - addr - sizeof(t1)], &wrapped, \
+ sizeof(t1)); \
}
#else
static inline void load_data(void *dest, const void *src, size_t n) {
memcpy(dest, src, n);
}
#define LOAD_DATA(m, o, i, s) do { \
- RANGE_CHECK((&m), o, char[s]); \
+ RANGE_CHECK((&m), o, s); \
load_data(&(m.data[o]), i, s); \
} while (0)
-#define DEFINE_LOAD(name, t1, t2, t3) \
- static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
- MEMCHECK(mem, addr, t1); \
- t1 result; \
- __builtin_memcpy(&result, &mem->data[addr], sizeof(t1)); \
- return (t3)(t2)result; \
+#define DEFINE_LOAD(name, t1, t2, t3) \
+ static inline t3 name(wasm_rt_memory_t* mem, u64 addr) { \
+ MEMCHECK(mem, addr, t1); \
+ t1 result; \
+ wasm_rt_memcpy(&result, &mem->data[addr], sizeof(t1)); \
+ return (t3)(t2)result; \
}
#define DEFINE_STORE(name, t1, t2) \
static inline void name(wasm_rt_memory_t* mem, u64 addr, t2 value) { \
MEMCHECK(mem, addr, t1); \
t1 wrapped = (t1)value; \
- __builtin_memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \
+ wasm_rt_memcpy(&mem->data[addr], &wrapped, sizeof(t1)); \
}
#endif
@@ -108,6 +109,78 @@ DEFINE_STORE(i64_store8, u8, u64)
DEFINE_STORE(i64_store16, u16, u64)
DEFINE_STORE(i64_store32, u32, u64)
+#if defined(_MSC_VER)
+
+#include <intrin.h>
+
+// Adapted from https://github.com/nemequ/portable-snippets/blob/master/builtin/builtin.h
+
+static inline int I64_CLZ(unsigned long long v) {
+ unsigned long r = 0;
+#if defined(_M_AMD64) || defined(_M_ARM)
+ if (_BitScanReverse64(&r, v)) {
+ return 63 - r;
+ }
+#else
+ if (_BitScanReverse(&r, (unsigned long) (v >> 32))) {
+ return 31 - r;
+ } else if (_BitScanReverse(&r, (unsigned long) v)) {
+ return 63 - r;
+ }
+#endif
+ return 64;
+}
+
+static inline int I32_CLZ(unsigned long v) {
+ unsigned long r = 0;
+ if (_BitScanReverse(&r, v)) {
+ return 31 - r;
+ }
+ return 32;
+}
+
+static inline int I64_CTZ(unsigned long long v) {
+ if (!v) {
+ return 64;
+ }
+ unsigned long r = 0;
+#if defined(_M_AMD64) || defined(_M_ARM)
+ _BitScanForward64(&r, v);
+ return (int) r;
+#else
+ if (_BitScanForward(&r, (unsigned int) (v))) {
+ return (int) (r);
+ }
+
+ _BitScanForward(&r, (unsigned int) (v >> 32));
+ return (int) (r + 32);
+#endif
+}
+
+static inline int I32_CTZ(unsigned long v) {
+ if (!v) {
+ return 32;
+ }
+ unsigned long r = 0;
+ _BitScanForward(&r, v);
+ return (int) r;
+}
+
+#define POPCOUNT_DEFINE_PORTABLE(f_n, T) \
+ static inline u32 f_n(T x) { \
+ x = x - ((x >> 1) & (T)~(T)0/3); \
+ x = (x & (T)~(T)0/15*3) + ((x >> 2) & (T)~(T)0/15*3); \
+ x = (x + (x >> 4)) & (T)~(T)0/255*15; \
+ return (T)(x * ((T)~(T)0/255)) >> (sizeof(T) - 1) * 8; \
+ }
+
+POPCOUNT_DEFINE_PORTABLE(I32_POPCNT, u32)
+POPCOUNT_DEFINE_PORTABLE(I64_POPCNT, u64)
+
+#undef POPCOUNT_DEFINE_PORTABLE
+
+#else
+
#define I32_CLZ(x) ((x) ? __builtin_clz(x) : 32)
#define I64_CLZ(x) ((x) ? __builtin_clzll(x) : 64)
#define I32_CTZ(x) ((x) ? __builtin_ctz(x) : 32)
@@ -115,6 +188,8 @@ DEFINE_STORE(i64_store32, u32, u64)
#define I32_POPCNT(x) (__builtin_popcount(x))
#define I64_POPCNT(x) (__builtin_popcountll(x))
+#endif
+
#define DIV_S(ut, min, x, y) \
((UNLIKELY((y) == 0)) ? TRAP(DIV_BY_ZERO) \
: (UNLIKELY((x) == min && (y) == -1)) ? TRAP(INT_OVERFLOW) \
diff --git a/wasm2c/examples/rot13/Makefile b/wasm2c/examples/rot13/Makefile
index b58db6be..5e5f5205 100644
--- a/wasm2c/examples/rot13/Makefile
+++ b/wasm2c/examples/rot13/Makefile
@@ -6,7 +6,7 @@ all: rot13
clean:
rm -rf rot13 rot13.wasm rot13.c *.o
-rot13: main.o rot13.o ../../wasm-rt-impl.o ../../wasm-rt-os-unix.o -lm
+rot13: main.o rot13.o ../../wasm-rt-impl.o -lm
rot13.wasm: rot13.wat ../../../bin/wat2wasm
../../../bin/wat2wasm $< -o $@
diff --git a/wasm2c/wasm-rt-impl.c b/wasm2c/wasm-rt-impl.c
index d11cd7e7..e685f38b 100644
--- a/wasm2c/wasm-rt-impl.c
+++ b/wasm2c/wasm-rt-impl.c
@@ -17,6 +17,7 @@
#include "wasm-rt-impl.h"
#include <assert.h>
+#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
@@ -26,10 +27,15 @@
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER_POSIX
#include <signal.h>
-#include <sys/mman.h>
#include <unistd.h>
#endif
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <sys/mman.h>
+#endif
+
#define PAGE_SIZE 65536
typedef struct FuncType {
@@ -108,6 +114,53 @@ static void signal_handler(int sig, siginfo_t* si, void* unused) {
}
#endif
+#ifdef _WIN32
+static void* os_mmap(size_t size) {
+ return VirtualAlloc(NULL, size, MEM_RESERVE | MEM_COMMIT, PAGE_NOACCESS);
+}
+
+static int os_mprotect(void* addr, size_t size) {
+ DWORD old;
+ BOOL succeeded = VirtualProtect((LPVOID)addr, size, PAGE_READWRITE, &old);
+ return succeeded ? 0 : -1;
+}
+
+static void os_print_last_error(const char* msg) {
+ DWORD errorMessageID = GetLastError();
+ if (errorMessageID != 0) {
+ LPSTR messageBuffer = 0;
+ // The api creates the buffer that holds the message
+ size_t size = FormatMessageA(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
+ FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPSTR)&messageBuffer, 0, NULL);
+ (void)size;
+ printf("%s. %s\n", msg, messageBuffer);
+ LocalFree(messageBuffer);
+ } else {
+ printf("%s. No error code.\n", msg);
+ }
+}
+#else
+static void* os_mmap(size_t size) {
+ int map_prot = PROT_NONE;
+ int map_flags = MAP_ANONYMOUS | MAP_PRIVATE;
+ uint8_t* addr = mmap(NULL, size, map_prot, map_flags, -1, 0);
+ if (addr == MAP_FAILED)
+ return NULL;
+ return addr;
+}
+
+static int os_mprotect(void* addr, size_t size) {
+ return mprotect(addr, size, PROT_READ | PROT_WRITE);
+}
+
+static void os_print_last_error(const char* msg) {
+ perror(msg);
+}
+#endif
+
void wasm_rt_allocate_memory(wasm_rt_memory_t* memory,
uint32_t initial_pages,
uint32_t max_pages) {
@@ -129,13 +182,17 @@ void wasm_rt_allocate_memory(wasm_rt_memory_t* memory,
}
/* Reserve 8GiB. */
- void* addr =
- mmap(NULL, 0x200000000ul, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ void* addr = os_mmap(0x200000000ul);
+
if (addr == (void*)-1) {
- perror("mmap failed");
+ os_print_last_error("os_mmap failed.");
+ abort();
+ }
+ int ret = os_mprotect(addr, byte_length);
+ if (ret != 0) {
+ os_print_last_error("os_mprotect failed.");
abort();
}
- mprotect(addr, byte_length, PROT_READ | PROT_WRITE);
memory->data = addr;
#else
memory->data = calloc(byte_length, 1);
@@ -159,7 +216,10 @@ uint32_t wasm_rt_grow_memory(wasm_rt_memory_t* memory, uint32_t delta) {
uint32_t delta_size = delta * PAGE_SIZE;
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER_POSIX
uint8_t* new_data = memory->data;
- mprotect(new_data + old_size, delta_size, PROT_READ | PROT_WRITE);
+ int ret = os_mprotect(new_data + old_size, delta_size);
+ if (ret != 0) {
+ return (uint32_t)-1;
+ }
#else
uint8_t* new_data = realloc(memory->data, new_size);
if (new_data == NULL) {
@@ -179,6 +239,74 @@ uint32_t wasm_rt_grow_memory(wasm_rt_memory_t* memory, uint32_t delta) {
return old_pages;
}
+#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 639c2406..d3c25194 100644
--- a/wasm2c/wasm-rt.h
+++ b/wasm2c/wasm-rt.h
@@ -17,12 +17,32 @@
#ifndef WASM_RT_H_
#define WASM_RT_H_
+#include <stdbool.h>
#include <stdint.h>
+#include <string.h>
#ifdef __cplusplus
extern "C" {
#endif
+#ifndef __has_builtin
+#define __has_builtin(x) 0 // Compatibility with non-clang compilers.
+#endif
+
+#if __has_builtin(__builtin_expect)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#define LIKELY(x) __builtin_expect(!!(x), 1)
+#else
+#define UNLIKELY(x) (x)
+#define LIKELY(x) (x)
+#endif
+
+#if __has_builtin(__builtin_memcpy)
+#define wasm_rt_memcpy __builtin_memcpy
+#else
+#define wasm_rt_memcpy memcpy
+#endif
+
/** Maximum stack depth before trapping. This can be configured by defining
* this symbol before including wasm-rt when building the generated c files,
* for example:
@@ -68,6 +88,12 @@ extern "C" {
#endif
+#if defined(_MSC_VER)
+#define WASM_RT_NO_RETURN __declspec(noreturn)
+#else
+#define WASM_RT_NO_RETURN __attribute__((noreturn))
+#endif
+
/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */
typedef enum {
WASM_RT_TRAP_NONE, /** No error. */
@@ -128,7 +154,7 @@ typedef struct {
* The result of `wasm_rt_try` will be the provided trap reason.
*
* This is typically called by the generated code, and not the embedder. */
-extern void wasm_rt_trap(wasm_rt_trap_t) __attribute__((noreturn));
+WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t);
/**
* Return a human readable error string based on a trap type.
@@ -199,6 +225,22 @@ extern void wasm_rt_allocate_table(wasm_rt_table_t*,
/** Current call stack depth. */
extern uint32_t wasm_rt_call_stack_depth;
+#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