From 490bf0c9d6261ccd9d5aca3e47adb15737932e43 Mon Sep 17 00:00:00 2001 From: Volodymyr Shymanskyy Date: Sat, 18 Apr 2020 16:51:30 +0300 Subject: [PATCH] Add emscripten_lib target --- CMakeLists.txt | 34 +++++-- platforms/emscripten_lib/main.c | 57 +++++++++++ platforms/emscripten_lib/wasm3_run.js | 139 ++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 platforms/emscripten_lib/main.c create mode 100644 platforms/emscripten_lib/wasm3_run.js diff --git a/CMakeLists.txt b/CMakeLists.txt index 741927c..07c4a51 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,12 +30,18 @@ if(CLANG_CL) set(CMAKE_LINKER "lld-link") endif() -if(EMSCRIPTEN) +if(EMSCRIPTEN OR EMSCRIPTEN_LIB) set(CMAKE_C_COMPILER "emcc") set(CMAKE_CXX_COMPILER "em++") - set(OUT_FILE "wasm3.html") - set(APP_DIR "platforms/emscripten") + if (EMSCRIPTEN_LIB) + set(APP_DIR "platforms/emscripten_lib") + set(OUT_FILE "wasm3.wasm") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s STANDALONE_WASM") + else() + set(APP_DIR "platforms/emscripten") + set(OUT_FILE "wasm3.html") + endif() endif() # Detect WasiEnv @@ -77,29 +83,36 @@ add_executable(${OUT_FILE} ${app_srcs}) set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -DDEBUG=1") -if(EMSCRIPTEN) - set(CMAKE_C_FLAGS_RELEASE "-O2 -flto -Wfatal-errors") - set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O2 --lto-O3 --closure 1 --strip-all --gc-sections") # --emrun - if(WASM_TCO) - set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Xclang -target-feature -Xclang +tail-call") +if(EMSCRIPTEN OR EMSCRIPTEN_LIB) + + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s GLOBAL_BASE=1024 -s TOTAL_STACK=2MB -s INITIAL_MEMORY=4MB -s ALLOW_MEMORY_GROWTH") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s EXPORTED_FUNCTIONS='[\"_malloc\",\"_free\"]'") + + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -flto -Wfatal-errors -s ASSERTIONS=0") + set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} --strip-all --gc-sections") + + if(WASM_EXT) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mbulk-memory -mnontrapping-fptoint -msign-ext -mtail-call") endif() + elseif(WASIENV) if(BUILD_WASI_SUPPORT) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasMetaWASI -Dd_m3HasTracer") endif() - set(CMAKE_C_FLAGS_RELEASE "-O3 -Wfatal-errors -fomit-frame-pointer -fno-stack-check -fno-stack-protector") + set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -O3 -Wfatal-errors -fomit-frame-pointer -fno-stack-check -fno-stack-protector") # TODO: LTO breaks wasm imports currently: # https://www.mail-archive.com/llvm-bugs@lists.llvm.org/msg36273.html #-flto -Wl,--lto-O3 set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,stack-size=8388608") + elseif(WIN32) if(BUILD_WASI_SUPPORT) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasWASI") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Dd_m3HasWASI -Dd_m3HasTracer") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_CRT_SECURE_NO_WARNINGS /WX- /diagnostics:column") @@ -145,6 +158,7 @@ else() set(CMAKE_EXE_LINKER_FLAGS_RELEASE "-O3") target_link_libraries(${OUT_FILE} m) + endif() check_ipo_supported(RESULT result) diff --git a/platforms/emscripten_lib/main.c b/platforms/emscripten_lib/main.c new file mode 100644 index 0000000..2062955 --- /dev/null +++ b/platforms/emscripten_lib/main.c @@ -0,0 +1,57 @@ +// +// Wasm3 - high performance WebAssembly interpreter written in C. +// +// Copyright © 2019 Steven Massey, Volodymyr Shymanskyy. +// All rights reserved. +// + +#include +#include +#include + +#include "wasm3.h" +#include "m3_env.h" + +IM3Environment env; + +EMSCRIPTEN_KEEPALIVE +void init() { + env = m3_NewEnvironment (); + if (!env) return; +} + +EMSCRIPTEN_KEEPALIVE +IM3Runtime new_runtime() { + return m3_NewRuntime (env, 64*1024, NULL); +} + +EMSCRIPTEN_KEEPALIVE +void free_runtime(IM3Runtime runtime) { + m3_FreeRuntime (runtime); +} + +EMSCRIPTEN_KEEPALIVE +void load(IM3Runtime runtime, uint8_t* wasm, size_t fsize) { + M3Result result = m3Err_none; + + IM3Module module; + result = m3_ParseModule (env, &module, wasm, fsize); + if (result) return; + + result = m3_LoadModule (runtime, module); + if (result) return; +} + +EMSCRIPTEN_KEEPALIVE +uint32_t call(IM3Runtime runtime, const char* name, int argc, const char** argv) { + M3Result result = m3Err_none; + + IM3Function f; + result = m3_FindFunction (&f, runtime, name); + if (result) return -1; + + result = m3_CallWithArgs (f, argc, argv); + if (result) return -2; + + return *(uint64_t*)(runtime->stack); +} diff --git a/platforms/emscripten_lib/wasm3_run.js b/platforms/emscripten_lib/wasm3_run.js new file mode 100644 index 0000000..1fb2c46 --- /dev/null +++ b/platforms/emscripten_lib/wasm3_run.js @@ -0,0 +1,139 @@ +'use strict'; + +/* + Node.js + ------- + node --v8-options | grep -A1 wasm + --print_wasm_code --code-comments + --wasm_interpret_all --trace_wasm_interpreter + + SpiderMonkey + ------------ + export PATH=/opt/jsshell/:$PATH + js --help | grep wasm + --wasm-compiler=baseline/ion/cranelift/baseline+ion/baseline+cranelift + --wasm-verbose + --ion-full-warmup-threshold=1 +*/ + +const isNodeJS = (typeof(process) != 'undefined'); + +function encode(str) { + var Len = str.length, resPos = -1; + var resArr = new Uint8Array(Len * 3); + for (var i = 0; i !== Len; i += 1) { + var point = str.charCodeAt(i); + if (point <= 0x007f) { + resArr[resPos += 1] = point; + } + } + return resArr.subarray(0, resPos + 1); +} + +if (isNodeJS) { + var scriptArgs = process.argv.slice(2); + const fs = require('fs'); + var readFile = (fn) => new Uint8Array(fs.readFileSync(fn)); +} else { + var readFile = (fn) => read(fn, 'binary'); +} + +const env = { + __memory_base: 0, + __table_base: 0, + "env": { + "emscripten_notify_memory_growth": function() {}, + "emscripten_get_sbrk_ptr": function() {}, + }, + "wasi_snapshot_preview1": { + "fd_close": function() {}, + "fd_seek": function() {}, + "fd_write": function() { return -1; }, + "proc_exit": function() {} + } +} + +var instance; +//let encoder = new TextEncoder('utf-8'); + +function strToMem(s) { + const data = encode(s); + const ptr = instance.exports.malloc(data.length+1); + const buf = new Uint8Array(instance.exports.memory.buffer, ptr, data.length+1); + buf.set(data) + buf[data.length] = 0; + return ptr; +} + +function bufToMem(data) { + const ptr = instance.exports.malloc(data.length); + const buf = new Uint8Array(instance.exports.memory.buffer, ptr, data.length); + buf.set(data) + return ptr; +} + + +function new_runtime() { + return instance.exports.new_runtime(); +} + +function free_runtime(runtime) { + instance.exports.free_runtime(runtime); +} + +function load(runtime, buff) { + const ptr = bufToMem(buff); + instance.exports.load(runtime, ptr, buff.length); +} + +function call(runtime, fname, args) { + const namePtr = strToMem(fname) + + const argsCnt = args.length; + const argsPtr = instance.exports.malloc(argsCnt*4); + const argsArray = new Uint32Array( + instance.exports.memory.buffer, + argsPtr, argsCnt + ); + + args.forEach((item, idx) => { + argsArray[idx] = strToMem(item.toString()); + }) + + return instance.exports.call(runtime, namePtr, argsCnt, argsPtr); +} + +/* TODO: + * - callback/hook exported function calls + * + */ + + +async function main() { + const FILE = scriptArgs[0]; + const FUNC = scriptArgs[1]; + const ARGS = scriptArgs.slice(2); + + for (let i=0; i<25000; i++) { + let rt = new_runtime(); + + load(rt, readFile(FILE)); + + let result = call(rt, FUNC, [1]); + //console.log(i, result); + + free_runtime(rt); + } +} + +try { + WebAssembly.instantiate(readFile('wasm3.wasm'), env).then(result => { + instance = result.instance; + instance.exports.init(); + main(); + + console.log(`Memory size: ${instance.exports.memory.buffer.byteLength}`); + }) +} catch (e) { + console.log(JSON.stringify(e.message)); +}