start calling into wasm2c version of platform

This commit is contained in:
2023-04-04 23:58:46 +02:00
parent 2df498152c
commit 45f4ddb4c4
9 changed files with 5453 additions and 18 deletions

View File

@@ -1,7 +1,7 @@
WASM3_C := $(wildcard wasm3/source/*.c) WASM3_C := $(wildcard wasm3/source/*.c)
WASM3_O := $(WASM3_C:.c=.o) WASM3_O := $(WASM3_C:.c=.o)
uw8-wasm3: main.o $(WASM3_O) uw8-wasm3: main.o $(WASM3_O) platform.o wasm-rt-impl.o
gcc -g -lm -lSDL2 -o uw8-wasm3 $^ gcc -g -lm -lSDL2 -o uw8-wasm3 $^
run: uw8-wasm3 .PHONY run: uw8-wasm3 .PHONY
@@ -11,10 +11,10 @@ run-ts:
deno run --allow-read main.ts deno run --allow-read main.ts
wasm3/source/%.o: wasm3/source/%.c wasm3/source/%.o: wasm3/source/%.c
gcc -g -O2 -c -o $@ $^ gcc -g -O2 -c -o $@ $<
main.o: main.c %.o: %.c platform.h wasm-rt.h wasm-rt-impl.h
gcc -g -O2 -c -o main.o main.c gcc -g -O2 -c -o $@ $<
clean: clean:
rm uw8-wasm3 main.o $(WASM3_O) rm uw8-wasm3 main.o $(WASM3_O)

Binary file not shown.

89
main.c
View File

@@ -1,5 +1,6 @@
#include "wasm3/source/wasm3.h" #include "wasm3/source/wasm3.h"
#include "wasm3/source/m3_env.h" #include "wasm3/source/m3_env.h"
#include "platform.h"
#include "SDL2/SDL.h" #include "SDL2/SDL.h"
#include "SDL2/SDL_video.h" #include "SDL2/SDL_video.h"
#include "SDL2/SDL_render.h" #include "SDL2/SDL_render.h"
@@ -21,6 +22,27 @@ void* loadFile(size_t* sizeOut, const char* filename) {
return buffer; return buffer;
} }
#define MATH1(name) \
f32 Z_envZ_##name(struct Z_env_instance_t* i, f32 v) { \
return name##f(v); \
}
#define MATH2(name) \
f32 Z_envZ_##name(struct Z_env_instance_t* i, f32 a, f32 b) { \
return name##f(a, b); \
}
MATH1(acos); MATH1(asin); MATH1(atan); MATH2(atan2);
MATH1(cos); MATH1(sin); MATH1(tan);
MATH1(exp); MATH2(pow);
void Z_envZ_logChar(struct Z_env_instance_t* i, u32 c) {}
u32 reservedGlobal;
#define G_RESERVED(n) u32* Z_envZ_g_reserved##n(struct Z_env_instance_t* i) { return &reservedGlobal; }
G_RESERVED(0); G_RESERVED(1); G_RESERVED(2); G_RESERVED(3);
G_RESERVED(4); G_RESERVED(5); G_RESERVED(6); G_RESERVED(7);
G_RESERVED(8); G_RESERVED(9); G_RESERVED(10); G_RESERVED(11);
G_RESERVED(12); G_RESERVED(13); G_RESERVED(14); G_RESERVED(15);
wasm_rt_memory_t* Z_envZ_memory(struct Z_env_instance_t* i) { return (wasm_rt_memory_t*)i; }
void verifyM3(IM3Runtime runtime, M3Result result) { void verifyM3(IM3Runtime runtime, M3Result result) {
if (result != m3Err_none) { if (result != m3Err_none) {
M3ErrorInfo info; M3ErrorInfo info;
@@ -86,6 +108,25 @@ m3ApiRawFunction(platformTrampoline) {
m3ApiSuccess(); m3ApiSuccess();
} }
m3ApiRawFunction(callCircle) {
Z_platformZ_circle((Z_platform_instance_t*)_ctx->userdata, *(f32*)&_sp[0], *(f32*)&_sp[1], *(f32*)&_sp[2], _sp[3]);
m3ApiSuccess();
}
m3ApiRawFunction(callBlitSprite) {
Z_platformZ_blitSprite((Z_platform_instance_t*)_ctx->userdata, _sp[0], _sp[1], _sp[2], _sp[3], _sp[4]);
m3ApiSuccess();
}
struct {
const char* name;
const char* signature;
M3RawCall function;
} cPlatformFunctions[] = {
{ "circle", "v(fffi)", callCircle },
{ "blitSprite", "v(iiiii)", callBlitSprite }
};
void appendType(char* signature, M3ValueType type) { void appendType(char* signature, M3ValueType type) {
if(type == c_m3Type_i32) { if(type == c_m3Type_i32) {
strcat(signature, "i"); strcat(signature, "i");
@@ -99,24 +140,33 @@ void appendType(char* signature, M3ValueType type) {
} }
} }
void linkPlatformFunctions(IM3Runtime runtime, IM3Module cartMod, IM3Module platformMod) { void linkPlatformFunctions(IM3Runtime runtime, IM3Module cartMod, IM3Module platformMod, Z_platform_instance_t* platformInstance) {
for(u32 functionIndex = 0; functionIndex < platformMod->numFunctions; ++functionIndex) { for(u32 functionIndex = 0; functionIndex < platformMod->numFunctions; ++functionIndex) {
M3Function function = platformMod->functions[functionIndex]; M3Function function = platformMod->functions[functionIndex];
if(function.export_name != NULL) { if(function.export_name != NULL) {
IM3Function iFunc; bool foundCImpl = false;
verifyM3(runtime, m3_FindFunction(&iFunc, runtime, function.export_name)); for(int i = 0; i * sizeof(cPlatformFunctions[0]) < sizeof(cPlatformFunctions); ++i) {
char signature[128] = { 0 }; if(strcmp(function.export_name, cPlatformFunctions[i].name) == 0) {
if(m3_GetRetCount(iFunc) > 0) { m3_LinkRawFunctionEx(cartMod, "env", function.export_name, cPlatformFunctions[i].signature, cPlatformFunctions[i].function, platformInstance);
appendType(signature, m3_GetRetType(iFunc, 0)); foundCImpl = true;
} else { }
strcat(signature, "v");
} }
strcat(signature, "("); if(!foundCImpl) {
for(uint32_t i = 0; i < m3_GetArgCount(iFunc); ++i) { IM3Function iFunc;
appendType(signature, m3_GetArgType(iFunc, i)); verifyM3(runtime, m3_FindFunction(&iFunc, runtime, function.export_name));
char signature[128] = { 0 };
if(m3_GetRetCount(iFunc) > 0) {
appendType(signature, m3_GetRetType(iFunc, 0));
} else {
strcat(signature, "v");
}
strcat(signature, "(");
for(uint32_t i = 0; i < m3_GetArgCount(iFunc); ++i) {
appendType(signature, m3_GetArgType(iFunc, i));
}
strcat(signature, ")");
m3_LinkRawFunctionEx(cartMod, "env", function.export_name, signature, platformTrampoline, iFunc);
} }
strcat(signature, ")");
m3_LinkRawFunctionEx(cartMod, "env", function.export_name, signature, platformTrampoline, iFunc);
} }
} }
} }
@@ -141,6 +191,8 @@ const uint32_t uw8buttonScanCodes[] = {
typedef struct { typedef struct {
IM3Runtime runtime; IM3Runtime runtime;
IM3Module platform; IM3Module platform;
wasm_rt_memory_t memory_c;
Z_platform_instance_t platform_c;
IM3Module cart; IM3Module cart;
} Uw8Runtime; } Uw8Runtime;
@@ -150,6 +202,12 @@ void initRuntime(Uw8Runtime* runtime, IM3Environment env,
runtime->runtime->memory.maxPages = 4; runtime->runtime->memory.maxPages = 4;
verifyM3(runtime->runtime, ResizeMemory(runtime->runtime, 4)); verifyM3(runtime->runtime, ResizeMemory(runtime->runtime, 4));
runtime->memory_c.data = m3_GetMemory(runtime->runtime, NULL, 0);
runtime->memory_c.max_pages = 4;
runtime->memory_c.pages = 4;
runtime->memory_c.size = 256*1024;
Z_platform_instantiate(&runtime->platform_c, (struct Z_env_instance_t*)&runtime->memory_c);
verifyM3(runtime->runtime, m3_ParseModule(env, &runtime->platform, platform, platformSize)); verifyM3(runtime->runtime, m3_ParseModule(env, &runtime->platform, platform, platformSize));
runtime->platform->memoryImported = true; runtime->platform->memoryImported = true;
verifyM3(runtime->runtime, m3_LoadModule(runtime->runtime, runtime->platform)); verifyM3(runtime->runtime, m3_LoadModule(runtime->runtime, runtime->platform));
@@ -161,7 +219,7 @@ void initRuntime(Uw8Runtime* runtime, IM3Environment env,
runtime->platform->memoryImported = true; runtime->platform->memoryImported = true;
verifyM3(runtime->runtime, m3_LoadModule(runtime->runtime, runtime->cart)); verifyM3(runtime->runtime, m3_LoadModule(runtime->runtime, runtime->cart));
linkSystemFunctions(runtime->runtime, runtime->cart); linkSystemFunctions(runtime->runtime, runtime->cart);
linkPlatformFunctions(runtime->runtime, runtime->cart, runtime->platform); linkPlatformFunctions(runtime->runtime, runtime->cart, runtime->platform, &runtime->platform_c);
verifyM3(runtime->runtime, m3_CompileModule(runtime->cart)); verifyM3(runtime->runtime, m3_CompileModule(runtime->cart));
verifyM3(runtime->runtime, m3_RunStart(runtime->cart)); verifyM3(runtime->runtime, m3_RunStart(runtime->cart));
} }
@@ -226,6 +284,9 @@ int main(int argc, const char** argv) {
m3_FreeRuntime(loaderRuntime); m3_FreeRuntime(loaderRuntime);
wasm_rt_init();
Z_platform_init_module();
bool quit = false; bool quit = false;
while(!quit) { while(!quit) {
Uw8Runtime runtime; Uw8Runtime runtime;

4242
platform.c Normal file

File diff suppressed because it is too large Load Diff

147
platform.h Normal file
View File

@@ -0,0 +1,147 @@
/* Automatically generated by wasm2c */
#ifndef PLATFORM_H_GENERATED_
#define PLATFORM_H_GENERATED_
#include <stdint.h>
#include "wasm-rt.h"
/* TODO(binji): only use stdint.h types in header */
#ifndef WASM_RT_CORE_TYPES_DEFINED
#define WASM_RT_CORE_TYPES_DEFINED
typedef uint8_t u8;
typedef int8_t s8;
typedef uint16_t u16;
typedef int16_t s16;
typedef uint32_t u32;
typedef int32_t s32;
typedef uint64_t u64;
typedef int64_t s64;
typedef float f32;
typedef double f64;
#endif
#ifdef __cplusplus
extern "C" {
#endif
struct Z_env_instance_t;
extern wasm_rt_memory_t* Z_envZ_memory(struct Z_env_instance_t*);
typedef struct Z_platform_instance_t {
struct Z_env_instance_t* Z_env_instance;
/* import: 'env' 'memory' */
wasm_rt_memory_t *Z_envZ_memory;
u64 w2c_g0;
u32 w2c_g1;
u32 w2c_g2;
u32 w2c_g3;
u32 w2c_g4;
u32 w2c_g5;
u32 w2c_g6;
} Z_platform_instance_t;
void Z_platform_init_module(void);
void Z_platform_instantiate(Z_platform_instance_t*, struct Z_env_instance_t*);
void Z_platform_free(Z_platform_instance_t*);
/* import: 'env' 'cos' */
f32 Z_envZ_cos(struct Z_env_instance_t*, f32);
/* import: 'env' 'exp' */
f32 Z_envZ_exp(struct Z_env_instance_t*, f32);
/* import: 'env' 'logChar' */
void Z_envZ_logChar(struct Z_env_instance_t*, u32);
/* import: 'env' 'pow' */
f32 Z_envZ_pow(struct Z_env_instance_t*, f32, f32);
/* import: 'env' 'sin' */
f32 Z_envZ_sin(struct Z_env_instance_t*, f32);
/* export: 'time' */
f32 Z_platformZ_time(Z_platform_instance_t*);
/* export: 'isButtonPressed' */
u32 Z_platformZ_isButtonPressed(Z_platform_instance_t*, u32);
/* export: 'isButtonTriggered' */
u32 Z_platformZ_isButtonTriggered(Z_platform_instance_t*, u32);
/* export: 'random' */
u32 Z_platformZ_random(Z_platform_instance_t*);
/* export: 'random64' */
u64 Z_platformZ_random64(Z_platform_instance_t*);
/* export: 'randomf' */
f32 Z_platformZ_randomf(Z_platform_instance_t*);
/* export: 'randomSeed' */
void Z_platformZ_randomSeed(Z_platform_instance_t*, u32);
/* export: 'fmod' */
f32 Z_platformZ_fmod(Z_platform_instance_t*, f32, f32);
/* export: 'cls' */
void Z_platformZ_cls(Z_platform_instance_t*, u32);
/* export: 'setPixel' */
void Z_platformZ_setPixel(Z_platform_instance_t*, u32, u32, u32);
/* export: 'getPixel' */
u32 Z_platformZ_getPixel(Z_platform_instance_t*, u32, u32);
/* export: 'hline' */
void Z_platformZ_hline(Z_platform_instance_t*, u32, u32, u32, u32);
/* export: 'rectangle' */
void Z_platformZ_rectangle(Z_platform_instance_t*, f32, f32, f32, f32, u32);
/* export: 'rectangleOutline' */
void Z_platformZ_rectangleOutline(Z_platform_instance_t*, f32, f32, f32, f32, u32);
/* export: 'circle' */
void Z_platformZ_circle(Z_platform_instance_t*, f32, f32, f32, u32);
/* export: 'circleOutline' */
void Z_platformZ_circleOutline(Z_platform_instance_t*, f32, f32, f32, u32);
/* export: 'line' */
void Z_platformZ_line(Z_platform_instance_t*, f32, f32, f32, f32, u32);
/* export: 'blitSprite' */
void Z_platformZ_blitSprite(Z_platform_instance_t*, u32, u32, u32, u32, u32);
/* export: 'grabSprite' */
void Z_platformZ_grabSprite(Z_platform_instance_t*, u32, u32, u32, u32, u32);
/* export: 'printChar' */
void Z_platformZ_printChar(Z_platform_instance_t*, u32);
/* export: 'printString' */
void Z_platformZ_printString(Z_platform_instance_t*, u32);
/* export: 'printInt' */
void Z_platformZ_printInt(Z_platform_instance_t*, u32);
/* export: 'setTextColor' */
void Z_platformZ_setTextColor(Z_platform_instance_t*, u32);
/* export: 'setBackgroundColor' */
void Z_platformZ_setBackgroundColor(Z_platform_instance_t*, u32);
/* export: 'setCursorPosition' */
void Z_platformZ_setCursorPosition(Z_platform_instance_t*, u32, u32);
/* export: 'playNote' */
void Z_platformZ_playNote(Z_platform_instance_t*, u32, u32);
/* export: 'endFrame' */
void Z_platformZ_endFrame(Z_platform_instance_t*);
/* export: 'sndGes' */
f32 Z_platformZ_sndGes(Z_platform_instance_t*, u32);
#ifdef __cplusplus
}
#endif
#endif /* PLATFORM_H_GENERATED_ */

Binary file not shown.

504
wasm-rt-impl.c Normal file
View File

@@ -0,0 +1,504 @@
/*
* Copyright 2018 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "wasm-rt-impl.h"
#include <assert.h>
#include <math.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY && \
!defined(_WIN32)
#include <signal.h>
#include <unistd.h>
#endif
#ifdef _WIN32
#include <windows.h>
#else
#include <sys/mman.h>
#endif
#if _MSC_VER
#include <malloc.h>
#define alloca _alloca
#endif
#define PAGE_SIZE 65536
#define MAX_EXCEPTION_SIZE PAGE_SIZE
typedef struct FuncType {
wasm_rt_type_t* params;
wasm_rt_type_t* results;
uint32_t param_count;
uint32_t result_count;
} FuncType;
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
static bool g_signal_handler_installed = false;
#ifdef _WIN32
static void* g_sig_handler_handle = 0;
#else
static char* g_alt_stack = 0;
#endif
#endif
#if WASM_RT_USE_STACK_DEPTH_COUNT
uint32_t wasm_rt_call_stack_depth;
uint32_t wasm_rt_saved_call_stack_depth;
#endif
static FuncType* g_func_types;
static uint32_t g_func_type_count;
jmp_buf wasm_rt_jmp_buf;
static uint32_t g_active_exception_tag;
static uint8_t g_active_exception[MAX_EXCEPTION_SIZE];
static uint32_t g_active_exception_size;
static jmp_buf* g_unwind_target;
void wasm_rt_trap(wasm_rt_trap_t code) {
assert(code != WASM_RT_TRAP_NONE);
#if WASM_RT_USE_STACK_DEPTH_COUNT
wasm_rt_call_stack_depth = wasm_rt_saved_call_stack_depth;
#endif
#ifdef WASM_RT_TRAP_HANDLER
WASM_RT_TRAP_HANDLER(code);
wasm_rt_unreachable();
#else
WASM_RT_LONGJMP(wasm_rt_jmp_buf, code);
#endif
}
static bool func_types_are_equal(FuncType* a, FuncType* b) {
if (a->param_count != b->param_count || a->result_count != b->result_count)
return 0;
uint32_t i;
for (i = 0; i < a->param_count; ++i)
if (a->params[i] != b->params[i])
return 0;
for (i = 0; i < a->result_count; ++i)
if (a->results[i] != b->results[i])
return 0;
return 1;
}
uint32_t wasm_rt_register_func_type(uint32_t param_count,
uint32_t result_count,
...) {
size_t param_size = param_count * sizeof(wasm_rt_type_t);
size_t result_size = result_count * sizeof(wasm_rt_type_t);
FuncType func_type;
func_type.param_count = param_count;
func_type.params = alloca(param_size);
func_type.result_count = result_count;
func_type.results = alloca(result_size);
va_list args;
va_start(args, result_count);
uint32_t i;
for (i = 0; i < param_count; ++i)
func_type.params[i] = va_arg(args, wasm_rt_type_t);
for (i = 0; i < result_count; ++i)
func_type.results[i] = va_arg(args, wasm_rt_type_t);
va_end(args);
for (i = 0; i < g_func_type_count; ++i)
if (func_types_are_equal(&g_func_types[i], &func_type))
return i + 1;
// This is a new/unseed type. Copy our stack allocated params/results into
// permanent heap allocated space.
wasm_rt_type_t* params = malloc(param_size);
wasm_rt_type_t* results = malloc(result_size);
memcpy(params, func_type.params, param_size);
memcpy(results, func_type.results, result_size);
func_type.params = params;
func_type.results = results;
uint32_t idx = g_func_type_count++;
g_func_types = realloc(g_func_types, g_func_type_count * sizeof(FuncType));
g_func_types[idx] = func_type;
return idx + 1;
}
uint32_t wasm_rt_register_tag(uint32_t size) {
static uint32_t s_tag_count = 0;
if (size > MAX_EXCEPTION_SIZE) {
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
}
return s_tag_count++;
}
void wasm_rt_load_exception(uint32_t tag, uint32_t size, const void* values) {
assert(size <= MAX_EXCEPTION_SIZE);
g_active_exception_tag = tag;
g_active_exception_size = size;
if (size) {
memcpy(g_active_exception, values, size);
}
}
WASM_RT_NO_RETURN void wasm_rt_throw(void) {
WASM_RT_LONGJMP(*g_unwind_target, WASM_RT_TRAP_UNCAUGHT_EXCEPTION);
}
WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void) {
return g_unwind_target;
}
void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target) {
g_unwind_target = target;
}
uint32_t wasm_rt_exception_tag(void) {
return g_active_exception_tag;
}
uint32_t wasm_rt_exception_size(void) {
return g_active_exception_size;
}
void* wasm_rt_exception(void) {
return g_active_exception;
}
#ifdef _WIN32
static void* os_mmap(size_t size) {
void* ret = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS);
return ret;
}
static int os_munmap(void* addr, size_t size) {
// Windows can only unmap the whole mapping
(void)size; /* unused */
BOOL succeeded = VirtualFree(addr, 0, MEM_RELEASE);
return succeeded ? 0 : -1;
}
static int os_mprotect(void* addr, size_t size) {
if (size == 0) {
return 0;
}
void* ret = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
if (ret == addr) {
return 0;
}
VirtualFree(addr, 0, MEM_RELEASE);
return -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);
}
}
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
static LONG os_signal_handler(PEXCEPTION_POINTERS info) {
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION) {
wasm_rt_trap(WASM_RT_TRAP_OOB);
} else if (info->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) {
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
}
return EXCEPTION_CONTINUE_SEARCH;
}
static void os_install_signal_handler(void) {
g_sig_handler_handle =
AddVectoredExceptionHandler(1 /* CALL_FIRST */, os_signal_handler);
}
static void os_cleanup_signal_handler(void) {
RemoveVectoredExceptionHandler(g_sig_handler_handle);
}
#endif
#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_munmap(void* addr, size_t size) {
return munmap(addr, size);
}
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);
}
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
static void os_signal_handler(int sig, siginfo_t* si, void* unused) {
if (si->si_code == SEGV_ACCERR) {
wasm_rt_trap(WASM_RT_TRAP_OOB);
} else {
wasm_rt_trap(WASM_RT_TRAP_EXHAUSTION);
}
}
static void os_install_signal_handler(void) {
/* Use alt stack to handle SIGSEGV from stack overflow */
g_alt_stack = malloc(SIGSTKSZ);
if (g_alt_stack == NULL) {
perror("malloc failed");
abort();
}
stack_t ss;
ss.ss_sp = g_alt_stack;
ss.ss_flags = 0;
ss.ss_size = SIGSTKSZ;
if (sigaltstack(&ss, NULL) != 0) {
perror("sigaltstack failed");
abort();
}
struct sigaction sa;
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = os_signal_handler;
/* Install SIGSEGV and SIGBUS handlers, since macOS seems to use SIGBUS. */
if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL) != 0) {
perror("sigaction failed");
abort();
}
}
static void os_cleanup_signal_handler(void) {
/* Undo what was done in os_install_signal_handler */
struct sigaction sa;
sa.sa_handler = SIG_DFL;
if (sigaction(SIGSEGV, &sa, NULL) != 0 || sigaction(SIGBUS, &sa, NULL)) {
perror("sigaction failed");
abort();
}
if (sigaltstack(NULL, NULL) != 0) {
perror("sigaltstack failed");
abort();
}
free(g_alt_stack);
}
#endif
#endif
void wasm_rt_init(void) {
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
if (!g_signal_handler_installed) {
g_signal_handler_installed = true;
os_install_signal_handler();
}
#endif
}
bool wasm_rt_is_initialized(void) {
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
return g_signal_handler_installed;
#else
return true;
#endif
}
void wasm_rt_free(void) {
for (uint32_t i = 0; i < g_func_type_count; ++i) {
free(g_func_types[i].params);
free(g_func_types[i].results);
}
g_func_type_count = 0;
free(g_func_types);
g_func_types = NULL;
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !WASM_RT_SKIP_SIGNAL_RECOVERY
os_cleanup_signal_handler();
#endif
}
void wasm_rt_allocate_memory(wasm_rt_memory_t* memory,
uint32_t initial_pages,
uint32_t max_pages) {
uint32_t byte_length = initial_pages * PAGE_SIZE;
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
/* Reserve 8GiB. */
void* addr = os_mmap(0x200000000ul);
if (!addr) {
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();
}
memory->data = addr;
#else
memory->data = calloc(byte_length, 1);
#endif
memory->size = byte_length;
memory->pages = initial_pages;
memory->max_pages = max_pages;
}
uint32_t wasm_rt_grow_memory(wasm_rt_memory_t* memory, uint32_t delta) {
uint32_t old_pages = memory->pages;
uint32_t new_pages = memory->pages + delta;
if (new_pages == 0) {
return 0;
}
if (new_pages < old_pages || new_pages > memory->max_pages) {
return (uint32_t)-1;
}
uint32_t old_size = old_pages * PAGE_SIZE;
uint32_t new_size = new_pages * PAGE_SIZE;
uint32_t delta_size = delta * PAGE_SIZE;
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
uint8_t* new_data = memory->data;
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) {
return (uint32_t)-1;
}
#if !WABT_BIG_ENDIAN
memset(new_data + old_size, 0, delta_size);
#endif
#endif
#if WABT_BIG_ENDIAN
memmove(new_data + new_size - old_size, new_data, old_size);
memset(new_data, 0, delta_size);
#endif
memory->pages = new_pages;
memory->size = new_size;
memory->data = new_data;
return old_pages;
}
void wasm_rt_free_memory(wasm_rt_memory_t* memory) {
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER
os_munmap(memory->data, memory->size); // ignore error?
#else
free(memory->data);
#endif
}
#define DEFINE_TABLE_OPS(type) \
void wasm_rt_allocate_##type##_table(wasm_rt_##type##_table_t* table, \
uint32_t elements, \
uint32_t max_elements) { \
table->size = elements; \
table->max_size = max_elements; \
table->data = calloc(table->size, sizeof(wasm_rt_##type##_t)); \
} \
void wasm_rt_free_##type##_table(wasm_rt_##type##_table_t* table) { \
free(table->data); \
} \
uint32_t wasm_rt_grow_##type##_table(wasm_rt_##type##_table_t* table, \
uint32_t delta, \
wasm_rt_##type##_t init) { \
uint32_t old_elems = table->size; \
uint64_t new_elems = (uint64_t)table->size + delta; \
if (new_elems == 0) { \
return 0; \
} \
if ((new_elems < old_elems) || (new_elems > table->max_size)) { \
return (uint32_t)-1; \
} \
void* new_data = \
realloc(table->data, new_elems * sizeof(wasm_rt_##type##_t)); \
if (!new_data) { \
return (uint32_t)-1; \
} \
table->data = new_data; \
table->size = new_elems; \
for (uint32_t i = old_elems; i < new_elems; i++) { \
table->data[i] = init; \
} \
return old_elems; \
}
DEFINE_TABLE_OPS(funcref)
DEFINE_TABLE_OPS(externref)
const char* wasm_rt_strerror(wasm_rt_trap_t trap) {
switch (trap) {
case WASM_RT_TRAP_NONE:
return "No error";
case WASM_RT_TRAP_OOB:
#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS
return "Out-of-bounds access in linear memory or a table, or call stack "
"exhausted";
#else
return "Out-of-bounds access in linear memory or a table";
case WASM_RT_TRAP_EXHAUSTION:
return "Call stack exhausted";
#endif
case WASM_RT_TRAP_INT_OVERFLOW:
return "Integer overflow on divide or truncation";
case WASM_RT_TRAP_DIV_BY_ZERO:
return "Integer divide by zero";
case WASM_RT_TRAP_INVALID_CONVERSION:
return "Conversion from NaN to integer";
case WASM_RT_TRAP_UNREACHABLE:
return "Unreachable instruction executed";
case WASM_RT_TRAP_CALL_INDIRECT:
return "Invalid call_indirect";
case WASM_RT_TRAP_UNCAUGHT_EXCEPTION:
return "Uncaught exception";
}
return "invalid trap code";
}

72
wasm-rt-impl.h Normal file
View File

@@ -0,0 +1,72 @@
/*
* Copyright 2018 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WASM_RT_IMPL_H_
#define WASM_RT_IMPL_H_
#include "wasm-rt.h"
#ifdef _WIN32
#include <windows.h>
#endif
#ifdef __cplusplus
extern "C" {
#endif
/** A setjmp buffer used for handling traps. */
extern jmp_buf wasm_rt_jmp_buf;
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !defined(_WIN32)
#define WASM_RT_LONGJMP(buf, val) siglongjmp(buf, val)
#else
#define WASM_RT_LONGJMP(buf, val) longjmp(buf, val)
#endif
#if WASM_RT_USE_STACK_DEPTH_COUNT
/** Saved call stack depth that will be restored in case a trap occurs. */
extern uint32_t wasm_rt_saved_call_stack_depth;
#define WASM_RT_SAVE_STACK_DEPTH() \
wasm_rt_saved_call_stack_depth = wasm_rt_call_stack_depth
#else
#define WASM_RT_SAVE_STACK_DEPTH() (void)0
#endif
/**
* Convenience macro to use before calling a wasm function. On first execution
* it will return `WASM_RT_TRAP_NONE` (i.e. 0). If the function traps, it will
* jump back and return the trap that occurred.
*
* ```
* wasm_rt_trap_t code = wasm_rt_impl_try();
* if (code != 0) {
* printf("A trap occurred with code: %d\n", code);
* ...
* }
*
* // Call the potentially-trapping function.
* my_wasm_func();
* ```
*/
#define wasm_rt_impl_try() \
(WASM_RT_SAVE_STACK_DEPTH(), wasm_rt_set_unwind_target(&wasm_rt_jmp_buf), \
WASM_RT_SETJMP(wasm_rt_jmp_buf))
#ifdef __cplusplus
}
#endif
#endif /* WASM_RT_IMPL_H_ */

409
wasm-rt.h Normal file
View File

@@ -0,0 +1,409 @@
/*
* Copyright 2018 WebAssembly Community Group participants
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef WASM_RT_H_
#define WASM_RT_H_
#include <setjmp.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdlib.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
#if __has_builtin(__builtin_unreachable)
#define wasm_rt_unreachable __builtin_unreachable
#else
#define wasm_rt_unreachable abort
#endif
/**
* Enable memory checking via a signal handler via the following definition:
*
* #define WASM_RT_MEMCHECK_SIGNAL_HANDLER 1
*
* This is usually 10%-25% faster, but requires OS-specific support.
*/
#ifndef WASM_RT_SKIP_SIGNAL_RECOVERY
#define WASM_RT_SKIP_SIGNAL_RECOVERY 0
#endif
/** Signal handler not supported on 32-bit platforms. */
#if UINTPTR_MAX > 0xffffffff
#define WASM_RT_SIGNAL_RECOVERY_SUPPORTED 1
/* Signal handler is supported. Use it by default. */
#ifndef WASM_RT_MEMCHECK_SIGNAL_HANDLER
#define WASM_RT_MEMCHECK_SIGNAL_HANDLER 1
#endif
#else
#define WASM_RT_SIGNAL_RECOVERY_SUPPORTED 0
/* Signal handler is not supported. */
#ifndef WASM_RT_MEMCHECK_SIGNAL_HANDLER
#define WASM_RT_MEMCHECK_SIGNAL_HANDLER 0
#endif
#endif
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && \
(!WASM_RT_SKIP_SIGNAL_RECOVERY && !WASM_RT_SIGNAL_RECOVERY_SUPPORTED)
/* The signal handler is not supported, error out if the user was trying to
* enable it. */
#error "Signal handler is not supported for this OS/Architecture!"
#endif
#ifndef WASM_RT_USE_STACK_DEPTH_COUNT
/* The signal handler on POSIX can detect call stack overflows. On windows, or
* platforms without a signal handler, we use stack depth counting. */
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !defined(_WIN32)
#define WASM_RT_USE_STACK_DEPTH_COUNT 0
#else
#define WASM_RT_USE_STACK_DEPTH_COUNT 1
#endif
#endif
#if WASM_RT_USE_STACK_DEPTH_COUNT
/**
* When the signal handler cannot be used to detect stack overflows, stack depth
* is limited explicitly. The maximum stack depth before trapping can be
* configured by defining this symbol before including wasm-rt when building the
* generated c files, for example:
*
* ```
* cc -c -DWASM_RT_MAX_CALL_STACK_DEPTH=100 my_module.c -o my_module.o
* ```
*/
#ifndef WASM_RT_MAX_CALL_STACK_DEPTH
#define WASM_RT_MAX_CALL_STACK_DEPTH 500
#endif
/** Current call stack depth. */
extern uint32_t wasm_rt_call_stack_depth;
#endif
#if defined(_MSC_VER)
#define WASM_RT_NO_RETURN __declspec(noreturn)
#else
#define WASM_RT_NO_RETURN __attribute__((noreturn))
#endif
#if defined(__APPLE__) && WASM_RT_MEMCHECK_SIGNAL_HANDLER
#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 1
#else
#define WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS 0
#endif
/** Reason a trap occurred. Provide this to `wasm_rt_trap`. */
typedef enum {
WASM_RT_TRAP_NONE, /** No error. */
WASM_RT_TRAP_OOB, /** Out-of-bounds access in linear memory or a table. */
WASM_RT_TRAP_INT_OVERFLOW, /** Integer overflow on divide or truncation. */
WASM_RT_TRAP_DIV_BY_ZERO, /** Integer divide by zero. */
WASM_RT_TRAP_INVALID_CONVERSION, /** Conversion from NaN to integer. */
WASM_RT_TRAP_UNREACHABLE, /** Unreachable instruction executed. */
WASM_RT_TRAP_CALL_INDIRECT, /** Invalid call_indirect, for any reason. */
WASM_RT_TRAP_UNCAUGHT_EXCEPTION, /* Exception thrown and not caught */
#if WASM_RT_MERGED_OOB_AND_EXHAUSTION_TRAPS
WASM_RT_TRAP_EXHAUSTION = WASM_RT_TRAP_OOB,
#else
WASM_RT_TRAP_EXHAUSTION, /** Call stack exhausted. */
#endif
} wasm_rt_trap_t;
/** Value types. Used to define function signatures. */
typedef enum {
WASM_RT_I32,
WASM_RT_I64,
WASM_RT_F32,
WASM_RT_F64,
WASM_RT_FUNCREF,
WASM_RT_EXTERNREF,
} wasm_rt_type_t;
/**
* A generic function pointer type, both for Wasm functions (`code`)
* and host functions (`hostcode`). All function pointers are stored
* in this canonical form, but must be cast to their proper signature
* to call.
*/
typedef void (*wasm_rt_function_ptr_t)(void);
/** A function instance (the runtime representation of a function).
* These can be stored in tables of type funcref, or used as values. */
typedef struct {
/** The index as returned from `wasm_rt_register_func_type`. */
uint32_t func_type;
/** The function. The embedder must know the actual C signature of the
* function and cast to it before calling. */
wasm_rt_function_ptr_t func;
/** A function instance is a closure of the function over an instance
* of the originating module. The module_instance element will be passed into
* the function at runtime. */
void* module_instance;
} wasm_rt_funcref_t;
/** Default (null) value of a funcref */
static const wasm_rt_funcref_t wasm_rt_funcref_null_value = {0, NULL, NULL};
/** The type of an external reference (opaque to WebAssembly). */
typedef void* wasm_rt_externref_t;
/** Default (null) value of an externref */
static const wasm_rt_externref_t wasm_rt_externref_null_value = NULL;
/** A Memory object. */
typedef struct {
/** The linear memory data, with a byte length of `size`. */
uint8_t* data;
/** The current and maximum page count for this Memory object. If there is no
* maximum, `max_pages` is 0xffffffffu (i.e. UINT32_MAX). */
uint32_t pages, max_pages;
/** The current size of the linear memory, in bytes. */
uint32_t size;
} wasm_rt_memory_t;
/** A Table of type funcref. */
typedef struct {
/** The table element data, with an element count of `size`. */
wasm_rt_funcref_t* data;
/** The maximum element count of this Table object. If there is no maximum,
* `max_size` is 0xffffffffu (i.e. UINT32_MAX). */
uint32_t max_size;
/** The current element count of the table. */
uint32_t size;
} wasm_rt_funcref_table_t;
/** A Table of type externref. */
typedef struct {
/** The table element data, with an element count of `size`. */
wasm_rt_externref_t* data;
/** The maximum element count of this Table object. If there is no maximum,
* `max_size` is 0xffffffffu (i.e. UINT32_MAX). */
uint32_t max_size;
/** The current element count of the table. */
uint32_t size;
} wasm_rt_externref_table_t;
/** Initialize the runtime. */
void wasm_rt_init(void);
/** Is the runtime initialized? */
bool wasm_rt_is_initialized(void);
/** Free the runtime's state. */
void wasm_rt_free(void);
/**
* Stop execution immediately and jump back to the call to `wasm_rt_impl_try`.
* The result of `wasm_rt_impl_try` will be the provided trap reason.
*
* This is typically called by the generated code, and not the embedder.
*/
WASM_RT_NO_RETURN void wasm_rt_trap(wasm_rt_trap_t);
/**
* Return a human readable error string based on a trap type.
*/
const char* wasm_rt_strerror(wasm_rt_trap_t trap);
/**
* Register a function type with the given signature. The returned function
* index is guaranteed to be the same for all calls with the same signature.
* The following varargs must all be of type `wasm_rt_type_t`, first the
* params` and then the `results`.
*
* ```
* // Register (func (param i32 f32) (result i64)).
* wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_I64);
* => returns 1
*
* // Register (func (result i64)).
* wasm_rt_register_func_type(0, 1, WASM_RT_I32);
* => returns 2
*
* // Register (func (param i32 f32) (result i64)) again.
* wasm_rt_register_func_type(2, 1, WASM_RT_I32, WASM_RT_F32, WASM_RT_I64);
* => returns 1
* ```
*/
uint32_t wasm_rt_register_func_type(uint32_t params, uint32_t results, ...);
/**
* Register a tag with the given size. Returns the tag.
*/
uint32_t wasm_rt_register_tag(uint32_t size);
/**
* Set the active exception to given tag, size, and contents.
*/
void wasm_rt_load_exception(uint32_t tag, uint32_t size, const void* values);
/**
* Throw the active exception.
*/
WASM_RT_NO_RETURN void wasm_rt_throw(void);
/**
* The type of an unwind target if an exception is thrown and caught.
*/
#define WASM_RT_UNWIND_TARGET jmp_buf
/**
* Get the current unwind target if an exception is thrown.
*/
WASM_RT_UNWIND_TARGET* wasm_rt_get_unwind_target(void);
/**
* Set the unwind target if an exception is thrown.
*/
void wasm_rt_set_unwind_target(WASM_RT_UNWIND_TARGET* target);
/**
* Tag of the active exception.
*/
uint32_t wasm_rt_exception_tag(void);
/**
* Size of the active exception.
*/
uint32_t wasm_rt_exception_size(void);
/**
* Contents of the active exception.
*/
void* wasm_rt_exception(void);
#if WASM_RT_MEMCHECK_SIGNAL_HANDLER && !defined(_WIN32)
#define WASM_RT_SETJMP(buf) sigsetjmp(buf, 1)
#else
#define WASM_RT_SETJMP(buf) setjmp(buf)
#endif
#define wasm_rt_try(target) WASM_RT_SETJMP(target)
/**
* Initialize a Memory object with an initial page size of `initial_pages` and
* a maximum page size of `max_pages`.
*
* ```
* wasm_rt_memory_t my_memory;
* // 1 initial page (65536 bytes), and a maximum of 2 pages.
* wasm_rt_allocate_memory(&my_memory, 1, 2);
* ```
*/
void wasm_rt_allocate_memory(wasm_rt_memory_t*,
uint32_t initial_pages,
uint32_t max_pages);
/**
* Grow a Memory object by `pages`, and return the previous page count. If
* this new page count is greater than the maximum page count, the grow fails
* and 0xffffffffu (UINT32_MAX) is returned instead.
*
* ```
* wasm_rt_memory_t my_memory;
* ...
* // Grow memory by 10 pages.
* uint32_t old_page_size = wasm_rt_grow_memory(&my_memory, 10);
* if (old_page_size == UINT32_MAX) {
* // Failed to grow memory.
* }
* ```
*/
uint32_t wasm_rt_grow_memory(wasm_rt_memory_t*, uint32_t pages);
/**
* Free a Memory object.
*/
void wasm_rt_free_memory(wasm_rt_memory_t*);
/**
* Initialize a funcref Table object with an element count of `elements` and a
* maximum size of `max_elements`.
*
* ```
* wasm_rt_funcref_table_t my_table;
* // 5 elements and a maximum of 10 elements.
* wasm_rt_allocate_funcref_table(&my_table, 5, 10);
* ```
*/
void wasm_rt_allocate_funcref_table(wasm_rt_funcref_table_t*,
uint32_t elements,
uint32_t max_elements);
/**
* Free a funcref Table object.
*/
void wasm_rt_free_funcref_table(wasm_rt_funcref_table_t*);
/**
* Initialize an externref Table object with an element count
* of `elements` and a maximum size of `max_elements`.
* Usage as per wasm_rt_allocate_funcref_table.
*/
void wasm_rt_allocate_externref_table(wasm_rt_externref_table_t*,
uint32_t elements,
uint32_t max_elements);
/**
* Free an externref Table object.
*/
void wasm_rt_free_externref_table(wasm_rt_externref_table_t*);
/**
* Grow a Table object by `delta` elements (giving the new elements the value
* `init`), and return the previous element count. If this new element count is
* greater than the maximum element count, the grow fails and 0xffffffffu
* (UINT32_MAX) is returned instead.
*/
uint32_t wasm_rt_grow_funcref_table(wasm_rt_funcref_table_t*,
uint32_t delta,
wasm_rt_funcref_t init);
uint32_t wasm_rt_grow_externref_table(wasm_rt_externref_table_t*,
uint32_t delta,
wasm_rt_externref_t init);
#ifdef __cplusplus
}
#endif
#endif /* WASM_RT_H_ */