diff options
author | Sam Clegg <sbc@chromium.org> | 2022-03-10 02:29:36 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-03-09 18:29:36 -0800 |
commit | 6a89e3f74560eb8f0396c24ce625de0023cb46b2 (patch) | |
tree | 163f824574bbd03d85f9bc4a17ce1886fd7936a4 /wasm2c | |
parent | da9f07ce054c7af5061279c3d237fd64d63d9f9b (diff) | |
download | wabt-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/Makefile | 2 | ||||
-rw-r--r-- | wasm2c/examples/fac/fac.c | 127 | ||||
-rw-r--r-- | wasm2c/examples/rot13/Makefile | 2 | ||||
-rw-r--r-- | wasm2c/wasm-rt-impl.c | 140 | ||||
-rw-r--r-- | wasm2c/wasm-rt.h | 44 |
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 |